@parcel/workers 2.10.2 → 2.10.3

Sign up to get free protection for your applications and to get access to all the features.
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 = process.execArgv.filter(v => !/^--(debug|inspect|no-opt|max-old-space-size=)/.test(v));
46
- for (let i = 0; i < filteredArgs.length; i++) {
47
- let arg = filteredArgs[i];
48
- let isArgWithParam = (arg === '-r' || arg === '--require') && filteredArgs[i + 1] === '@parcel/register' || arg === '--title';
49
- if (isArgWithParam) {
50
- filteredArgs.splice(i, 2);
51
- i--;
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 this must be dynamic
110
- this.localWorker = require(this.options.workerPath);
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', () => this.processQueue());
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 this must be dynamic
282
- mod = require(location);
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
- location: __filename,
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 this must be dynamic
101
- this.module = require(module);
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();
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ // eslint-disable-next-line monorepo/no-internal-import
4
+ module.exports = require('@parcel/core/src/worker.js');
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ // This is used only in browser builds
4
+ module.exports = {};
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
- try {
60
- cores = detectRealCores();
61
- } catch (e) {
62
- // Guess the amount of real cores
63
- cores = _os().default.cpus().filter((cpu, index) => !cpu.model.includes('Intel') || index % 2 === 1).length;
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.2",
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.2",
25
- "@parcel/logger": "2.10.2",
26
- "@parcel/profiler": "2.10.2",
27
- "@parcel/types": "2.10.2",
28
- "@parcel/utils": "2.10.2",
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.2"
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": "a1391ed8a719fc2f976dbadb528ca2dcffa7da12"
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 = process.execArgv.filter(
52
- v => !/^--(debug|inspect|no-opt|max-old-space-size=)/.test(v),
53
- );
54
-
55
- for (let i = 0; i < filteredArgs.length; i++) {
56
- let arg = filteredArgs[i];
57
- let isArgWithParam =
58
- ((arg === '-r' || arg === '--require') &&
59
- filteredArgs[i + 1] === '@parcel/register') ||
60
- arg === '--title';
61
- if (isArgWithParam) {
62
- filteredArgs.splice(i, 2);
63
- i--;
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 this must be dynamic
103
- this.localWorker = require(this.options.workerPath);
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.setMaxListeners(
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', () => this.processQueue());
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 this must be dynamic
336
- mod = require(location);
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
@@ -7,7 +7,8 @@ class Bus extends EventEmitter {
7
7
  if (child) {
8
8
  child.workerApi.callMaster(
9
9
  {
10
- location: __filename,
10
+ // $FlowFixMe
11
+ location: process.browser ? '@parcel/workers/src/bus.js' : __filename,
11
12
  method: 'emit',
12
13
  args: [event, ...args],
13
14
  },
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 this must be dynamic
104
- this.module = require(module);
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) {
@@ -0,0 +1,3 @@
1
+ // @flow
2
+ // eslint-disable-next-line monorepo/no-internal-import
3
+ module.exports = require('@parcel/core/src/worker.js');
@@ -0,0 +1,2 @@
1
+ // This is used only in browser builds
2
+ module.exports = {};
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
- try {
51
- cores = detectRealCores();
52
- } catch (e) {
53
- // Guess the amount of real cores
54
- cores = os
55
- .cpus()
56
- .filter(
57
- (cpu, index) => !cpu.model.includes('Intel') || index % 2 === 1,
58
- ).length;
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
@@ -65,4 +65,4 @@ export interface ChildImpl {
65
65
  send(data: WorkerMessage): void;
66
66
  }
67
67
 
68
- export type BackendType = 'threads' | 'process';
68
+ export type BackendType = 'threads' | 'process' | 'web';
@@ -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
+ }