metro 0.83.3 → 0.84.0

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 (158) hide show
  1. package/package.json +28 -25
  2. package/src/Assets.js +42 -29
  3. package/src/Assets.js.flow +26 -15
  4. package/src/Bundler/util.js +25 -21
  5. package/src/Bundler/util.js.flow +2 -2
  6. package/src/Bundler.js.flow +1 -1
  7. package/src/DeltaBundler/DeltaCalculator.js +4 -4
  8. package/src/DeltaBundler/DeltaCalculator.js.flow +8 -8
  9. package/src/DeltaBundler/Graph.js +16 -16
  10. package/src/DeltaBundler/Graph.js.flow +30 -30
  11. package/src/DeltaBundler/Serializers/baseJSBundle.js.flow +1 -1
  12. package/src/DeltaBundler/Serializers/getAllFiles.js.flow +2 -2
  13. package/src/DeltaBundler/Serializers/getAssets.js.flow +2 -2
  14. package/src/DeltaBundler/Serializers/getExplodedSourceMap.js.flow +2 -2
  15. package/src/DeltaBundler/Serializers/getRamBundleInfo.js.flow +8 -8
  16. package/src/DeltaBundler/Serializers/helpers/js.js +25 -21
  17. package/src/DeltaBundler/Serializers/helpers/js.js.flow +6 -6
  18. package/src/DeltaBundler/Serializers/helpers/processModules.js.flow +3 -3
  19. package/src/DeltaBundler/Serializers/hmrJSBundle.js +25 -21
  20. package/src/DeltaBundler/Serializers/hmrJSBundle.js.flow +5 -5
  21. package/src/DeltaBundler/Serializers/sourceMapGenerator.js.flow +6 -6
  22. package/src/DeltaBundler/Serializers/sourceMapObject.js.flow +2 -2
  23. package/src/DeltaBundler/Serializers/sourceMapString.js.flow +2 -2
  24. package/src/DeltaBundler/Transformer.js.flow +3 -3
  25. package/src/DeltaBundler/Worker.flow.js.flow +1 -1
  26. package/src/DeltaBundler/WorkerFarm.js +1 -1
  27. package/src/DeltaBundler/WorkerFarm.js.flow +26 -13
  28. package/src/DeltaBundler/buildSubgraph.js +4 -4
  29. package/src/DeltaBundler/buildSubgraph.js.flow +8 -8
  30. package/src/DeltaBundler/types.js.flow +36 -34
  31. package/src/DeltaBundler.js.flow +2 -2
  32. package/src/HmrServer.js +34 -29
  33. package/src/HmrServer.js.flow +17 -12
  34. package/src/IncrementalBundler.js +29 -21
  35. package/src/IncrementalBundler.js.flow +13 -9
  36. package/src/ModuleGraph/worker/JsFileWrapping.js +25 -21
  37. package/src/ModuleGraph/worker/JsFileWrapping.js.flow +10 -5
  38. package/src/ModuleGraph/worker/collectDependencies.js +25 -21
  39. package/src/ModuleGraph/worker/collectDependencies.js.flow +27 -21
  40. package/src/ModuleGraph/worker/generateImportNames.js.flow +4 -2
  41. package/src/ModuleGraph/worker/importLocationsPlugin.js.flow +7 -3
  42. package/src/Server/MultipartResponse.js.flow +1 -1
  43. package/src/Server/symbolicate.js.flow +4 -4
  44. package/src/Server.js +139 -39
  45. package/src/Server.js.flow +158 -47
  46. package/src/cli/parseKeyValueParamArray.js.flow +1 -1
  47. package/src/cli-utils.js.flow +2 -2
  48. package/src/commands/build.js.flow +11 -10
  49. package/src/commands/dependencies.js.flow +8 -4
  50. package/src/commands/serve.js +2 -0
  51. package/src/commands/serve.js.flow +14 -9
  52. package/src/index.flow.js +30 -26
  53. package/src/index.flow.js.flow +25 -20
  54. package/src/integration_tests/basic_bundle/AssetRegistry.js.flow +1 -1
  55. package/src/integration_tests/basic_bundle/ErrorBundle.js.flow +1 -1
  56. package/src/integration_tests/basic_bundle/build-errors/cannot-resolve-import.js.flow +1 -1
  57. package/src/integration_tests/basic_bundle/build-errors/cannot-resolve-multi-line-import-with-escapes.js.flow +1 -1
  58. package/src/integration_tests/basic_bundle/build-errors/cannot-resolve-multi-line-import.js.flow +1 -1
  59. package/src/integration_tests/basic_bundle/build-errors/cannot-resolve-require-with-embedded-comment.js.flow +1 -1
  60. package/src/integration_tests/basic_bundle/build-errors/cannot-resolve-require.js.flow +1 -1
  61. package/src/integration_tests/basic_bundle/build-errors/cannot-resolve-specifier-with-escapes.js.flow +1 -1
  62. package/src/integration_tests/basic_bundle/build-errors/inline-requires-cannot-resolve-import.js.flow +1 -1
  63. package/src/integration_tests/basic_bundle/build-errors/inline-requires-cannot-resolve-require.js.flow +1 -1
  64. package/src/integration_tests/basic_bundle/import-export/index.js +25 -21
  65. package/src/integration_tests/basic_bundle/import-export/index.js.flow +3 -3
  66. package/src/integration_tests/basic_bundle/import-export/utils.js.flow +2 -2
  67. package/src/integration_tests/basic_bundle/loadBundleAsyncForTest.js.flow +1 -1
  68. package/src/integration_tests/basic_bundle/optional-dependencies/index.js.flow +1 -1
  69. package/src/integration_tests/basic_bundle/require-context/conflict.js.flow +1 -1
  70. package/src/integration_tests/basic_bundle/require-context/empty.js.flow +1 -1
  71. package/src/integration_tests/basic_bundle/require-context/matching.js.flow +1 -1
  72. package/src/integration_tests/basic_bundle/require-context/mode-eager.js.flow +1 -1
  73. package/src/integration_tests/basic_bundle/require-context/mode-lazy-once.js.flow +1 -1
  74. package/src/integration_tests/basic_bundle/require-context/mode-lazy.js.flow +1 -1
  75. package/src/integration_tests/basic_bundle/require-context/mode-sync.js.flow +2 -2
  76. package/src/integration_tests/basic_bundle/require-context/utils.js.flow +1 -1
  77. package/src/integration_tests/basic_bundle/require-resolveWeak/import-and-resolveWeak.js.flow +1 -1
  78. package/src/integration_tests/basic_bundle/require-resolveWeak/multiple.js.flow +1 -1
  79. package/src/integration_tests/basic_bundle/require-resolveWeak/never-required.js.flow +1 -1
  80. package/src/integration_tests/basic_bundle/require-resolveWeak/require-and-resolveWeak.js.flow +1 -1
  81. package/src/integration_tests/execBundle.js.flow +1 -1
  82. package/src/lib/BatchProcessor.js +5 -2
  83. package/src/lib/BatchProcessor.js.flow +10 -7
  84. package/src/lib/CountingSet.js.flow +4 -4
  85. package/src/lib/JsonReporter.js +5 -3
  86. package/src/lib/JsonReporter.js.flow +19 -17
  87. package/src/lib/RamBundleParser.js.flow +1 -1
  88. package/src/lib/TerminalReporter.js +31 -27
  89. package/src/lib/TerminalReporter.js.flow +15 -15
  90. package/src/lib/contextModule.js.flow +1 -1
  91. package/src/lib/contextModuleTemplates.js +25 -21
  92. package/src/lib/countLines.js +4 -3
  93. package/src/lib/countLines.js.flow +3 -4
  94. package/src/lib/createWebsocketServer.js +9 -2
  95. package/src/lib/createWebsocketServer.js.flow +16 -9
  96. package/src/lib/debounceAsyncQueue.js.flow +1 -1
  97. package/src/lib/formatBundlingError.js.flow +1 -1
  98. package/src/lib/getAppendScripts.js.flow +4 -4
  99. package/src/lib/getGraphId.js.flow +1 -1
  100. package/src/lib/getPreludeCode.js +4 -0
  101. package/src/lib/getPreludeCode.js.flow +10 -3
  102. package/src/lib/getPrependedScripts.js +36 -22
  103. package/src/lib/getPrependedScripts.js.flow +10 -3
  104. package/src/lib/logToConsole.js.flow +2 -2
  105. package/src/lib/parseBundleOptionsFromBundleRequestUrl.js +25 -21
  106. package/src/lib/parseCustomResolverOptions.js.flow +2 -2
  107. package/src/lib/parseCustomTransformOptions.js.flow +1 -1
  108. package/src/lib/parseJsonBody.js.flow +11 -1
  109. package/src/lib/pathUtils.js +25 -21
  110. package/src/lib/pathUtils.js.flow +1 -1
  111. package/src/lib/reporting.js.flow +4 -4
  112. package/src/lib/transformHelpers.js +11 -9
  113. package/src/lib/transformHelpers.js.flow +17 -15
  114. package/src/node-haste/DependencyGraph/ModuleResolution.js +48 -42
  115. package/src/node-haste/DependencyGraph/ModuleResolution.js.flow +35 -32
  116. package/src/node-haste/DependencyGraph/createFileMap.js +56 -38
  117. package/src/node-haste/DependencyGraph/createFileMap.js.flow +44 -18
  118. package/src/node-haste/DependencyGraph.js +40 -31
  119. package/src/node-haste/DependencyGraph.js.flow +35 -37
  120. package/src/node-haste/lib/AssetPaths.js +2 -2
  121. package/src/node-haste/lib/AssetPaths.js.flow +4 -4
  122. package/src/node-haste/lib/parsePlatformFilePath.js +6 -6
  123. package/src/node-haste/lib/parsePlatformFilePath.js.flow +4 -4
  124. package/src/shared/output/RamBundle/as-assets.js.flow +6 -6
  125. package/src/shared/output/RamBundle/as-indexed-file.js.flow +5 -5
  126. package/src/shared/output/RamBundle/buildSourcemapWithMetadata.js.flow +5 -5
  127. package/src/shared/output/RamBundle/util.js.flow +5 -5
  128. package/src/shared/output/RamBundle/write-sourcemap.js.flow +1 -1
  129. package/src/shared/output/RamBundle.js.flow +1 -1
  130. package/src/shared/output/bundle.flow.js.flow +3 -3
  131. package/src/shared/output/meta.js +2 -2
  132. package/src/shared/output/meta.js.flow +1 -1
  133. package/src/shared/output/writeFile.js +8 -3
  134. package/src/shared/output/writeFile.js.flow +8 -2
  135. package/src/shared/types.js.flow +20 -5
  136. package/src/Asset.d.ts +0 -25
  137. package/src/Bundler.d.ts +0 -39
  138. package/src/DeltaBundler/Graph.d.ts +0 -40
  139. package/src/DeltaBundler/Serializers/getExplodedSourceMap.d.ts +0 -26
  140. package/src/DeltaBundler/Serializers/getRamBundleInfo.d.ts +0 -18
  141. package/src/DeltaBundler/Worker.d.ts +0 -45
  142. package/src/DeltaBundler/types.d.ts +0 -166
  143. package/src/DeltaBundler.d.ts +0 -58
  144. package/src/IncrementalBundler.d.ts +0 -98
  145. package/src/ModuleGraph/test-helpers.js +0 -75
  146. package/src/ModuleGraph/worker/collectDependencies.d.ts +0 -27
  147. package/src/Server/MultipartResponse.d.ts +0 -31
  148. package/src/Server/symbolicate.d.ts +0 -31
  149. package/src/Server.d.ts +0 -118
  150. package/src/index.d.ts +0 -193
  151. package/src/lib/CountingSet.d.ts +0 -48
  152. package/src/lib/TerminalReporter.d.ts +0 -27
  153. package/src/lib/contextModule.d.ts +0 -22
  154. package/src/lib/getGraphId.d.ts +0 -11
  155. package/src/lib/reporting.d.ts +0 -144
  156. package/src/node-haste/DependencyGraph.d.ts +0 -62
  157. package/src/shared/output/bundle.d.ts +0 -35
  158. package/src/shared/types.d.ts +0 -130
@@ -22,6 +22,7 @@ import type {
22
22
  } from './DeltaBundler/types';
23
23
  import type {RevisionId} from './IncrementalBundler';
24
24
  import type {GraphId} from './lib/getGraphId';
25
+ import type {JsonData} from './lib/parseJsonBody';
25
26
  import type {Reporter} from './lib/reporting';
26
27
  import type {StackFrameInput, StackFrameOutput} from './Server/symbolicate';
27
28
  import type {
@@ -65,7 +66,6 @@ import symbolicate from './Server/symbolicate';
65
66
  import {SourcePathsMode} from './shared/types';
66
67
  import {codeFrameColumns} from '@babel/code-frame';
67
68
  import * as fs from 'graceful-fs';
68
- import invariant from 'invariant';
69
69
  import * as jscSafeUrl from 'jsc-safe-url';
70
70
  import {Logger} from 'metro-core';
71
71
  import mime from 'mime-types';
@@ -98,6 +98,7 @@ export type BundleMetadata = {
98
98
  };
99
99
 
100
100
  type ProcessStartContext = {
101
+ ...SplitBundleOptions,
101
102
  +buildNumber: number,
102
103
  +bundleOptions: BundleOptions,
103
104
  +graphId: GraphId,
@@ -107,7 +108,6 @@ type ProcessStartContext = {
107
108
  +revisionId?: ?RevisionId,
108
109
  +bundlePerfLogger: RootPerfLogger,
109
110
  +requestStartTimestamp: number,
110
- ...SplitBundleOptions,
111
111
  };
112
112
 
113
113
  type ProcessDeleteContext = {
@@ -121,7 +121,7 @@ type ProcessEndContext<T> = {
121
121
  +result: T,
122
122
  };
123
123
 
124
- export type ServerOptions = $ReadOnly<{
124
+ export type ServerOptions = Readonly<{
125
125
  hasReducedPerformance?: boolean,
126
126
  onBundleBuilt?: (bundlePath: string) => void,
127
127
  watch?: boolean,
@@ -130,6 +130,13 @@ export type ServerOptions = $ReadOnly<{
130
130
  const DELTA_ID_HEADER = 'X-Metro-Delta-ID';
131
131
  const FILES_CHANGED_COUNT_HEADER = 'X-Metro-Files-Changed-Count';
132
132
 
133
+ type FetchTiming = {
134
+ graphId: GraphId,
135
+ startTime: number,
136
+ endTime: number | null,
137
+ isPrefetch: boolean,
138
+ };
139
+
133
140
  export default class Server {
134
141
  _bundler: IncrementalBundler;
135
142
  _config: ConfigT;
@@ -140,10 +147,12 @@ export default class Server {
140
147
  _platforms: Set<string>;
141
148
  _reporter: Reporter;
142
149
  _serverOptions: ServerOptions | void;
143
- _allowedSuffixesForSourceRequests: $ReadOnlyArray<string>;
144
- _sourceRequestRoutingMap: $ReadOnlyArray<
150
+ _allowedSuffixesForSourceRequests: ReadonlyArray<string>;
151
+ _sourceRequestRoutingMap: ReadonlyArray<
145
152
  [pathnamePrefix: string, normalizedRootDir: string],
146
153
  >;
154
+ _fetchTimings: Array<FetchTiming>;
155
+ _activeFetchCount: number;
147
156
 
148
157
  constructor(config: ConfigT, options?: ServerOptions) {
149
158
  this._config = config;
@@ -176,6 +185,8 @@ export default class Server {
176
185
  ]),
177
186
  ];
178
187
  this._isEnded = false;
188
+ this._fetchTimings = [];
189
+ this._activeFetchCount = 0;
179
190
 
180
191
  // TODO(T34760917): These two properties should eventually be instantiated
181
192
  // elsewhere and passed as parameters, since they are also needed by
@@ -208,9 +219,9 @@ export default class Server {
208
219
  splitOptions,
209
220
  prepend,
210
221
  graph,
211
- }: $ReadOnly<{
222
+ }: Readonly<{
212
223
  splitOptions: SplitBundleOptions,
213
- prepend: $ReadOnlyArray<Module<>>,
224
+ prepend: ReadonlyArray<Module<>>,
214
225
  graph: ReadOnlyGraph<>,
215
226
  }>): Promise<{code: string, map: string}> {
216
227
  const {
@@ -299,7 +310,7 @@ export default class Server {
299
310
  ): Promise<{
300
311
  code: string,
301
312
  map: string,
302
- assets?: $ReadOnlyArray<AssetData>,
313
+ assets?: ReadonlyArray<AssetData>,
303
314
  ...
304
315
  }> {
305
316
  const splitOptions = splitBundleOptions(bundleOptions);
@@ -403,7 +414,7 @@ export default class Server {
403
414
  });
404
415
  }
405
416
 
406
- async getAssets(options: BundleOptions): Promise<$ReadOnlyArray<AssetData>> {
417
+ async getAssets(options: BundleOptions): Promise<ReadonlyArray<AssetData>> {
407
418
  const {entryFile, onProgress, resolverOptions, transformOptions} =
408
419
  splitBundleOptions(options);
409
420
 
@@ -423,7 +434,7 @@ export default class Server {
423
434
  async _getAssetsFromDependencies(
424
435
  dependencies: ReadOnlyDependencies<>,
425
436
  platform: ?string,
426
- ): Promise<$ReadOnlyArray<AssetData>> {
437
+ ): Promise<ReadonlyArray<AssetData>> {
427
438
  return await getAssets(dependencies, {
428
439
  processModuleFilter: this._config.serializer.processModuleFilter,
429
440
  assetPlugins: this._config.transformer.assetPlugins,
@@ -549,12 +560,14 @@ export default class Server {
549
560
  );
550
561
 
551
562
  try {
563
+ const depGraph = await this._bundler.getBundler().getDependencyGraph();
552
564
  const data = await getAsset(
553
565
  assetPath,
554
566
  this._config.projectRoot,
555
567
  this._config.watchFolders,
556
568
  urlObj.searchParams.get('platform'),
557
569
  this._config.resolver.assetExts,
570
+ filePath => depGraph.doesFileExist(filePath),
558
571
  );
559
572
  // Tell clients to cache this for 1 year.
560
573
  // This is safe as the asset url contains a hash of the asset.
@@ -615,6 +628,13 @@ export default class Server {
615
628
  debug('Rewritten to: %s', req.url);
616
629
  }
617
630
  const reqHost = req.headers['x-forwarded-host'] || req.headers['host'];
631
+ debug('Request host is: %s', req.headers['host']);
632
+ if (req.headers['x-forwarded-host']) {
633
+ debug(
634
+ 'Request x-forwarded-host is: %s',
635
+ req.headers['x-forwarded-host'],
636
+ );
637
+ }
618
638
  if (!reqHost) {
619
639
  throw new Error('No host header was found.');
620
640
  }
@@ -759,7 +779,7 @@ export default class Server {
759
779
  req: IncomingMessage,
760
780
  res: ServerResponse,
761
781
  bundleOptions: BundleOptions,
762
- buildContext: $ReadOnly<{
782
+ buildContext: Readonly<{
763
783
  buildNumber: number,
764
784
  bundlePerfLogger: RootPerfLogger,
765
785
  }>,
@@ -769,7 +789,7 @@ export default class Server {
769
789
  req: IncomingMessage,
770
790
  res: ServerResponse,
771
791
  bundleOptions: BundleOptions,
772
- buildContext: $ReadOnly<{
792
+ buildContext: Readonly<{
773
793
  buildNumber: number,
774
794
  bundlePerfLogger: RootPerfLogger,
775
795
  }>,
@@ -917,8 +937,18 @@ export default class Server {
917
937
  createActionStartEntry(createStartEntry(startContext)),
918
938
  );
919
939
 
940
+ const fetchTiming: FetchTiming = {
941
+ graphId,
942
+ startTime: requestStartTimestamp,
943
+ endTime: null,
944
+ isPrefetch: req.method === 'HEAD',
945
+ };
946
+
920
947
  let result;
921
948
  try {
949
+ this._fetchTimings.push(fetchTiming);
950
+ this._activeFetchCount++;
951
+
922
952
  result = await build(startContext);
923
953
  } catch (error) {
924
954
  const formattedError = formatBundlingError(error);
@@ -951,6 +981,33 @@ export default class Server {
951
981
  buildContext.bundlePerfLogger.end('FAIL');
952
982
 
953
983
  return;
984
+ } finally {
985
+ fetchTiming.endTime = performance.timeOrigin + performance.now();
986
+
987
+ if (!fetchTiming.isPrefetch) {
988
+ buildContext.bundlePerfLogger.annotate({
989
+ bool: {
990
+ had_competing_prefetch: this._fetchTimings
991
+ // fetching the same bundle as a prefetch don't compete, since they resolve a shared promise for the same graph id
992
+ .filter(t => t.isPrefetch && t.graphId !== graphId)
993
+ .some(prefetch => {
994
+ const prefetchEndTime =
995
+ prefetch.endTime ?? Number.MAX_SAFE_INTEGER;
996
+ const fetchEndTime =
997
+ fetchTiming.endTime ?? Number.MAX_SAFE_INTEGER;
998
+ return (
999
+ prefetch.startTime < fetchEndTime &&
1000
+ prefetchEndTime > fetchTiming.startTime
1001
+ );
1002
+ }),
1003
+ },
1004
+ });
1005
+ }
1006
+
1007
+ this._activeFetchCount--;
1008
+ if (this._activeFetchCount === 0) {
1009
+ this._fetchTimings = [];
1010
+ }
954
1011
  }
955
1012
 
956
1013
  const endContext: ProcessEndContext<T> = {
@@ -980,7 +1037,7 @@ export default class Server {
980
1037
  req: IncomingMessage,
981
1038
  res: ServerResponse,
982
1039
  bundleOptions: BundleOptions,
983
- buildContext: $ReadOnly<{
1040
+ buildContext: Readonly<{
984
1041
  buildNumber: number,
985
1042
  bundlePerfLogger: RootPerfLogger,
986
1043
  }>,
@@ -990,6 +1047,7 @@ export default class Server {
990
1047
  return {
991
1048
  action_name: 'Requesting bundle',
992
1049
  bundle_url: context.req.url,
1050
+ bundle_original_url: context.req.originalUrl ?? 'unknown',
993
1051
  entry_point: context.entryFile,
994
1052
  bundler: 'delta',
995
1053
  build_id: getBuildID(context.buildNumber),
@@ -1174,7 +1232,7 @@ export default class Server {
1174
1232
 
1175
1233
  // This function ensures that modules in source maps are sorted in the same
1176
1234
  // order as in a plain JS bundle.
1177
- _getSortedModules(graph: ReadOnlyGraph<>): $ReadOnlyArray<Module<>> {
1235
+ _getSortedModules(graph: ReadOnlyGraph<>): ReadonlyArray<Module<>> {
1178
1236
  const modules = [...graph.dependencies.values()];
1179
1237
  // Assign IDs to modules in a consistent order
1180
1238
  for (const module of modules) {
@@ -1191,7 +1249,7 @@ export default class Server {
1191
1249
  req: IncomingMessage,
1192
1250
  res: ServerResponse,
1193
1251
  bundleOptions: BundleOptions,
1194
- buildContext: $ReadOnly<{
1252
+ buildContext: Readonly<{
1195
1253
  buildNumber: number,
1196
1254
  bundlePerfLogger: RootPerfLogger,
1197
1255
  }>,
@@ -1263,7 +1321,7 @@ export default class Server {
1263
1321
  req: IncomingMessage,
1264
1322
  res: ServerResponse,
1265
1323
  bundleOptions: BundleOptions,
1266
- buildContext: $ReadOnly<{
1324
+ buildContext: Readonly<{
1267
1325
  buildNumber: number,
1268
1326
  bundlePerfLogger: RootPerfLogger,
1269
1327
  }>,
@@ -1277,7 +1335,7 @@ export default class Server {
1277
1335
  bundler: 'delta',
1278
1336
  };
1279
1337
  },
1280
- createEndEntry(context: ProcessEndContext<$ReadOnlyArray<AssetData>>) {
1338
+ createEndEntry(context: ProcessEndContext<ReadonlyArray<AssetData>>) {
1281
1339
  return {
1282
1340
  bundler: 'delta',
1283
1341
  };
@@ -1309,10 +1367,12 @@ export default class Server {
1309
1367
  },
1310
1368
  });
1311
1369
 
1312
- async _symbolicate(req: IncomingMessage, res: ServerResponse) {
1370
+ async _symbolicate(req: IncomingMessage, res: ServerResponse): Promise<void> {
1371
+ const depGraph = await this._bundler.getBundler().getDependencyGraph();
1372
+
1313
1373
  const getCodeFrame = (
1314
1374
  urls: Set<string>,
1315
- symbolicatedStack: $ReadOnlyArray<StackFrameOutput>,
1375
+ symbolicatedStack: ReadonlyArray<StackFrameOutput>,
1316
1376
  ) => {
1317
1377
  const allFramesCollapsed = symbolicatedStack.every(
1318
1378
  ({collapse}) => collapse,
@@ -1332,6 +1392,13 @@ export default class Server {
1332
1392
  }
1333
1393
 
1334
1394
  const fileAbsolute = path.resolve(this._config.projectRoot, file ?? '');
1395
+ if (!depGraph.doesFileExist(fileAbsolute)) {
1396
+ debug(
1397
+ 'Skipping code frame for file not in dependency graph.',
1398
+ fileAbsolute,
1399
+ );
1400
+ continue;
1401
+ }
1335
1402
  try {
1336
1403
  return {
1337
1404
  content: codeFrameColumns(
@@ -1361,6 +1428,7 @@ export default class Server {
1361
1428
  return null;
1362
1429
  };
1363
1430
 
1431
+ let inputValidated = false;
1364
1432
  try {
1365
1433
  const symbolicatingLogEntry = log(
1366
1434
  createActionStartEntry('Symbolicating'),
@@ -1373,35 +1441,78 @@ export default class Server {
1373
1441
  // < 0.80 and Expo SDK < 53
1374
1442
  // $FlowFixMe[prop-missing] - rawBody assigned by legacy CLI integrations
1375
1443
  const body = await req.rawBody;
1376
- parsedBody = JSON.parse(body);
1444
+ parsedBody = JSON.parse(body) as JsonData;
1377
1445
  } else {
1378
- parsedBody = (await parseJsonBody(req)) as {
1379
- stack: $ReadOnlyArray<StackFrameInput>,
1380
- extraData: {[string]: mixed},
1381
- };
1446
+ parsedBody = await parseJsonBody(req, {strict: false});
1382
1447
  }
1383
1448
 
1384
- const rewriteAndNormalizeStackFrame = <T>(
1385
- frame: T,
1386
- lineNumber: number,
1387
- ): T => {
1388
- invariant(
1389
- frame != null && typeof frame === 'object',
1390
- 'Bad stack frame at line %d, expected object, received: %s',
1391
- lineNumber,
1392
- typeof frame,
1449
+ let validatedBody: {
1450
+ stack: ReadonlyArray<JsonData>,
1451
+ extraData?: JsonData,
1452
+ };
1453
+
1454
+ if (
1455
+ parsedBody != null &&
1456
+ typeof parsedBody === 'object' &&
1457
+ !Array.isArray(parsedBody) &&
1458
+ Array.isArray(parsedBody['stack'])
1459
+ ) {
1460
+ const maybeStack: Array<JsonData> = parsedBody['stack'];
1461
+ const extraData = parsedBody['extraData'];
1462
+ validatedBody = {
1463
+ stack: maybeStack,
1464
+ extraData,
1465
+ };
1466
+ } else {
1467
+ throw new Error(
1468
+ `Bad symbolication input, expected object with stack array, got: ${JSON.stringify(parsedBody)}`,
1393
1469
  );
1394
- const frameFile = frame.file;
1395
- if (typeof frameFile === 'string' && frameFile.includes('://')) {
1396
- return {
1397
- ...frame,
1398
- file: this._rewriteAndNormalizeUrl(frameFile),
1399
- };
1470
+ }
1471
+
1472
+ const validateAndNormalizeStackFrame = (
1473
+ frame: JsonData,
1474
+ ): StackFrameInput => {
1475
+ if (
1476
+ frame == null ||
1477
+ typeof frame !== 'object' ||
1478
+ Array.isArray(frame)
1479
+ ) {
1480
+ throw new Error('Expected frame to be a JSON object');
1481
+ }
1482
+ if (frame.file != null && typeof frame.file !== 'string') {
1483
+ throw new Error('Expected file to be string or nullish');
1484
+ }
1485
+ let frameFile = frame.file;
1486
+ if (frameFile != null && frameFile.includes('://')) {
1487
+ frameFile = this._rewriteAndNormalizeUrl(frameFile);
1488
+ }
1489
+ if (frame.methodName != null && typeof frame.methodName !== 'string') {
1490
+ throw new Error('Expected methodName to be string or nullish');
1491
+ }
1492
+ if (frame.lineNumber != null && typeof frame.lineNumber !== 'number') {
1493
+ throw new Error('Expected lineNumber to be number or nullish');
1494
+ }
1495
+ if (frame.column != null && typeof frame.column !== 'number') {
1496
+ throw new Error('Expected column to be number or nullish');
1400
1497
  }
1401
- return frame;
1498
+ return {
1499
+ ...frame,
1500
+ file: frameFile,
1501
+ lineNumber: frame.lineNumber,
1502
+ column: frame.column,
1503
+ methodName: frame.methodName,
1504
+ };
1402
1505
  };
1403
1506
 
1404
- const stack = parsedBody.stack.map(rewriteAndNormalizeStackFrame);
1507
+ const stack = validatedBody.stack.map((frame, lineNumber) => {
1508
+ try {
1509
+ return validateAndNormalizeStackFrame(frame);
1510
+ } catch (e) {
1511
+ throw new Error(`Bad frame at line ${lineNumber}: ${e.message}`);
1512
+ }
1513
+ });
1514
+
1515
+ inputValidated = true;
1405
1516
  // In case of multiple bundles / HMR, some stack frames can have different URLs from others
1406
1517
  const urls = new Set<string>();
1407
1518
 
@@ -1433,7 +1544,7 @@ export default class Server {
1433
1544
  stack,
1434
1545
  zip(urls.values(), sourceMaps),
1435
1546
  this._config,
1436
- parsedBody.extraData ?? {},
1547
+ validatedBody.extraData ?? {},
1437
1548
  );
1438
1549
 
1439
1550
  debug('Symbolication done');
@@ -1448,7 +1559,7 @@ export default class Server {
1448
1559
  });
1449
1560
  } catch (error) {
1450
1561
  debug('Symbolication failed', error.stack || error);
1451
- res.statusCode = 500;
1562
+ res.statusCode = inputValidated ? 500 : 400;
1452
1563
  res.end(JSON.stringify({error: error.message}));
1453
1564
  }
1454
1565
  }
@@ -1518,7 +1629,7 @@ export default class Server {
1518
1629
  relativeTo,
1519
1630
  resolverOptions,
1520
1631
  transformOptions,
1521
- }: $ReadOnly<{
1632
+ }: Readonly<{
1522
1633
  relativeTo: 'project' | 'server',
1523
1634
  resolverOptions: ResolverInputOptions,
1524
1635
  transformOptions: TransformInputOptions,
@@ -1543,15 +1654,15 @@ export default class Server {
1543
1654
  return this._nextBundleBuildNumber++;
1544
1655
  }
1545
1656
 
1546
- getPlatforms(): $ReadOnlyArray<string> {
1657
+ getPlatforms(): ReadonlyArray<string> {
1547
1658
  return this._config.resolver.platforms;
1548
1659
  }
1549
1660
 
1550
- getWatchFolders(): $ReadOnlyArray<string> {
1661
+ getWatchFolders(): ReadonlyArray<string> {
1551
1662
  return this._config.watchFolders;
1552
1663
  }
1553
1664
 
1554
- static DEFAULT_GRAPH_OPTIONS: $ReadOnly<{
1665
+ static DEFAULT_GRAPH_OPTIONS: Readonly<{
1555
1666
  customResolverOptions: CustomResolverOptions,
1556
1667
  customTransformOptions: CustomTransformOptions,
1557
1668
  dev: boolean,
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  export default function coerceKeyValueArray(
13
- keyValueArray: $ReadOnlyArray<string>,
13
+ keyValueArray: ReadonlyArray<string>,
14
14
  ): {
15
15
  [key: string]: string,
16
16
  __proto__: null,
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @flow
7
+ * @flow strict
8
8
  * @format
9
9
  * @oncall react_native
10
10
  */
@@ -13,7 +13,7 @@ import fs from 'fs';
13
13
 
14
14
  export const watchFile = async function (
15
15
  filename: string,
16
- callback: () => any,
16
+ callback: () => unknown,
17
17
  ): Promise<void> {
18
18
  fs.watchFile(filename, () => {
19
19
  callback();
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @flow
7
+ * @flow strict-local
8
8
  * @format
9
9
  * @oncall react_native
10
10
  */
@@ -12,7 +12,7 @@
12
12
  import type {RunBuildOptions} from '../index';
13
13
  import type {CustomTransformOptions} from 'metro-babel-transformer';
14
14
  import type {CustomResolverOptions} from 'metro-resolver';
15
- import type {ModuleObject} from 'yargs';
15
+ import type {CommandModule} from 'yargs';
16
16
  import typeof Yargs from 'yargs';
17
17
 
18
18
  import {makeAsyncCommand} from '../cli-utils';
@@ -24,7 +24,9 @@ import {Terminal} from 'metro-core';
24
24
  const term = new Terminal(process.stdout);
25
25
  const updateReporter = new TerminalReporter(term);
26
26
 
27
- type Args = $ReadOnly<{
27
+ type Args = Readonly<{
28
+ _: unknown,
29
+ $0: unknown,
28
30
  config?: string,
29
31
  dev?: boolean,
30
32
  entry: string,
@@ -34,18 +36,16 @@ type Args = $ReadOnly<{
34
36
  out: string,
35
37
  outputType?: string,
36
38
  platform?: string,
37
- projectRoots?: $ReadOnlyArray<string>,
39
+ projectRoots?: ReadonlyArray<string>,
38
40
  resetCache?: boolean,
39
41
  sourceMap?: boolean,
40
42
  sourceMapUrl?: string,
41
43
  transformOption: CustomTransformOptions,
42
44
  resolverOption: CustomResolverOptions,
45
+ ...
43
46
  }>;
44
47
 
45
- export default (): {
46
- ...ModuleObject,
47
- handler: Function,
48
- } => ({
48
+ export default (): CommandModule => ({
49
49
  command: 'build <entry>',
50
50
  desc: 'Generates a JavaScript bundle containing the specified entrypoint and its descendants',
51
51
 
@@ -76,7 +76,7 @@ export default (): {
76
76
  type: 'string',
77
77
  array: true,
78
78
  alias: 'transformer-option',
79
- coerce: (parseKeyValueParamArray: $FlowFixMe),
79
+ coerce: parseKeyValueParamArray as $FlowFixMe,
80
80
  describe:
81
81
  'Custom transform options of the form key=value. URL-encoded. May be specified multiple times.',
82
82
  });
@@ -84,7 +84,7 @@ export default (): {
84
84
  yargs.option('resolver-option', {
85
85
  type: 'string',
86
86
  array: true,
87
- coerce: (parseKeyValueParamArray: $FlowFixMe),
87
+ coerce: parseKeyValueParamArray as $FlowFixMe,
88
88
  describe:
89
89
  'Custom resolver options of the form key=value. URL-encoded. May be specified multiple times.',
90
90
  });
@@ -94,6 +94,7 @@ export default (): {
94
94
  },
95
95
 
96
96
  handler: makeAsyncCommand(async (argv: Args) => {
97
+ // $FlowFixMe[incompatible-type] argv has extra props.
97
98
  const config = await loadConfig(argv);
98
99
  const options: RunBuildOptions = {
99
100
  entry: argv.entry,
@@ -4,13 +4,13 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @flow
7
+ * @flow strict-local
8
8
  * @format
9
9
  * @oncall react_native
10
10
  */
11
11
 
12
12
  import type {ConfigT} from 'metro-config';
13
- import type {ModuleObject} from 'yargs';
13
+ import type {CommandModule} from 'yargs';
14
14
  import typeof Yargs from 'yargs';
15
15
 
16
16
  import {makeAsyncCommand} from '../cli-utils';
@@ -20,7 +20,9 @@ import {loadConfig} from 'metro-config';
20
20
  import path from 'path';
21
21
  import {promisify} from 'util';
22
22
 
23
- type Args = $ReadOnly<{
23
+ type Args = Readonly<{
24
+ _: unknown,
25
+ $0: unknown,
24
26
  entryFile: string,
25
27
  output?: string,
26
28
  platform?: string,
@@ -28,6 +30,7 @@ type Args = $ReadOnly<{
28
30
  maxWorkers?: number,
29
31
  dev: boolean,
30
32
  verbose: boolean,
33
+ ...
31
34
  }>;
32
35
 
33
36
  async function dependencies(args: Args, config: ConfigT) {
@@ -82,7 +85,7 @@ async function dependencies(args: Args, config: ConfigT) {
82
85
  : Promise.resolve();
83
86
  }
84
87
 
85
- export default (): {...ModuleObject, handler: Function} => ({
88
+ export default (): CommandModule => ({
86
89
  command: 'get-dependencies [entryFile]',
87
90
  desc: 'List all dependencies that will be bundled for a given entry point',
88
91
  builder: (yargs: Yargs) => {
@@ -123,6 +126,7 @@ export default (): {...ModuleObject, handler: Function} => ({
123
126
  });
124
127
  },
125
128
  handler: makeAsyncCommand(async (argv: Args) => {
129
+ // $FlowFixMe[incompatible-type] argv has extra props.
126
130
  const config = await loadConfig(argv);
127
131
  await dependencies(argv, config);
128
132
  }),
@@ -80,6 +80,8 @@ var _default = () => ({
80
80
  const config = await (0, _metroConfig.loadConfig)(argv);
81
81
  const MetroApi = require("../index");
82
82
  const {
83
+ _,
84
+ $0: _0,
83
85
  config: _config,
84
86
  hmrEnabled: _hmrEnabled,
85
87
  maxWorkers: _maxWorkers,
@@ -4,36 +4,37 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @flow
7
+ * @flow strict-local
8
8
  * @format
9
9
  * @oncall react_native
10
10
  */
11
11
 
12
- import type {ModuleObject} from 'yargs';
12
+ import type {ServerOptions as HttpsServerOptions} from 'https';
13
+ import type {CommandModule} from 'yargs';
13
14
  import typeof Yargs from 'yargs';
14
15
 
15
16
  import {makeAsyncCommand, watchFile} from '../cli-utils';
16
17
  import {loadConfig, resolveConfig} from 'metro-config';
17
18
  import {promisify} from 'util';
18
19
 
19
- type Args = $ReadOnly<{
20
- projectRoots?: $ReadOnlyArray<string>,
20
+ type Args = Readonly<{
21
+ _: unknown,
22
+ $0: unknown,
23
+ projectRoots?: ReadonlyArray<string>,
21
24
  host: string,
22
25
  port: number,
23
26
  maxWorkers?: number,
24
27
  secure?: boolean,
25
28
  secureKey?: string,
26
29
  secureCert?: string,
27
- secureServerOptions?: string,
30
+ secureServerOptions?: HttpsServerOptions,
28
31
  hmrEnabled?: boolean,
29
32
  config?: string,
30
33
  resetCache?: boolean,
34
+ ...
31
35
  }>;
32
36
 
33
- export default (): {
34
- ...ModuleObject,
35
- handler: Function,
36
- } => ({
37
+ export default (): CommandModule => ({
37
38
  command: 'serve',
38
39
  aliases: ['start'],
39
40
  desc: 'Starts Metro on the given port, building bundles on the fly',
@@ -91,6 +92,7 @@ export default (): {
91
92
  await promisify(server.close).call(server);
92
93
  }
93
94
 
95
+ // $FlowFixMe[incompatible-type] argv has extra props.
94
96
  const config = await loadConfig(argv);
95
97
 
96
98
  // Inline require() to avoid circular dependency with ../index
@@ -98,6 +100,8 @@ export default (): {
98
100
  const MetroApi = require('../index');
99
101
 
100
102
  const {
103
+ _,
104
+ $0: _0,
101
105
  config: _config,
102
106
  hmrEnabled: _hmrEnabled,
103
107
  maxWorkers: _maxWorkers,
@@ -108,6 +112,7 @@ export default (): {
108
112
  } = argv;
109
113
  ({httpServer: server} = await MetroApi.runServer(
110
114
  config,
115
+ // $FlowFixMe[incompatible-exact] argv has extra props.
111
116
  runServerOptions,
112
117
  ));
113
118