Skip to content

Commit e0376d7

Browse files
committed
update packaging and deps
1 parent 962337e commit e0376d7

File tree

8 files changed

+175
-100
lines changed

8 files changed

+175
-100
lines changed

.editorconfig

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ charset = utf-8
77
trim_trailing_whitespace = true
88
insert_final_newline = true
99

10-
[{*.json,*.md,.*rc,*.yml,*.txt}]
10+
[{*.json,.*rc,*.yml}]
1111
indent_style = space
1212
indent_size = 2
13-
insert_final_newline = false
13+
insert_final_newline = false
14+
15+
[*.md]
16+
trim_trailing_whitespace = false

.eslintrc.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// eslint-disable-next-line
2+
module.exports = {
3+
env: {
4+
node: true,
5+
browser: true,
6+
es2021: true,
7+
},
8+
extends: ['eslint-config-developit', 'prettier', 'eslint:recommended'],
9+
ignorePatterns: ['**/dist/**'],
10+
};

README.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,23 @@
22
<img src="https://i.imgur.com/Xqla6Ia.jpg" width="1100">
33
</p>
44

5-
65
# jsdom-worker
76

87
> _Lets you use Web Workers in Jest!_
98
109
This is an experimental implementation of the Web Worker API (specifically Dedicated Worker) for JSDOM.
1110

12-
It does not currently do any real threading, rather it implements the `Worker` interface but all work is done in the current thread. `jsdom-worker` runs wherever JSDOM runs, and does not require Node.
11+
It does not currently do any real threading, rather it implements the `Worker` interface but all work is done in the current thread. `jsdom-worker` runs wherever JSDOM runs, and does not require Node.
1312

1413
It supports both "inline" _(created via Blob)_ and standard _(loaded via URL)_ workers.
1514

1615
> **Hot Take:** this module likely works in the browser, where it could act as a simple inline worker "poorlyfill".
1716
1817
<a href="https://www.npmjs.org/package/jsdom-worker"><img src="https://img.shields.io/npm/v/jsdom-worker.svg?style=flat" alt="npm"></a> <a href="https://travis-ci.org/developit/jsdom-worker"><img src="https://travis-ci.org/developit/jsdom-worker.svg?branch=master" alt="travis"></a>
1918

20-
2119
## Why?
2220

23-
Jest uses a JSDOM environment by default, which means it doesn't support Workers. This means it is impossible to test code that requires both NodeJS functionality _and_ Web Workers. `jsdom-worker` implements enough of the Worker spec that it is now possible to do so.
21+
Jest uses a JSDOM environment by default, which means it doesn't support Workers. This means it is impossible to test code that requires both NodeJS functionality _and_ Web Workers. `jsdom-worker` implements enough of the Worker spec that it is now possible to do so.
2422

2523
## Installation
2624

@@ -29,13 +27,13 @@ Jest uses a JSDOM environment by default, which means it doesn't support Workers
2927
## Example
3028

3129
```js
32-
import 'jsdom-global/register'
33-
import 'jsdom-worker'
30+
import 'jsdom-global/register';
31+
import 'jsdom-worker';
3432

35-
let code = `onmessage = e => postMessage(e.data*2)`
36-
let worker = new Worker(URL.createObjectURL(new Blob([code])))
37-
worker.onmessage = console.log
38-
worker.postMessage(5) // 10
33+
let code = `onmessage = e => postMessage(e.data*2)`;
34+
let worker = new Worker(URL.createObjectURL(new Blob([code])));
35+
worker.onmessage = console.log;
36+
worker.postMessage(5); // 10
3937
```
4038

4139
## Usage with Jest

package.json

Lines changed: 78 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,80 @@
11
{
2-
"name": "jsdom-worker",
3-
"version": "0.2.1",
4-
"description": "Experimental Web Worker API implementation for JSDOM.",
5-
"main": "dist/jsdom-worker.js",
6-
"module": "dist/jsdom-worker.module.js",
7-
"unpkg": "dist/jsdom-worker.umd.js",
8-
"scripts": {
9-
"build": "microbundle --external all",
10-
"test": "eslint src test && npm run -s build && jest",
11-
"prepare": "npm run -s build && npm t",
12-
"release": "npm t && git commit -am \"$npm_package_version\" && git tag $npm_package_version && git push && git push --tags && npm publish"
13-
},
14-
"repository": "developit/jsdom-worker",
15-
"babel": {
16-
"presets": [
17-
[
18-
"@babel/preset-env",
19-
{
20-
"targets": {
21-
"node": "8"
22-
}
23-
}
24-
]
25-
]
26-
},
27-
"keywords": [
28-
"jsdom",
29-
"web worker"
30-
],
31-
"eslintConfig": {
32-
"extends": "eslint-config-developit"
33-
},
34-
"author": "Jason Miller <[email protected]> (http://jasonformat.com)",
35-
"license": "MIT",
36-
"files": [
37-
"dist"
38-
],
39-
"devDependencies": {
40-
"@babel/preset-env": "^7.10.2",
41-
"babel-jest": "^26.0.1",
42-
"eslint": "^7.2.0",
43-
"eslint-config-developit": "^1.1.1",
44-
"jest": "^26.0.1",
45-
"microbundle": "^0.12.1",
46-
"node-fetch": "^2.6.0"
47-
},
48-
"peerDependencies": {
49-
"node-fetch": "*"
50-
},
51-
"dependencies": {
52-
"mitt": "^1.1.3",
53-
"uuid-v4": "^0.1.0"
54-
}
2+
"name": "jsdom-worker",
3+
"version": "0.2.1",
4+
"description": "Experimental Web Worker API implementation for JSDOM.",
5+
"main": "./dist/jsdom-worker.js",
6+
"module": "./dist/jsdom-worker.mjs",
7+
"unpkg": "./dist/jsdom-worker.umd.js",
8+
"exports": {
9+
"import": "./dist/jsdom-worker.mjs",
10+
"default": "./dist/jsdom-worker.js"
11+
},
12+
"scripts": {
13+
"build": "microbundle --external all -f esm,cjs,umd",
14+
"test": "eslint '{src,test}/**/*.js' && npm run -s build && jest",
15+
"prepare": "npm run -s build && npm t",
16+
"release": "npm t && git commit -am \"$npm_package_version\" && git tag $npm_package_version && git push && git push --tags && npm publish"
17+
},
18+
"repository": "developit/jsdom-worker",
19+
"babel": {
20+
"presets": [
21+
[
22+
"@babel/preset-env",
23+
{
24+
"targets": {
25+
"node": "12"
26+
}
27+
}
28+
]
29+
]
30+
},
31+
"jest": {
32+
"testEnvironment": "jsdom"
33+
},
34+
"keywords": [
35+
"jsdom",
36+
"web worker"
37+
],
38+
"author": "Jason Miller <[email protected]> (http://jasonformat.com)",
39+
"license": "MIT",
40+
"files": [
41+
"dist"
42+
],
43+
"prettier": {
44+
"useTabs": true,
45+
"arrowParens": "avoid",
46+
"singleQuote": true
47+
},
48+
"lint-staged": {
49+
"**/*.{js,jsx,ts,tsx,yml}": [
50+
"prettier --write"
51+
]
52+
},
53+
"husky": {
54+
"hooks": {
55+
"pre-commit": "lint-staged"
56+
}
57+
},
58+
"devDependencies": {
59+
"@babel/preset-env": "^7.10.2",
60+
"@types/jest": "^28.1.8",
61+
"babel-jest": "^29.0.1",
62+
"eslint": "^7.2.0",
63+
"eslint-config-developit": "^1.1.1",
64+
"eslint-config-prettier": "^8.5.0",
65+
"husky": "^8.0.1",
66+
"jest": "^29.0.1",
67+
"jest-environment-jsdom": "^29.0.1",
68+
"lint-staged": "^13.0.3",
69+
"microbundle": "^0.15.1",
70+
"node-fetch": "^2.6.7",
71+
"prettier": "^2.7.1"
72+
},
73+
"peerDependencies": {
74+
"node-fetch": "*"
75+
},
76+
"dependencies": {
77+
"mitt": "^3.0.0",
78+
"uuid-v4": "^0.1.0"
79+
}
5580
}

src/index.js

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,54 +2,83 @@ import mitt from 'mitt';
22
import uuid from 'uuid-v4';
33
import fetch, { Response } from 'node-fetch';
44

5-
if (!global.URL) global.URL = {};
6-
if (!global.URL.$$objects) {
7-
global.URL.$$objects = new Map();
8-
global.URL.createObjectURL = blob => {
5+
// eslint-disable-next-line no-undef
6+
7+
const self = /** @type {globalThis} */ (
8+
typeof global === 'object'
9+
? global
10+
: typeof globalThis === 'object'
11+
? globalThis
12+
: this
13+
);
14+
15+
// @ts-ignore-next-line
16+
if (!self.URL) self.URL = {};
17+
18+
// @ts-ignore
19+
let objects = /** @type {Map<any, string>} */ (self.URL.$$objects);
20+
21+
if (!objects) {
22+
objects = new Map();
23+
// @ts-ignore
24+
self.URL.$$objects = objects;
25+
self.URL.createObjectURL = blob => {
926
let id = uuid();
10-
global.URL.$$objects[id] = blob;
27+
objects[id] = blob;
1128
return `blob:http://localhost/${id}`;
1229
};
1330
}
1431

15-
if (!global.fetch || !global.fetch.jsdomWorker) {
16-
let oldFetch = global.fetch || fetch;
17-
global.fetch = function(url, opts) {
18-
if (url.match(/^blob:/)) {
19-
return new Promise( (resolve, reject) => {
32+
if (!self.fetch || !('jsdomWorker' in self.fetch)) {
33+
let oldFetch = self.fetch || fetch;
34+
self.fetch = function (url, opts) {
35+
let _url = typeof url === 'object' ? url.url || url.href : url;
36+
if (_url.match(/^blob:/)) {
37+
return new Promise((resolve, reject) => {
2038
let fr = new FileReader();
2139
fr.onload = () => {
22-
let Res = global.Response || Response;
40+
let Res = self.Response || Response;
2341
resolve(new Res(fr.result, { status: 200, statusText: 'OK' }));
2442
};
2543
fr.onerror = () => {
2644
reject(fr.error);
2745
};
28-
let id = url.match(/[^/]+$/)[0];
29-
fr.readAsText(global.URL.$$objects[id]);
46+
let id = _url.match(/[^/]+$/)[0];
47+
fr.readAsText(objects[id]);
3048
});
3149
}
3250
return oldFetch.call(this, url, opts);
3351
};
34-
global.fetch.jsdomWorker = true;
52+
Object.defineProperty(self.fetch, 'jsdomWorker', {
53+
configurable: true,
54+
value: true,
55+
});
3556
}
3657

37-
if (!global.document) {
38-
global.document = {};
39-
}
58+
// @ts-ignore
59+
if (!self.document) self.document = {};
4060

41-
function Event(type) { this.type = type; }
61+
function Event(type) {
62+
this.type = type;
63+
}
4264
Event.prototype.initEvent = Object;
43-
if (!global.document.createEvent) {
44-
global.document.createEvent = function(type) {
65+
if (!self.document.createEvent) {
66+
self.document.createEvent = function (type) {
4567
let Ctor = global[type] || Event;
4668
return new Ctor(type);
4769
};
4870
}
4971

72+
// @ts-ignore
73+
self.Worker = Worker;
5074

51-
global.Worker = function Worker(url) {
75+
/**
76+
* @param {string | URL} url
77+
* @param {object} [options = {}]
78+
*/
79+
function Worker(url, options) {
5280
let getScopeVar;
81+
/** @type {any[] | null} */
5382
let messageQueue = [];
5483
let inside = mitt();
5584
let outside = mitt();
@@ -62,8 +91,8 @@ global.Worker = function Worker(url) {
6291
postMessage(data) {
6392
outside.emit('message', { data });
6493
},
65-
fetch: global.fetch,
66-
importScripts() {}
94+
fetch: self.fetch,
95+
importScripts() {},
6796
};
6897
inside.on('message', e => {
6998
if (terminated) return;
@@ -76,30 +105,35 @@ global.Worker = function Worker(url) {
76105
outside.on('message', e => {
77106
if (this.onmessage) this.onmessage(e);
78107
});
108+
this.onmessage = null;
79109
this.postMessage = data => {
80110
if (terminated) return;
81111
if (messageQueue != null) messageQueue.push(data);
82112
else inside.emit('message', { data });
83113
};
84114
this.terminate = () => {
85115
console.warn('Worker.prototype.terminate() not supported in jsdom-worker.');
86-
messageQueue = null;
87116
terminated = true;
117+
messageQueue = null;
88118
};
89-
global.fetch(url)
119+
self
120+
.fetch(url)
90121
.then(r => r.text())
91122
.then(code => {
92123
let vars = 'var self=this,global=self';
93124
for (let k in scope) vars += `,${k}=self.${k}`;
94125
getScopeVar = Function(
95-
vars + ';\n' + code + '\nreturn function(n){return n=="onmessage"?onmessage:null;}'
126+
vars +
127+
';\n' +
128+
code +
129+
'\nreturn function(n){return n=="onmessage"?onmessage:null;}'
96130
).call(scope);
97131
let q = messageQueue;
98132
messageQueue = null;
99-
q.forEach(this.postMessage);
133+
if (q) q.forEach(this.postMessage);
100134
})
101135
.catch(e => {
102136
outside.emit('error', e);
103137
console.error(e);
104138
});
105-
};
139+
}

test/index.test.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import 'jsdom-worker';
33
import fs from 'fs';
44
import path from 'path';
55

6-
const sleep = t => new Promise( r => { setTimeout(r, t); });
6+
const sleep = t =>
7+
new Promise(r => {
8+
setTimeout(r, t);
9+
});
710

811
describe('jsdom-worker', () => {
912
it('should work', async () => {
@@ -16,11 +19,11 @@ describe('jsdom-worker', () => {
1619
});
1720

1821
it('should work with importScripts', async () => {
19-
const mod = fs.readFileSync(path.join(__dirname, './module.js'));
20-
const code = fs.readFileSync(path.join(__dirname, './worker.js'));
22+
const mod = fs.readFileSync(path.join(__dirname, './module.js'), 'utf-8');
23+
const code = fs.readFileSync(path.join(__dirname, './worker.js'), 'utf-8');
2124
const worker = new Worker(URL.createObjectURL(new Blob([mod + code])));
2225
worker.onmessage = jest.fn();
23-
worker.postMessage();
26+
worker.postMessage(0);
2427
await sleep(10);
2528
expect(worker.onmessage).toHaveBeenCalledWith({ data: 'test' });
2629
});
@@ -30,7 +33,7 @@ describe('jsdom-worker', () => {
3033
const code = `(function(n){ onmessage = e => { postMessage(n) } })(${n})`;
3134
const worker = new Worker(URL.createObjectURL(new Blob([code])));
3235
worker.onmessage = jest.fn();
33-
worker.postMessage();
36+
worker.postMessage({ hi: 'bye' });
3437
await sleep(10);
3538
expect(worker.onmessage).toHaveBeenCalledWith({ data: n });
3639
});

0 commit comments

Comments
 (0)