@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 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.1",
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.1",
25
- "@parcel/logger": "2.10.1",
26
- "@parcel/profiler": "2.10.1",
27
- "@parcel/types": "2.10.1",
28
- "@parcel/utils": "2.10.1",
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.1"
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": "bba0417f68159b1f71c747cfd06fa3fe2dd374b5"
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
+ }