@parcel/workers 2.10.1 → 2.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Worker.js +10 -7
- package/lib/WorkerFarm.js +35 -7
- package/lib/backend.js +4 -0
- package/lib/bus.js +2 -1
- package/lib/child.js +13 -3
- package/lib/core-worker.browser.js +4 -0
- package/lib/core-worker.js +4 -0
- package/lib/cpuCount.js +13 -5
- package/lib/web/WebChild.js +44 -0
- package/lib/web/WebWorker.js +85 -0
- package/package.json +10 -10
- package/src/Worker.js +16 -13
- package/src/WorkerFarm.js +40 -8
- package/src/backend.js +5 -0
- package/src/bus.js +2 -1
- package/src/child.js +12 -2
- package/src/core-worker.browser.js +3 -0
- package/src/core-worker.js +2 -0
- package/src/cpuCount.js +17 -9
- package/src/types.js +1 -1
- package/src/web/WebChild.js +50 -0
- package/src/web/WebWorker.js +85 -0
package/lib/Worker.js
CHANGED
@@ -42,13 +42,16 @@ class Worker extends _events().default {
|
|
42
42
|
this.options = options;
|
43
43
|
}
|
44
44
|
async fork(forkModule) {
|
45
|
-
let filteredArgs =
|
46
|
-
|
47
|
-
|
48
|
-
let
|
49
|
-
|
50
|
-
filteredArgs
|
51
|
-
|
45
|
+
let filteredArgs = [];
|
46
|
+
if (process.execArgv) {
|
47
|
+
filteredArgs = process.execArgv.filter(v => !/^--(debug|inspect|no-opt|max-old-space-size=)/.test(v));
|
48
|
+
for (let i = 0; i < filteredArgs.length; i++) {
|
49
|
+
let arg = filteredArgs[i];
|
50
|
+
let isArgWithParam = (arg === '-r' || arg === '--require') && filteredArgs[i + 1] === '@parcel/register' || arg === '--title';
|
51
|
+
if (isArgWithParam) {
|
52
|
+
filteredArgs.splice(i, 2);
|
53
|
+
i--;
|
54
|
+
}
|
52
55
|
}
|
53
56
|
}
|
54
57
|
|
package/lib/WorkerFarm.js
CHANGED
@@ -10,6 +10,8 @@ Object.defineProperty(exports, "Handle", {
|
|
10
10
|
}
|
11
11
|
});
|
12
12
|
exports.default = void 0;
|
13
|
+
var coreWorker = _interopRequireWildcard(require("./core-worker"));
|
14
|
+
var bus = _interopRequireWildcard(require("./bus"));
|
13
15
|
function _assert() {
|
14
16
|
const data = _interopRequireDefault(require("assert"));
|
15
17
|
_assert = function () {
|
@@ -71,9 +73,9 @@ function _logger() {
|
|
71
73
|
};
|
72
74
|
return data;
|
73
75
|
}
|
76
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
74
77
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
75
78
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
76
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
77
79
|
let referenceId = 1;
|
78
80
|
const DEFAULT_MAX_CONCURRENT_CALLS = 30;
|
79
81
|
|
@@ -85,12 +87,14 @@ class WorkerFarm extends _events().default {
|
|
85
87
|
callQueue = [];
|
86
88
|
ending = false;
|
87
89
|
warmWorkers = 0;
|
90
|
+
readyWorkers = 0;
|
88
91
|
workers = new Map();
|
89
92
|
handles = new Map();
|
90
93
|
sharedReferences = new Map();
|
91
94
|
sharedReferencesByValue = new Map();
|
92
95
|
serializedSharedReferences = new Map();
|
93
96
|
constructor(farmOptions = {}) {
|
97
|
+
var _process$stdout;
|
94
98
|
super();
|
95
99
|
this.options = {
|
96
100
|
maxConcurrentWorkers: WorkerFarm.getNumWorkers(),
|
@@ -106,8 +110,17 @@ class WorkerFarm extends _events().default {
|
|
106
110
|
throw new Error('Please provide a worker path!');
|
107
111
|
}
|
108
112
|
|
109
|
-
// $FlowFixMe
|
110
|
-
|
113
|
+
// $FlowFixMe
|
114
|
+
if (process.browser) {
|
115
|
+
if (this.options.workerPath === '@parcel/core/src/worker.js') {
|
116
|
+
this.localWorker = coreWorker;
|
117
|
+
} else {
|
118
|
+
throw new Error('No dynamic require possible: ' + this.options.workerPath);
|
119
|
+
}
|
120
|
+
} else {
|
121
|
+
// $FlowFixMe this must be dynamic
|
122
|
+
this.localWorker = require(this.options.workerPath);
|
123
|
+
}
|
111
124
|
this.localWorkerInit = this.localWorker.childInit != null ? this.localWorker.childInit() : null;
|
112
125
|
this.run = this.createHandle('run');
|
113
126
|
|
@@ -119,7 +132,7 @@ class WorkerFarm extends _events().default {
|
|
119
132
|
// Note this can't be fixed easily where other things pipe into stdout - even after starting > 10 worker
|
120
133
|
// threads `process.stdout.getMaxListeners()` will still return 10, however adding another pipe into `stdout`
|
121
134
|
// will give the warning with `<worker count + 1>` as the number of listeners.
|
122
|
-
process.stdout.setMaxListeners(Math.max(process.stdout.getMaxListeners(), WorkerFarm.getNumWorkers() + 1));
|
135
|
+
(_process$stdout = process.stdout) === null || _process$stdout === void 0 || _process$stdout.setMaxListeners(Math.max(process.stdout.getMaxListeners(), WorkerFarm.getNumWorkers() + 1));
|
123
136
|
this.startMaxWorkers();
|
124
137
|
}
|
125
138
|
workerApi = {
|
@@ -215,7 +228,13 @@ class WorkerFarm extends _events().default {
|
|
215
228
|
});
|
216
229
|
worker.fork((0, _nullthrows().default)(this.options.workerPath));
|
217
230
|
worker.on('request', data => this.processRequest(data, worker));
|
218
|
-
worker.on('ready', () =>
|
231
|
+
worker.on('ready', () => {
|
232
|
+
this.readyWorkers++;
|
233
|
+
if (this.readyWorkers === this.options.maxConcurrentWorkers) {
|
234
|
+
this.emit('ready');
|
235
|
+
}
|
236
|
+
this.processQueue();
|
237
|
+
});
|
219
238
|
worker.on('response', () => this.processQueue());
|
220
239
|
worker.on('error', err => this.onError(err, worker));
|
221
240
|
worker.once('exit', () => this.stopWorker(worker));
|
@@ -278,8 +297,17 @@ class WorkerFarm extends _events().default {
|
|
278
297
|
var _this$handles$get;
|
279
298
|
mod = (0, _nullthrows().default)((_this$handles$get = this.handles.get(handleId)) === null || _this$handles$get === void 0 ? void 0 : _this$handles$get.fn);
|
280
299
|
} else if (location) {
|
281
|
-
// $FlowFixMe
|
282
|
-
|
300
|
+
// $FlowFixMe
|
301
|
+
if (process.browser) {
|
302
|
+
if (location === '@parcel/workers/src/bus.js') {
|
303
|
+
mod = bus;
|
304
|
+
} else {
|
305
|
+
throw new Error('No dynamic require possible: ' + location);
|
306
|
+
}
|
307
|
+
} else {
|
308
|
+
// $FlowFixMe this must be dynamic
|
309
|
+
mod = require(location);
|
310
|
+
}
|
283
311
|
} else {
|
284
312
|
throw new Error('Unknown request');
|
285
313
|
}
|
package/lib/backend.js
CHANGED
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
6
6
|
exports.detectBackend = detectBackend;
|
7
7
|
exports.getWorkerBackend = getWorkerBackend;
|
8
8
|
function detectBackend() {
|
9
|
+
// $FlowFixMe
|
10
|
+
if (process.browser) return 'web';
|
9
11
|
switch (process.env.PARCEL_WORKER_BACKEND) {
|
10
12
|
case 'threads':
|
11
13
|
case 'process':
|
@@ -24,6 +26,8 @@ function getWorkerBackend(backend) {
|
|
24
26
|
return require('./threads/ThreadsWorker').default;
|
25
27
|
case 'process':
|
26
28
|
return require('./process/ProcessWorker').default;
|
29
|
+
case 'web':
|
30
|
+
return require('./web/WebWorker').default;
|
27
31
|
default:
|
28
32
|
throw new Error(`Invalid backend: ${backend}`);
|
29
33
|
}
|
package/lib/bus.js
CHANGED
@@ -17,7 +17,8 @@ class Bus extends _events().default {
|
|
17
17
|
emit(event, ...args) {
|
18
18
|
if (_childState.child) {
|
19
19
|
_childState.child.workerApi.callMaster({
|
20
|
-
|
20
|
+
// $FlowFixMe
|
21
|
+
location: process.browser ? '@parcel/workers/src/bus.js' : __filename,
|
21
22
|
method: 'emit',
|
22
23
|
args: [event, ...args]
|
23
24
|
}, false);
|
package/lib/child.js
CHANGED
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
5
5
|
});
|
6
6
|
exports.Child = void 0;
|
7
|
+
var coreWorker = _interopRequireWildcard(require("./core-worker"));
|
7
8
|
function _assert() {
|
8
9
|
const data = _interopRequireDefault(require("assert"));
|
9
10
|
_assert = function () {
|
@@ -48,9 +49,9 @@ function _profiler() {
|
|
48
49
|
return data;
|
49
50
|
}
|
50
51
|
var _Handle2 = _interopRequireDefault(require("./Handle"));
|
52
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
51
53
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
52
54
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
53
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
54
55
|
// The import of './Handle' should really be imported eagerly (with @babel/plugin-transform-modules-commonjs's lazy mode).
|
55
56
|
const Handle = _Handle2.default;
|
56
57
|
class Child {
|
@@ -97,8 +98,17 @@ class Child {
|
|
97
98
|
this.child.send(data);
|
98
99
|
}
|
99
100
|
async childInit(module, childId) {
|
100
|
-
// $FlowFixMe
|
101
|
-
|
101
|
+
// $FlowFixMe
|
102
|
+
if (process.browser) {
|
103
|
+
if (module === '@parcel/core/src/worker.js') {
|
104
|
+
this.module = coreWorker;
|
105
|
+
} else {
|
106
|
+
throw new Error('No dynamic require possible: ' + module);
|
107
|
+
}
|
108
|
+
} else {
|
109
|
+
// $FlowFixMe this must be dynamic
|
110
|
+
this.module = require(module);
|
111
|
+
}
|
102
112
|
this.childId = childId;
|
103
113
|
if (this.module.childInit != null) {
|
104
114
|
await this.module.childInit();
|
package/lib/cpuCount.js
CHANGED
@@ -56,11 +56,19 @@ function getCores(bypassCache = false) {
|
|
56
56
|
if (cores && !bypassCache) {
|
57
57
|
return cores;
|
58
58
|
}
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
//
|
63
|
-
cores =
|
59
|
+
|
60
|
+
// $FlowFixMe
|
61
|
+
if (process.browser) {
|
62
|
+
// eslint-disable-next-line no-undef
|
63
|
+
cores = navigator.hardwareConcurrency / 2;
|
64
|
+
}
|
65
|
+
if (!cores) {
|
66
|
+
try {
|
67
|
+
cores = detectRealCores();
|
68
|
+
} catch (e) {
|
69
|
+
// Guess the amount of real cores
|
70
|
+
cores = _os().default.cpus().filter((cpu, index) => !cpu.model.includes('Intel') || index % 2 === 1).length;
|
71
|
+
}
|
64
72
|
}
|
65
73
|
|
66
74
|
// Another fallback
|
@@ -0,0 +1,44 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
4
|
+
value: true
|
5
|
+
});
|
6
|
+
exports.default = void 0;
|
7
|
+
var _childState = require("../childState");
|
8
|
+
var _child = require("../child");
|
9
|
+
function _core() {
|
10
|
+
const data = require("@parcel/core");
|
11
|
+
_core = function () {
|
12
|
+
return data;
|
13
|
+
};
|
14
|
+
return data;
|
15
|
+
}
|
16
|
+
/* eslint-env worker*/
|
17
|
+
class WebChild {
|
18
|
+
constructor(onMessage, onExit) {
|
19
|
+
if (!(typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)) {
|
20
|
+
throw new Error('Only create WebChild instances in a worker!');
|
21
|
+
}
|
22
|
+
this.onMessage = onMessage;
|
23
|
+
this.onExit = onExit;
|
24
|
+
self.addEventListener('message', ({
|
25
|
+
data
|
26
|
+
}) => {
|
27
|
+
if (data === 'stop') {
|
28
|
+
this.onExit(0);
|
29
|
+
self.postMessage('stopped');
|
30
|
+
}
|
31
|
+
// $FlowFixMe assume WorkerMessage as data
|
32
|
+
this.handleMessage(data);
|
33
|
+
});
|
34
|
+
self.postMessage('online');
|
35
|
+
}
|
36
|
+
handleMessage(data) {
|
37
|
+
this.onMessage((0, _core().restoreDeserializedObject)(data));
|
38
|
+
}
|
39
|
+
send(data) {
|
40
|
+
self.postMessage((0, _core().prepareForSerialization)(data));
|
41
|
+
}
|
42
|
+
}
|
43
|
+
exports.default = WebChild;
|
44
|
+
(0, _childState.setChild)(new _child.Child(WebChild));
|
@@ -0,0 +1,85 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
4
|
+
value: true
|
5
|
+
});
|
6
|
+
exports.default = void 0;
|
7
|
+
function _core() {
|
8
|
+
const data = require("@parcel/core");
|
9
|
+
_core = function () {
|
10
|
+
return data;
|
11
|
+
};
|
12
|
+
return data;
|
13
|
+
}
|
14
|
+
function _utils() {
|
15
|
+
const data = require("@parcel/utils");
|
16
|
+
_utils = function () {
|
17
|
+
return data;
|
18
|
+
};
|
19
|
+
return data;
|
20
|
+
}
|
21
|
+
let id = 0;
|
22
|
+
class WebWorker {
|
23
|
+
constructor(execArgv, onMessage, onError, onExit) {
|
24
|
+
this.execArgv = execArgv;
|
25
|
+
this.onMessage = onMessage;
|
26
|
+
this.onError = onError;
|
27
|
+
this.onExit = onExit;
|
28
|
+
}
|
29
|
+
start() {
|
30
|
+
// $FlowFixMe[incompatible-call]
|
31
|
+
this.worker = new Worker(new URL('./WebChild.js', import.meta.url), {
|
32
|
+
name: `Parcel Worker ${id++}`,
|
33
|
+
type: 'module'
|
34
|
+
});
|
35
|
+
let {
|
36
|
+
deferred,
|
37
|
+
promise
|
38
|
+
} = (0, _utils().makeDeferredWithPromise)();
|
39
|
+
this.worker.onmessage = ({
|
40
|
+
data
|
41
|
+
}) => {
|
42
|
+
if (data === 'online') {
|
43
|
+
deferred.resolve();
|
44
|
+
return;
|
45
|
+
}
|
46
|
+
|
47
|
+
// $FlowFixMe assume WorkerMessage as data
|
48
|
+
this.handleMessage(data);
|
49
|
+
};
|
50
|
+
this.worker.onerror = this.onError;
|
51
|
+
// Web workers can't crash or intentionally stop on their own, apart from stop() below
|
52
|
+
// this.worker.on('exit', this.onExit);
|
53
|
+
|
54
|
+
return promise;
|
55
|
+
}
|
56
|
+
stop() {
|
57
|
+
if (!this.stopping) {
|
58
|
+
this.stopping = (async () => {
|
59
|
+
this.worker.postMessage('stop');
|
60
|
+
let {
|
61
|
+
deferred,
|
62
|
+
promise
|
63
|
+
} = (0, _utils().makeDeferredWithPromise)();
|
64
|
+
this.worker.addEventListener('message', ({
|
65
|
+
data
|
66
|
+
}) => {
|
67
|
+
if (data === 'stopped') {
|
68
|
+
deferred.resolve();
|
69
|
+
}
|
70
|
+
});
|
71
|
+
await promise;
|
72
|
+
this.worker.terminate();
|
73
|
+
this.onExit(0);
|
74
|
+
})();
|
75
|
+
}
|
76
|
+
return this.stopping;
|
77
|
+
}
|
78
|
+
handleMessage(data) {
|
79
|
+
this.onMessage((0, _core().restoreDeserializedObject)(data));
|
80
|
+
}
|
81
|
+
send(data) {
|
82
|
+
this.worker.postMessage((0, _core().prepareForSerialization)(data));
|
83
|
+
}
|
84
|
+
}
|
85
|
+
exports.default = WebWorker;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@parcel/workers",
|
3
|
-
"version": "2.10.
|
3
|
+
"version": "2.10.3",
|
4
4
|
"description": "Blazing fast, zero configuration web application bundler",
|
5
5
|
"license": "MIT",
|
6
6
|
"publishConfig": {
|
@@ -21,20 +21,20 @@
|
|
21
21
|
"node": ">= 12.0.0"
|
22
22
|
},
|
23
23
|
"dependencies": {
|
24
|
-
"@parcel/diagnostic": "2.10.
|
25
|
-
"@parcel/logger": "2.10.
|
26
|
-
"@parcel/profiler": "2.10.
|
27
|
-
"@parcel/types": "2.10.
|
28
|
-
"@parcel/utils": "2.10.
|
24
|
+
"@parcel/diagnostic": "2.10.3",
|
25
|
+
"@parcel/logger": "2.10.3",
|
26
|
+
"@parcel/profiler": "2.10.3",
|
27
|
+
"@parcel/types": "2.10.3",
|
28
|
+
"@parcel/utils": "2.10.3",
|
29
29
|
"nullthrows": "^1.1.1"
|
30
30
|
},
|
31
31
|
"peerDependencies": {
|
32
|
-
"@parcel/core": "^2.10.
|
32
|
+
"@parcel/core": "^2.10.3"
|
33
33
|
},
|
34
34
|
"browser": {
|
35
|
-
"./src/cpuCount.js": false,
|
36
35
|
"./src/process/ProcessWorker.js": false,
|
37
|
-
"./src/threads/ThreadsWorker.js": false
|
36
|
+
"./src/threads/ThreadsWorker.js": false,
|
37
|
+
"./src/core-worker.js": "./src/core-worker.browser.js"
|
38
38
|
},
|
39
|
-
"gitHead": "
|
39
|
+
"gitHead": "65d42a955db665a04817fa9be55df16f588593d4"
|
40
40
|
}
|
package/src/Worker.js
CHANGED
@@ -48,19 +48,22 @@ export default class Worker extends EventEmitter {
|
|
48
48
|
}
|
49
49
|
|
50
50
|
async fork(forkModule: FilePath) {
|
51
|
-
let filteredArgs =
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
let
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
51
|
+
let filteredArgs = [];
|
52
|
+
if (process.execArgv) {
|
53
|
+
filteredArgs = process.execArgv.filter(
|
54
|
+
v => !/^--(debug|inspect|no-opt|max-old-space-size=)/.test(v),
|
55
|
+
);
|
56
|
+
|
57
|
+
for (let i = 0; i < filteredArgs.length; i++) {
|
58
|
+
let arg = filteredArgs[i];
|
59
|
+
let isArgWithParam =
|
60
|
+
((arg === '-r' || arg === '--require') &&
|
61
|
+
filteredArgs[i + 1] === '@parcel/register') ||
|
62
|
+
arg === '--title';
|
63
|
+
if (isArgWithParam) {
|
64
|
+
filteredArgs.splice(i, 2);
|
65
|
+
i--;
|
66
|
+
}
|
64
67
|
}
|
65
68
|
}
|
66
69
|
|
package/src/WorkerFarm.js
CHANGED
@@ -11,6 +11,8 @@ import type {
|
|
11
11
|
} from './types';
|
12
12
|
import type {HandleFunction} from './Handle';
|
13
13
|
|
14
|
+
import * as coreWorker from './core-worker';
|
15
|
+
import * as bus from './bus';
|
14
16
|
import invariant from 'assert';
|
15
17
|
import nullthrows from 'nullthrows';
|
16
18
|
import EventEmitter from 'events';
|
@@ -46,9 +48,10 @@ export type FarmOptions = {|
|
|
46
48
|
shouldTrace?: boolean,
|
47
49
|
|};
|
48
50
|
|
49
|
-
type WorkerModule = {
|
51
|
+
type WorkerModule = {
|
50
52
|
+[string]: (...args: Array<mixed>) => Promise<mixed>,
|
51
|
-
|
53
|
+
...
|
54
|
+
};
|
52
55
|
|
53
56
|
export type WorkerApi = {|
|
54
57
|
callMaster(CallRequest, ?boolean): Promise<mixed>,
|
@@ -74,6 +77,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
74
77
|
options: FarmOptions;
|
75
78
|
run: HandleFunction;
|
76
79
|
warmWorkers: number = 0;
|
80
|
+
readyWorkers: number = 0;
|
77
81
|
workers: Map<number, Worker> = new Map();
|
78
82
|
handles: Map<number, Handle> = new Map();
|
79
83
|
sharedReferences: Map<SharedReference, mixed> = new Map();
|
@@ -99,10 +103,23 @@ export default class WorkerFarm extends EventEmitter {
|
|
99
103
|
throw new Error('Please provide a worker path!');
|
100
104
|
}
|
101
105
|
|
102
|
-
// $FlowFixMe
|
103
|
-
|
106
|
+
// $FlowFixMe
|
107
|
+
if (process.browser) {
|
108
|
+
if (this.options.workerPath === '@parcel/core/src/worker.js') {
|
109
|
+
this.localWorker = coreWorker;
|
110
|
+
} else {
|
111
|
+
throw new Error(
|
112
|
+
'No dynamic require possible: ' + this.options.workerPath,
|
113
|
+
);
|
114
|
+
}
|
115
|
+
} else {
|
116
|
+
// $FlowFixMe this must be dynamic
|
117
|
+
this.localWorker = require(this.options.workerPath);
|
118
|
+
}
|
119
|
+
|
104
120
|
this.localWorkerInit =
|
105
121
|
this.localWorker.childInit != null ? this.localWorker.childInit() : null;
|
122
|
+
|
106
123
|
this.run = this.createHandle('run');
|
107
124
|
|
108
125
|
// Worker thread stdout is by default piped into the process stdout, if there are enough worker
|
@@ -113,7 +130,7 @@ export default class WorkerFarm extends EventEmitter {
|
|
113
130
|
// Note this can't be fixed easily where other things pipe into stdout - even after starting > 10 worker
|
114
131
|
// threads `process.stdout.getMaxListeners()` will still return 10, however adding another pipe into `stdout`
|
115
132
|
// will give the warning with `<worker count + 1>` as the number of listeners.
|
116
|
-
process.stdout
|
133
|
+
process.stdout?.setMaxListeners(
|
117
134
|
Math.max(
|
118
135
|
process.stdout.getMaxListeners(),
|
119
136
|
WorkerFarm.getNumWorkers() + 1,
|
@@ -251,7 +268,13 @@ export default class WorkerFarm extends EventEmitter {
|
|
251
268
|
|
252
269
|
worker.on('request', data => this.processRequest(data, worker));
|
253
270
|
|
254
|
-
worker.on('ready', () =>
|
271
|
+
worker.on('ready', () => {
|
272
|
+
this.readyWorkers++;
|
273
|
+
if (this.readyWorkers === this.options.maxConcurrentWorkers) {
|
274
|
+
this.emit('ready');
|
275
|
+
}
|
276
|
+
this.processQueue();
|
277
|
+
});
|
255
278
|
worker.on('response', () => this.processQueue());
|
256
279
|
|
257
280
|
worker.on('error', err => this.onError(err, worker));
|
@@ -332,8 +355,17 @@ export default class WorkerFarm extends EventEmitter {
|
|
332
355
|
if (handleId != null) {
|
333
356
|
mod = nullthrows(this.handles.get(handleId)?.fn);
|
334
357
|
} else if (location) {
|
335
|
-
// $FlowFixMe
|
336
|
-
|
358
|
+
// $FlowFixMe
|
359
|
+
if (process.browser) {
|
360
|
+
if (location === '@parcel/workers/src/bus.js') {
|
361
|
+
mod = (bus: any);
|
362
|
+
} else {
|
363
|
+
throw new Error('No dynamic require possible: ' + location);
|
364
|
+
}
|
365
|
+
} else {
|
366
|
+
// $FlowFixMe this must be dynamic
|
367
|
+
mod = require(location);
|
368
|
+
}
|
337
369
|
} else {
|
338
370
|
throw new Error('Unknown request');
|
339
371
|
}
|
package/src/backend.js
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
import type {BackendType, WorkerImpl} from './types';
|
3
3
|
|
4
4
|
export function detectBackend(): BackendType {
|
5
|
+
// $FlowFixMe
|
6
|
+
if (process.browser) return 'web';
|
7
|
+
|
5
8
|
switch (process.env.PARCEL_WORKER_BACKEND) {
|
6
9
|
case 'threads':
|
7
10
|
case 'process':
|
@@ -22,6 +25,8 @@ export function getWorkerBackend(backend: BackendType): Class<WorkerImpl> {
|
|
22
25
|
return require('./threads/ThreadsWorker').default;
|
23
26
|
case 'process':
|
24
27
|
return require('./process/ProcessWorker').default;
|
28
|
+
case 'web':
|
29
|
+
return require('./web/WebWorker').default;
|
25
30
|
default:
|
26
31
|
throw new Error(`Invalid backend: ${backend}`);
|
27
32
|
}
|
package/src/bus.js
CHANGED
package/src/child.js
CHANGED
@@ -12,6 +12,7 @@ import type {
|
|
12
12
|
import type {Async, IDisposable} from '@parcel/types';
|
13
13
|
import type {SharedReference} from './WorkerFarm';
|
14
14
|
|
15
|
+
import * as coreWorker from './core-worker';
|
15
16
|
import invariant from 'assert';
|
16
17
|
import nullthrows from 'nullthrows';
|
17
18
|
import Logger, {patchConsole, unpatchConsole} from '@parcel/logger';
|
@@ -100,8 +101,17 @@ export class Child {
|
|
100
101
|
}
|
101
102
|
|
102
103
|
async childInit(module: string, childId: number): Promise<void> {
|
103
|
-
// $FlowFixMe
|
104
|
-
|
104
|
+
// $FlowFixMe
|
105
|
+
if (process.browser) {
|
106
|
+
if (module === '@parcel/core/src/worker.js') {
|
107
|
+
this.module = coreWorker;
|
108
|
+
} else {
|
109
|
+
throw new Error('No dynamic require possible: ' + module);
|
110
|
+
}
|
111
|
+
} else {
|
112
|
+
// $FlowFixMe this must be dynamic
|
113
|
+
this.module = require(module);
|
114
|
+
}
|
105
115
|
this.childId = childId;
|
106
116
|
|
107
117
|
if (this.module.childInit != null) {
|
package/src/cpuCount.js
CHANGED
@@ -47,15 +47,23 @@ export default function getCores(bypassCache?: boolean = false): number {
|
|
47
47
|
return cores;
|
48
48
|
}
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
)
|
50
|
+
// $FlowFixMe
|
51
|
+
if (process.browser) {
|
52
|
+
// eslint-disable-next-line no-undef
|
53
|
+
cores = navigator.hardwareConcurrency / 2;
|
54
|
+
}
|
55
|
+
|
56
|
+
if (!cores) {
|
57
|
+
try {
|
58
|
+
cores = detectRealCores();
|
59
|
+
} catch (e) {
|
60
|
+
// Guess the amount of real cores
|
61
|
+
cores = os
|
62
|
+
.cpus()
|
63
|
+
.filter(
|
64
|
+
(cpu, index) => !cpu.model.includes('Intel') || index % 2 === 1,
|
65
|
+
).length;
|
66
|
+
}
|
59
67
|
}
|
60
68
|
|
61
69
|
// Another fallback
|
package/src/types.js
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
// @flow
|
2
|
+
/* eslint-env worker*/
|
3
|
+
|
4
|
+
import type {
|
5
|
+
ChildImpl,
|
6
|
+
MessageHandler,
|
7
|
+
ExitHandler,
|
8
|
+
WorkerMessage,
|
9
|
+
} from '../types';
|
10
|
+
import {setChild} from '../childState';
|
11
|
+
import {Child} from '../child';
|
12
|
+
import {prepareForSerialization, restoreDeserializedObject} from '@parcel/core';
|
13
|
+
|
14
|
+
export default class WebChild implements ChildImpl {
|
15
|
+
onMessage: MessageHandler;
|
16
|
+
onExit: ExitHandler;
|
17
|
+
|
18
|
+
constructor(onMessage: MessageHandler, onExit: ExitHandler) {
|
19
|
+
if (
|
20
|
+
!(
|
21
|
+
typeof WorkerGlobalScope !== 'undefined' &&
|
22
|
+
self instanceof WorkerGlobalScope
|
23
|
+
)
|
24
|
+
) {
|
25
|
+
throw new Error('Only create WebChild instances in a worker!');
|
26
|
+
}
|
27
|
+
|
28
|
+
this.onMessage = onMessage;
|
29
|
+
this.onExit = onExit;
|
30
|
+
self.addEventListener('message', ({data}: MessageEvent) => {
|
31
|
+
if (data === 'stop') {
|
32
|
+
this.onExit(0);
|
33
|
+
self.postMessage('stopped');
|
34
|
+
}
|
35
|
+
// $FlowFixMe assume WorkerMessage as data
|
36
|
+
this.handleMessage(data);
|
37
|
+
});
|
38
|
+
self.postMessage('online');
|
39
|
+
}
|
40
|
+
|
41
|
+
handleMessage(data: WorkerMessage) {
|
42
|
+
this.onMessage(restoreDeserializedObject(data));
|
43
|
+
}
|
44
|
+
|
45
|
+
send(data: WorkerMessage) {
|
46
|
+
self.postMessage(prepareForSerialization(data));
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
setChild(new Child(WebChild));
|
@@ -0,0 +1,85 @@
|
|
1
|
+
// @flow
|
2
|
+
|
3
|
+
import type {
|
4
|
+
WorkerImpl,
|
5
|
+
MessageHandler,
|
6
|
+
ErrorHandler,
|
7
|
+
ExitHandler,
|
8
|
+
WorkerMessage,
|
9
|
+
} from '../types';
|
10
|
+
import {prepareForSerialization, restoreDeserializedObject} from '@parcel/core';
|
11
|
+
import {makeDeferredWithPromise} from '@parcel/utils';
|
12
|
+
|
13
|
+
let id = 0;
|
14
|
+
|
15
|
+
export default class WebWorker implements WorkerImpl {
|
16
|
+
execArgv: Object;
|
17
|
+
onMessage: MessageHandler;
|
18
|
+
onError: ErrorHandler;
|
19
|
+
onExit: ExitHandler;
|
20
|
+
worker: Worker;
|
21
|
+
stopping: ?Promise<void>;
|
22
|
+
|
23
|
+
constructor(
|
24
|
+
execArgv: Object,
|
25
|
+
onMessage: MessageHandler,
|
26
|
+
onError: ErrorHandler,
|
27
|
+
onExit: ExitHandler,
|
28
|
+
) {
|
29
|
+
this.execArgv = execArgv;
|
30
|
+
this.onMessage = onMessage;
|
31
|
+
this.onError = onError;
|
32
|
+
this.onExit = onExit;
|
33
|
+
}
|
34
|
+
|
35
|
+
start(): Promise<void> {
|
36
|
+
// $FlowFixMe[incompatible-call]
|
37
|
+
this.worker = new Worker(new URL('./WebChild.js', import.meta.url), {
|
38
|
+
name: `Parcel Worker ${id++}`,
|
39
|
+
type: 'module',
|
40
|
+
});
|
41
|
+
|
42
|
+
let {deferred, promise} = makeDeferredWithPromise();
|
43
|
+
|
44
|
+
this.worker.onmessage = ({data}) => {
|
45
|
+
if (data === 'online') {
|
46
|
+
deferred.resolve();
|
47
|
+
return;
|
48
|
+
}
|
49
|
+
|
50
|
+
// $FlowFixMe assume WorkerMessage as data
|
51
|
+
this.handleMessage(data);
|
52
|
+
};
|
53
|
+
this.worker.onerror = this.onError;
|
54
|
+
// Web workers can't crash or intentionally stop on their own, apart from stop() below
|
55
|
+
// this.worker.on('exit', this.onExit);
|
56
|
+
|
57
|
+
return promise;
|
58
|
+
}
|
59
|
+
|
60
|
+
stop(): Promise<void> {
|
61
|
+
if (!this.stopping) {
|
62
|
+
this.stopping = (async () => {
|
63
|
+
this.worker.postMessage('stop');
|
64
|
+
let {deferred, promise} = makeDeferredWithPromise();
|
65
|
+
this.worker.addEventListener('message', ({data}: MessageEvent) => {
|
66
|
+
if (data === 'stopped') {
|
67
|
+
deferred.resolve();
|
68
|
+
}
|
69
|
+
});
|
70
|
+
await promise;
|
71
|
+
this.worker.terminate();
|
72
|
+
this.onExit(0);
|
73
|
+
})();
|
74
|
+
}
|
75
|
+
return this.stopping;
|
76
|
+
}
|
77
|
+
|
78
|
+
handleMessage(data: WorkerMessage) {
|
79
|
+
this.onMessage(restoreDeserializedObject(data));
|
80
|
+
}
|
81
|
+
|
82
|
+
send(data: WorkerMessage) {
|
83
|
+
this.worker.postMessage(prepareForSerialization(data));
|
84
|
+
}
|
85
|
+
}
|