@parcel/workers 2.0.0-nightly.1427 → 2.0.0-nightly.1430
Sign up to get free protection for your applications and to get access to all the features.
- 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.0.0-nightly.
|
3
|
+
"version": "2.0.0-nightly.1430+febe16994",
|
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.0.0-nightly.
|
25
|
-
"@parcel/logger": "2.0.0-nightly.
|
26
|
-
"@parcel/profiler": "2.10.3-nightly.
|
27
|
-
"@parcel/types": "2.0.0-nightly.
|
28
|
-
"@parcel/utils": "2.0.0-nightly.
|
24
|
+
"@parcel/diagnostic": "2.0.0-nightly.1430+febe16994",
|
25
|
+
"@parcel/logger": "2.0.0-nightly.1430+febe16994",
|
26
|
+
"@parcel/profiler": "2.10.3-nightly.3053+febe16994",
|
27
|
+
"@parcel/types": "2.0.0-nightly.1430+febe16994",
|
28
|
+
"@parcel/utils": "2.0.0-nightly.1430+febe16994",
|
29
29
|
"nullthrows": "^1.1.1"
|
30
30
|
},
|
31
31
|
"peerDependencies": {
|
32
|
-
"@parcel/core": "2.0.0-nightly.
|
32
|
+
"@parcel/core": "2.0.0-nightly.1428+febe16994"
|
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": "febe169948376cceb60efd9d5a5982106dc80b5a"
|
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
|
+
}
|