metro 0.80.9 → 0.80.10

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.
Files changed (60) hide show
  1. package/package.json +18 -18
  2. package/src/DeltaBundler/Serializers/baseJSBundle.js +1 -0
  3. package/src/DeltaBundler/Serializers/baseJSBundle.js.flow +1 -0
  4. package/src/DeltaBundler/Serializers/getRamBundleInfo.js +1 -0
  5. package/src/DeltaBundler/Serializers/getRamBundleInfo.js.flow +1 -0
  6. package/src/DeltaBundler/Serializers/helpers/getSourceMapInfo.js +1 -1
  7. package/src/DeltaBundler/Serializers/helpers/getSourceMapInfo.js.flow +2 -1
  8. package/src/DeltaBundler/Serializers/sourceMapGenerator.js +1 -0
  9. package/src/DeltaBundler/Serializers/sourceMapGenerator.js.flow +3 -0
  10. package/src/DeltaBundler/Serializers/sourceMapString.js +14 -2
  11. package/src/DeltaBundler/Serializers/sourceMapString.js.flow +18 -2
  12. package/src/DeltaBundler/Transformer.js +1 -1
  13. package/src/DeltaBundler/Transformer.js.flow +4 -2
  14. package/src/DeltaBundler/types.flow.js.flow +2 -1
  15. package/src/ModuleGraph/worker/collectDependencies.js +54 -4
  16. package/src/ModuleGraph/worker/collectDependencies.js.flow +69 -4
  17. package/src/Server.js +113 -10
  18. package/src/Server.js.flow +130 -10
  19. package/src/cli/parseKeyValueParamArray.js +4 -5
  20. package/src/cli/parseKeyValueParamArray.js.flow +5 -3
  21. package/src/index.flow.js +7 -0
  22. package/src/index.flow.js.flow +8 -0
  23. package/src/integration_tests/basic_bundle/excluded_from_file_map.js +8 -0
  24. package/src/integration_tests/basic_bundle/excluded_from_file_map.js.flow +11 -0
  25. package/src/integration_tests/basic_bundle/import-export/export-7.js +5 -0
  26. package/src/integration_tests/basic_bundle/import-export/export-7.js.flow +15 -0
  27. package/src/integration_tests/basic_bundle/import-export/export-8.js +10 -0
  28. package/src/integration_tests/basic_bundle/import-export/export-8.js.flow +15 -0
  29. package/src/integration_tests/basic_bundle/import-export/index.js +9 -1
  30. package/src/integration_tests/basic_bundle/import-export/index.js.flow +9 -0
  31. package/src/integration_tests/basic_bundle/import-export/utils.js +1 -0
  32. package/src/integration_tests/basic_bundle/import-export/utils.js.flow +14 -0
  33. package/src/integration_tests/basic_bundle/not_a_source_file.xyz +1 -0
  34. package/src/integration_tests/metro.config.js +1 -0
  35. package/src/lib/BatchProcessor.js +3 -0
  36. package/src/lib/BatchProcessor.js.flow +4 -0
  37. package/src/lib/JsonReporter.js +30 -3
  38. package/src/lib/JsonReporter.js.flow +40 -7
  39. package/src/lib/TerminalReporter.js +4 -0
  40. package/src/lib/TerminalReporter.js.flow +12 -1
  41. package/src/lib/getAppendScripts.js +4 -1
  42. package/src/lib/getAppendScripts.js.flow +5 -1
  43. package/src/lib/parseOptionsFromUrl.js +4 -0
  44. package/src/lib/parseOptionsFromUrl.js.flow +4 -0
  45. package/src/lib/reporting.d.ts +6 -0
  46. package/src/lib/reporting.js.flow +6 -0
  47. package/src/lib/splitBundleOptions.js +1 -0
  48. package/src/lib/splitBundleOptions.js.flow +1 -0
  49. package/src/node-haste/DependencyGraph/ModuleResolution.js +10 -9
  50. package/src/node-haste/DependencyGraph/ModuleResolution.js.flow +16 -14
  51. package/src/node-haste/DependencyGraph.js +8 -4
  52. package/src/node-haste/DependencyGraph.js.flow +12 -4
  53. package/src/node-haste/Module.js +1 -1
  54. package/src/node-haste/Module.js.flow +1 -1
  55. package/src/node-haste/ModuleCache.js +28 -14
  56. package/src/node-haste/ModuleCache.js.flow +43 -18
  57. package/src/shared/output/bundle.flow.js +2 -2
  58. package/src/shared/output/bundle.flow.js.flow +2 -2
  59. package/src/shared/types.flow.js +10 -0
  60. package/src/shared/types.flow.js.flow +9 -0
@@ -43,6 +43,8 @@ import type {
43
43
  import type {CustomResolverOptions} from 'metro-resolver/src/types';
44
44
  import type {CustomTransformOptions} from 'metro-transform-worker';
45
45
 
46
+ import {SourcePathsMode} from './shared/types.flow';
47
+
46
48
  const {getAsset} = require('./Assets');
47
49
  const baseJSBundle = require('./DeltaBundler/Serializers/baseJSBundle');
48
50
  const getAllFiles = require('./DeltaBundler/Serializers/getAllFiles');
@@ -51,7 +53,9 @@ const {
51
53
  getExplodedSourceMap,
52
54
  } = require('./DeltaBundler/Serializers/getExplodedSourceMap');
53
55
  const getRamBundleInfo = require('./DeltaBundler/Serializers/getRamBundleInfo');
54
- const sourceMapString = require('./DeltaBundler/Serializers/sourceMapString');
56
+ const {
57
+ sourceMapStringNonBlocking,
58
+ } = require('./DeltaBundler/Serializers/sourceMapString');
55
59
  const IncrementalBundler = require('./IncrementalBundler');
56
60
  const ResourceNotFoundError = require('./IncrementalBundler/ResourceNotFoundError');
57
61
  const bundleToString = require('./lib/bundleToString');
@@ -143,6 +147,10 @@ class Server {
143
147
  _platforms: Set<string>;
144
148
  _reporter: Reporter;
145
149
  _serverOptions: ServerOptions | void;
150
+ _allowedSuffixesForSourceRequests: $ReadOnlyArray<string>;
151
+ _sourceRequestRoutingMap: $ReadOnlyArray<
152
+ [pathnamePrefix: string, normalizedRootDir: string],
153
+ >;
146
154
 
147
155
  constructor(config: ConfigT, options?: ServerOptions) {
148
156
  this._config = config;
@@ -158,6 +166,22 @@ class Server {
158
166
  this._reporter = config.reporter;
159
167
  this._logger = Logger;
160
168
  this._platforms = new Set(this._config.resolver.platforms);
169
+ this._allowedSuffixesForSourceRequests = [
170
+ ...new Set(
171
+ [
172
+ ...this._config.resolver.sourceExts,
173
+ ...this._config.watcher.additionalExts,
174
+ ...this._config.resolver.assetExts,
175
+ ].map(ext => '.' + ext),
176
+ ),
177
+ ];
178
+ this._sourceRequestRoutingMap = [
179
+ ['/[metro-project]/', path.resolve(this._config.projectRoot)],
180
+ ...this._config.watchFolders.map((watchFolder, index) => [
181
+ `/[metro-watchFolders]/${index}/`,
182
+ path.resolve(watchFolder),
183
+ ]),
184
+ ];
161
185
  this._isEnded = false;
162
186
 
163
187
  // TODO(T34760917): These two properties should eventually be instantiated
@@ -242,6 +266,8 @@ class Server {
242
266
  this._config.server.unstable_serverRoot ?? this._config.projectRoot,
243
267
  shouldAddToIgnoreList: (module: Module<>) =>
244
268
  this._shouldAddModuleToIgnoreList(module),
269
+ getSourceUrl: (module: Module<>) =>
270
+ this._getModuleSourceUrl(module, serializerOptions.sourcePaths),
245
271
  };
246
272
  let bundleCode = null;
247
273
  let bundleMap = null;
@@ -264,12 +290,14 @@ class Server {
264
290
  ).code;
265
291
  }
266
292
  if (!bundleMap) {
267
- bundleMap = sourceMapString(
293
+ bundleMap = await sourceMapStringNonBlocking(
268
294
  [...prepend, ...this._getSortedModules(graph)],
269
295
  {
270
296
  excludeSource: serializerOptions.excludeSource,
271
297
  processModuleFilter: this._config.serializer.processModuleFilter,
272
298
  shouldAddToIgnoreList: bundleOptions.shouldAddToIgnoreList,
299
+ getSourceUrl: (module: Module<>) =>
300
+ this._getModuleSourceUrl(module, serializerOptions.sourcePaths),
273
301
  },
274
302
  );
275
303
  }
@@ -333,6 +361,8 @@ class Server {
333
361
  this._config.server.unstable_serverRoot ?? this._config.projectRoot,
334
362
  shouldAddToIgnoreList: (module: Module<>) =>
335
363
  this._shouldAddModuleToIgnoreList(module),
364
+ getSourceUrl: (module: Module<>) =>
365
+ this._getModuleSourceUrl(module, serializerOptions.sourcePaths),
336
366
  });
337
367
  }
338
368
 
@@ -510,7 +540,7 @@ class Server {
510
540
  ) {
511
541
  const originalUrl = req.url;
512
542
  req.url = this._rewriteAndNormalizeUrl(req.url);
513
- const urlObj = url.parse(req.url, true);
543
+ const urlObj = url.parse(decodeURI(req.url), true);
514
544
  const {host} = req.headers;
515
545
  debug(
516
546
  `Handling request: ${host ? 'http://' + host : ''}${req.url}` +
@@ -563,8 +593,62 @@ class Server {
563
593
  } else if (pathname === '/symbolicate') {
564
594
  await this._symbolicate(req, res);
565
595
  } else {
566
- next();
596
+ let handled = false;
597
+ for (const [pathnamePrefix, normalizedRootDir] of this
598
+ ._sourceRequestRoutingMap) {
599
+ if (pathname.startsWith(pathnamePrefix)) {
600
+ const relativePathname = pathname.substr(pathnamePrefix.length);
601
+ await this._processSourceRequest(
602
+ relativePathname,
603
+ normalizedRootDir,
604
+ res,
605
+ );
606
+ handled = true;
607
+ break;
608
+ }
609
+ }
610
+ if (!handled) {
611
+ next();
612
+ }
613
+ }
614
+ }
615
+
616
+ async _processSourceRequest(
617
+ relativePathname: string,
618
+ rootDir: string,
619
+ res: ServerResponse,
620
+ ): Promise<void> {
621
+ if (
622
+ !this._allowedSuffixesForSourceRequests.some(suffix =>
623
+ relativePathname.endsWith(suffix),
624
+ )
625
+ ) {
626
+ res.writeHead(404);
627
+ res.end();
628
+ return;
629
+ }
630
+ const depGraph = await this._bundler.getBundler().getDependencyGraph();
631
+ const filePath = path.join(rootDir, relativePathname);
632
+ try {
633
+ depGraph.getSha1(filePath);
634
+ } catch {
635
+ res.writeHead(404);
636
+ res.end();
637
+ return;
567
638
  }
639
+ const mimeType = mime.lookup(path.basename(relativePathname));
640
+ res.setHeader('Content-Type', mimeType);
641
+ const stream = fs.createReadStream(filePath);
642
+ stream.pipe(res);
643
+ stream.on('error', error => {
644
+ if (error.code === 'ENOENT') {
645
+ res.writeHead(404);
646
+ res.end();
647
+ } else {
648
+ res.writeHead(500);
649
+ res.end();
650
+ }
651
+ });
568
652
  }
569
653
 
570
654
  _createRequestProcessor<T>({
@@ -921,6 +1005,8 @@ class Server {
921
1005
  this._config.server.unstable_serverRoot ?? this._config.projectRoot,
922
1006
  shouldAddToIgnoreList: (module: Module<>) =>
923
1007
  this._shouldAddModuleToIgnoreList(module),
1008
+ getSourceUrl: (module: Module<>) =>
1009
+ this._getModuleSourceUrl(module, serializerOptions.sourcePaths),
924
1010
  },
925
1011
  );
926
1012
  bundlePerfLogger.point('serializingBundle_end');
@@ -1062,12 +1148,17 @@ class Server {
1062
1148
  prepend = [];
1063
1149
  }
1064
1150
 
1065
- return sourceMapString([...prepend, ...this._getSortedModules(graph)], {
1066
- excludeSource: serializerOptions.excludeSource,
1067
- processModuleFilter: this._config.serializer.processModuleFilter,
1068
- shouldAddToIgnoreList: (module: Module<>) =>
1069
- this._shouldAddModuleToIgnoreList(module),
1070
- });
1151
+ return await sourceMapStringNonBlocking(
1152
+ [...prepend, ...this._getSortedModules(graph)],
1153
+ {
1154
+ excludeSource: serializerOptions.excludeSource,
1155
+ processModuleFilter: this._config.serializer.processModuleFilter,
1156
+ shouldAddToIgnoreList: (module: Module<>) =>
1157
+ this._shouldAddModuleToIgnoreList(module),
1158
+ getSourceUrl: (module: Module<>) =>
1159
+ this._getModuleSourceUrl(module, serializerOptions.sourcePaths),
1160
+ },
1161
+ );
1071
1162
  },
1072
1163
  finish({mres, result}) {
1073
1164
  mres.setHeader('Content-Type', 'application/json');
@@ -1372,6 +1463,7 @@ class Server {
1372
1463
  shallow: false,
1373
1464
  sourceMapUrl: null,
1374
1465
  sourceUrl: null,
1466
+ sourcePaths: SourcePathsMode,
1375
1467
  } = {
1376
1468
  ...Server.DEFAULT_GRAPH_OPTIONS,
1377
1469
  excludeSource: false,
@@ -1383,6 +1475,7 @@ class Server {
1383
1475
  shallow: false,
1384
1476
  sourceMapUrl: null,
1385
1477
  sourceUrl: null,
1478
+ sourcePaths: SourcePathsMode.Absolute,
1386
1479
  };
1387
1480
 
1388
1481
  _getServerRootDir(): string {
@@ -1409,6 +1502,33 @@ class Server {
1409
1502
  this._config.serializer.isThirdPartyModule(module)
1410
1503
  );
1411
1504
  }
1505
+
1506
+ // Flow checking is enough to ensure that a value is returned in all cases.
1507
+ // eslint-disable-next-line consistent-return
1508
+ _getModuleSourceUrl(module: Module<>, mode: SourcePathsMode): string {
1509
+ switch (mode) {
1510
+ case SourcePathsMode.ServerUrl:
1511
+ for (const [pathnamePrefix, normalizedRootDir] of this
1512
+ ._sourceRequestRoutingMap) {
1513
+ if (module.path.startsWith(normalizedRootDir + path.sep)) {
1514
+ const relativePath = module.path.slice(
1515
+ normalizedRootDir.length + 1,
1516
+ );
1517
+ const relativePathPosix = relativePath.split(path.sep).join('/');
1518
+ return pathnamePrefix + encodeURI(relativePathPosix);
1519
+ }
1520
+ }
1521
+ // Ordinarily all files should match one of the roots above. If they
1522
+ // don't, try to preserve useful information, even if fetching the path
1523
+ // from Metro might fail.
1524
+ const modulePathPosix = module.path.split(path.sep).join('/');
1525
+ return modulePathPosix.startsWith('/')
1526
+ ? encodeURI(modulePathPosix)
1527
+ : '/' + encodeURI(modulePathPosix);
1528
+ case SourcePathsMode.Absolute:
1529
+ return module.path;
1530
+ }
1531
+ }
1412
1532
  }
1413
1533
 
1414
1534
  function* zip<X, Y>(xs: Iterable<X>, ys: Iterable<Y>): Iterable<[X, Y]> {
@@ -4,10 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true,
5
5
  });
6
6
  exports.default = coerceKeyValueArray;
7
- var _querystring = _interopRequireDefault(require("querystring"));
8
- function _interopRequireDefault(obj) {
9
- return obj && obj.__esModule ? obj : { default: obj };
10
- }
11
7
  function coerceKeyValueArray(keyValueArray) {
12
8
  const result = Object.create(null);
13
9
  for (const item of keyValueArray) {
@@ -17,7 +13,10 @@ function coerceKeyValueArray(keyValueArray) {
17
13
  if (item.indexOf("&") !== -1) {
18
14
  throw new Error('Parameter cannot include "&" but found: ' + item);
19
15
  }
20
- Object.assign(result, _querystring.default.parse(item));
16
+ const params = new URLSearchParams(item);
17
+ params.forEach((value, key) => {
18
+ result[key] = value;
19
+ });
21
20
  }
22
21
  return result;
23
22
  }
@@ -9,8 +9,6 @@
9
9
  * @oncall react_native
10
10
  */
11
11
 
12
- import querystring from 'querystring';
13
-
14
12
  export default function coerceKeyValueArray(
15
13
  keyValueArray: $ReadOnlyArray<string>,
16
14
  ): {
@@ -25,7 +23,11 @@ export default function coerceKeyValueArray(
25
23
  if (item.indexOf('&') !== -1) {
26
24
  throw new Error('Parameter cannot include "&" but found: ' + item);
27
25
  }
28
- Object.assign(result, querystring.parse(item));
26
+ const params = new URLSearchParams(item);
27
+ params.forEach((value, key) => {
28
+ // $FlowExpectedError[prop-missing]
29
+ result[key] = value;
30
+ });
29
31
  }
30
32
  return result;
31
33
  }
package/src/index.flow.js CHANGED
@@ -167,6 +167,13 @@ exports.runServer = async (
167
167
  end();
168
168
  });
169
169
  httpServer.listen(config.server.port, host, () => {
170
+ const { address, port, family } = httpServer.address();
171
+ config.reporter.update({
172
+ type: "server_listening",
173
+ address,
174
+ port,
175
+ family,
176
+ });
170
177
  if (onReady) {
171
178
  onReady(httpServer);
172
179
  }
@@ -309,6 +309,14 @@ exports.runServer = async (
309
309
  });
310
310
 
311
311
  httpServer.listen(config.server.port, host, () => {
312
+ const {address, port, family} = httpServer.address();
313
+ config.reporter.update({
314
+ type: 'server_listening',
315
+ address,
316
+ port, // Assigned port if configured with port 0
317
+ family,
318
+ });
319
+
312
320
  if (onReady) {
313
321
  onReady(httpServer);
314
322
  }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true,
5
+ });
6
+ exports.default = void 0;
7
+ var _default = "/* secret */";
8
+ exports.default = _default;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ */
10
+
11
+ export default '/* secret */';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ foo: "export-7: FOO",
5
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @flow strict
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ module.exports = {
14
+ foo: 'export-7: FOO',
15
+ };
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true,
5
+ });
6
+ exports.foo = exports.default = void 0;
7
+ var _default = "export-8: DEFAULT";
8
+ exports.default = _default;
9
+ const foo = "export-8: FOO";
10
+ exports.foo = foo;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @flow strict
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ export default 'export-8: DEFAULT';
14
+
15
+ export const foo = 'export-8: FOO';
@@ -3,7 +3,11 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true,
5
5
  });
6
- exports.asyncImportESM = exports.asyncImportCJS = void 0;
6
+ exports.asyncImportMaybeSyncESM =
7
+ exports.asyncImportMaybeSyncCJS =
8
+ exports.asyncImportESM =
9
+ exports.asyncImportCJS =
10
+ void 0;
7
11
  Object.defineProperty(exports, "default", {
8
12
  enumerable: true,
9
13
  get: function () {
@@ -82,3 +86,7 @@ const asyncImportCJS = import("./export-5");
82
86
  exports.asyncImportCJS = asyncImportCJS;
83
87
  const asyncImportESM = import("./export-6");
84
88
  exports.asyncImportESM = asyncImportESM;
89
+ const asyncImportMaybeSyncCJS = require.unstable_importMaybeSync("./export-7");
90
+ exports.asyncImportMaybeSyncCJS = asyncImportMaybeSyncCJS;
91
+ const asyncImportMaybeSyncESM = require.unstable_importMaybeSync("./export-8");
92
+ exports.asyncImportMaybeSyncESM = asyncImportMaybeSyncESM;
@@ -10,6 +10,8 @@
10
10
 
11
11
  'use strict';
12
12
 
13
+ import type {RequireWithUnstableImportMaybeSync} from './utils';
14
+
13
15
  import {default as myDefault, foo as myFoo, myFunction} from './export-1';
14
16
  import * as importStar from './export-2';
15
17
  import {foo} from './export-null';
@@ -17,6 +19,8 @@ import primitiveDefault, {
17
19
  foo as primitiveFoo,
18
20
  } from './export-primitive-default';
19
21
 
22
+ declare var require: RequireWithUnstableImportMaybeSync;
23
+
20
24
  export {default as namedDefaultExported} from './export-3';
21
25
  export {foo as default} from './export-4';
22
26
 
@@ -32,3 +36,8 @@ export const extraData = {
32
36
 
33
37
  export const asyncImportCJS = import('./export-5');
34
38
  export const asyncImportESM = import('./export-6');
39
+
40
+ export const asyncImportMaybeSyncCJS: mixed =
41
+ require.unstable_importMaybeSync('./export-7');
42
+ export const asyncImportMaybeSyncESM: mixed =
43
+ require.unstable_importMaybeSync('./export-8');
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @flow strict
9
+ */
10
+
11
+ export type RequireWithUnstableImportMaybeSync = {
12
+ (id: string | number): mixed,
13
+ unstable_importMaybeSync: (id: string) => mixed,
14
+ };
@@ -14,6 +14,7 @@ module.exports = {
14
14
  port: 10028,
15
15
  },
16
16
  resolver: {
17
+ blockList: [/excluded_from_file_map\.js$/],
17
18
  useWatchman: false,
18
19
  },
19
20
  transformer: {
@@ -62,5 +62,8 @@ class BatchProcessor {
62
62
  this._processQueueOnceReady();
63
63
  });
64
64
  }
65
+ getQueueLength() {
66
+ return this._queue.length;
67
+ }
65
68
  }
66
69
  module.exports = BatchProcessor;
@@ -125,6 +125,10 @@ class BatchProcessor<TItem, TResult> {
125
125
  },
126
126
  );
127
127
  }
128
+
129
+ getQueueLength(): number {
130
+ return this._queue.length;
131
+ }
128
132
  }
129
133
 
130
134
  module.exports = BatchProcessor;
@@ -5,13 +5,40 @@ class JsonReporter {
5
5
  this._stream = stream;
6
6
  }
7
7
  update(event) {
8
- if (Object.prototype.toString.call(event.error) === "[object Error]") {
8
+ if (event.error instanceof Error) {
9
+ const { message, stack } = event.error;
9
10
  event = Object.assign(event, {
10
- message: event.error.message,
11
- stack: event.error.stack,
11
+ error: serializeError(event.error),
12
+ message,
13
+ stack,
12
14
  });
13
15
  }
14
16
  this._stream.write(JSON.stringify(event) + "\n");
15
17
  }
16
18
  }
19
+ function serializeError(e, seen = new Set()) {
20
+ if (seen.has(e)) {
21
+ return {
22
+ message: "[circular]: " + e.message,
23
+ stack: e.stack,
24
+ };
25
+ }
26
+ seen.add(e);
27
+ const { message, stack, cause } = e;
28
+ const serialized = {
29
+ message,
30
+ stack,
31
+ };
32
+ if (e instanceof AggregateError) {
33
+ serialized.errors = [...e.errors]
34
+ .map((innerError) =>
35
+ innerError instanceof Error ? serializeError(innerError, seen) : null
36
+ )
37
+ .filter(Boolean);
38
+ }
39
+ if (cause instanceof Error) {
40
+ serialized.cause = serializeError(cause, seen);
41
+ }
42
+ return serialized;
43
+ }
17
44
  module.exports = JsonReporter;
@@ -13,14 +13,21 @@
13
13
 
14
14
  import type {Writable} from 'stream';
15
15
 
16
+ export type SerializedError = {
17
+ message: string,
18
+ stack: string,
19
+ errors?: $ReadOnlyArray<SerializedError>,
20
+ cause?: SerializedError,
21
+ ...
22
+ };
23
+
16
24
  export type SerializedEvent<TEvent: {[string]: any, ...}> = TEvent extends {
17
25
  error: Error,
18
26
  ...
19
27
  }
20
28
  ? {
21
29
  ...Omit<TEvent, 'error'>,
22
- message: string,
23
- stack: string,
30
+ error: SerializedError,
24
31
  ...
25
32
  }
26
33
  : TEvent;
@@ -37,16 +44,42 @@ class JsonReporter<TEvent: {[string]: any, ...}> {
37
44
  * (Perhaps we should switch in favor of plain object?)
38
45
  */
39
46
  update(event: TEvent): void {
40
- // $FlowFixMe[method-unbinding] added when improving typing for this parameters
41
- if (Object.prototype.toString.call(event.error) === '[object Error]') {
47
+ if (event.error instanceof Error) {
48
+ const {message, stack} = event.error;
42
49
  event = Object.assign(event, {
43
- message: event.error.message,
44
- stack: event.error.stack,
50
+ error: serializeError(event.error),
51
+ // TODO: Preexisting issue - this writes message, stack, etc. as
52
+ // top-level siblings of event.error (which was serialized to {}), whereas it was presumably
53
+ // intended to nest them _under_ error. Fix this in a breaking change.
54
+ message,
55
+ stack,
45
56
  });
46
57
  }
47
-
48
58
  this._stream.write(JSON.stringify(event) + '\n');
49
59
  }
50
60
  }
51
61
 
62
+ function serializeError(
63
+ e: Error,
64
+ seen: Set<Error> = new Set(),
65
+ ): SerializedError {
66
+ if (seen.has(e)) {
67
+ return {message: '[circular]: ' + e.message, stack: e.stack};
68
+ }
69
+ seen.add(e);
70
+ const {message, stack, cause} = e;
71
+ const serialized: SerializedError = {message, stack};
72
+ if (e instanceof AggregateError) {
73
+ serialized.errors = [...e.errors]
74
+ .map(innerError =>
75
+ innerError instanceof Error ? serializeError(innerError, seen) : null,
76
+ )
77
+ .filter(Boolean);
78
+ }
79
+ if (cause instanceof Error) {
80
+ serialized.cause = serializeError(cause, seen);
81
+ }
82
+ return serialized;
83
+ }
84
+
52
85
  module.exports = JsonReporter;
@@ -257,6 +257,9 @@ class TerminalReporter {
257
257
  case "bundle_transform_progressed_throttled":
258
258
  this._updateBundleProgress(event);
259
259
  break;
260
+ case "unstable_set_interaction_status":
261
+ this._interactionStatus = event.status;
262
+ break;
260
263
  }
261
264
  }
262
265
  _getStatusMessage() {
@@ -264,6 +267,7 @@ class TerminalReporter {
264
267
  .map(([_, progress]) =>
265
268
  this._getBundleStatusMessage(progress, "in_progress")
266
269
  )
270
+ .concat([this._interactionStatus])
267
271
  .filter((str) => str != null)
268
272
  .join("\n");
269
273
  }
@@ -39,6 +39,11 @@ export type TerminalReportableEvent =
39
39
  transformedFileCount: number,
40
40
  totalFileCount: number,
41
41
  ...
42
+ }
43
+ | {
44
+ type: 'unstable_set_interaction_status',
45
+ status: ?string,
46
+ ...
42
47
  };
43
48
 
44
49
  type BuildPhase = 'in_progress' | 'done' | 'failed';
@@ -65,6 +70,8 @@ class TerminalReporter {
65
70
  */
66
71
  _activeBundles: Map<string, BundleProgress>;
67
72
 
73
+ _interactionStatus: ?string;
74
+
68
75
  _scheduleUpdateBundleProgress: {
69
76
  (data: {
70
77
  buildID: string,
@@ -378,6 +385,9 @@ class TerminalReporter {
378
385
  case 'bundle_transform_progressed_throttled':
379
386
  this._updateBundleProgress(event);
380
387
  break;
388
+ case 'unstable_set_interaction_status':
389
+ this._interactionStatus = event.status;
390
+ break;
381
391
  }
382
392
  }
383
393
 
@@ -391,7 +401,8 @@ class TerminalReporter {
391
401
  .map(([_, progress]: [string, BundleProgress]) =>
392
402
  this._getBundleStatusMessage(progress, 'in_progress'),
393
403
  )
394
- .filter((str: null | string) => str != null)
404
+ .concat([this._interactionStatus])
405
+ .filter((str: ?string) => str != null)
395
406
  .join('\n');
396
407
  }
397
408