metro 0.71.1 → 0.72.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 (56) hide show
  1. package/package.json +22 -22
  2. package/src/Assets.js +3 -2
  3. package/src/Assets.js.flow +3 -2
  4. package/src/Bundler.js +0 -2
  5. package/src/Bundler.js.flow +1 -1
  6. package/src/DeltaBundler/DeltaCalculator.js +2 -0
  7. package/src/DeltaBundler/DeltaCalculator.js.flow +2 -0
  8. package/src/DeltaBundler/WorkerFarm.js.flow +2 -2
  9. package/src/DeltaBundler/graphOperations.js +50 -47
  10. package/src/DeltaBundler/graphOperations.js.flow +46 -52
  11. package/src/DeltaBundler/types.flow.js +6 -0
  12. package/src/DeltaBundler/types.flow.js.flow +12 -3
  13. package/src/DeltaBundler.js +0 -2
  14. package/src/DeltaBundler.js.flow +1 -1
  15. package/src/HmrServer.js +0 -2
  16. package/src/HmrServer.js.flow +1 -2
  17. package/src/ModuleGraph/node-haste/node-haste.flow.js +0 -1
  18. package/src/ModuleGraph/node-haste/node-haste.flow.js.flow +1 -2
  19. package/src/ModuleGraph/node-haste/node-haste.js +12 -3
  20. package/src/ModuleGraph/node-haste/node-haste.js.flow +34 -7
  21. package/src/ModuleGraph/output/util.js +1 -0
  22. package/src/ModuleGraph/output/util.js.flow +3 -2
  23. package/src/ModuleGraph/silent-console.js +5 -4
  24. package/src/ModuleGraph/silent-console.js.flow +8 -4
  25. package/src/ModuleGraph/worker/collectDependencies.js +229 -33
  26. package/src/ModuleGraph/worker/collectDependencies.js.flow +250 -48
  27. package/src/Server.js +4 -0
  28. package/src/Server.js.flow +11 -2
  29. package/src/cli-utils.js.flow +1 -1
  30. package/src/commands/build.js +1 -2
  31. package/src/commands/build.js.flow +6 -9
  32. package/src/commands/dependencies.js +1 -1
  33. package/src/commands/serve.js +2 -1
  34. package/src/commands/serve.js.flow +7 -8
  35. package/src/index.flow.js +11 -8
  36. package/src/index.flow.js.flow +10 -7
  37. package/src/lib/CountingSet.js +116 -0
  38. package/src/lib/CountingSet.js.flow +126 -0
  39. package/src/lib/JsonReporter.js +0 -2
  40. package/src/lib/JsonReporter.js.flow +1 -1
  41. package/src/lib/getAppendScripts.js +10 -4
  42. package/src/lib/getAppendScripts.js.flow +6 -4
  43. package/src/lib/getPreludeCode.js +19 -1
  44. package/src/lib/getPreludeCode.js.flow +15 -0
  45. package/src/lib/getPrependedScripts.js +10 -2
  46. package/src/lib/getPrependedScripts.js.flow +11 -2
  47. package/src/lib/reporting.js +0 -2
  48. package/src/lib/reporting.js.flow +2 -1
  49. package/src/node-haste/DependencyGraph/createHasteMap.js +8 -1
  50. package/src/node-haste/DependencyGraph/createHasteMap.js.flow +9 -2
  51. package/src/node-haste/DependencyGraph.js +2 -2
  52. package/src/node-haste/DependencyGraph.js.flow +4 -2
  53. package/src/shared/output/bundle.flow.js +67 -0
  54. package/src/shared/output/bundle.flow.js.flow +89 -0
  55. package/src/shared/output/bundle.js +8 -55
  56. package/src/shared/output/bundle.js.flow +8 -75
@@ -17,6 +17,7 @@ import type {
17
17
  AsyncDependencyType,
18
18
  } from 'metro/src/DeltaBundler/types.flow.js';
19
19
 
20
+ const crypto = require('crypto');
20
21
  const generate = require('@babel/generator').default;
21
22
  const template = require('@babel/template').default;
22
23
  const traverse = require('@babel/traverse').default;
@@ -37,7 +38,23 @@ export type Dependency<TSplitCondition> = $ReadOnly<{
37
38
  name: string,
38
39
  }>;
39
40
 
41
+ // TODO: Convert to a Flow enum
42
+ type ContextMode = 'sync' | 'eager' | 'lazy' | 'lazy-once';
43
+
44
+ type ContextFilter = {pattern: string, flags: string};
45
+
46
+ export type RequireContextParams = $ReadOnly<{
47
+ /* Should search for files recursively. Optional, default `true` when `require.context` is used */
48
+ recursive: boolean,
49
+ /* Filename filter pattern for use in `require.context`. Optional, default `.*` (any file) when `require.context` is used */
50
+ filter: $ReadOnly<ContextFilter>,
51
+ /** Mode for resolving dynamic dependencies. Defaults to `sync` */
52
+ mode: ContextMode,
53
+ }>;
54
+
40
55
  type DependencyData<TSplitCondition> = $ReadOnly<{
56
+ // A locally unique key for this dependency within the current module.
57
+ key: string,
41
58
  // If null, then the dependency is synchronous.
42
59
  // (ex. `require('foo')`)
43
60
  asyncType: AsyncDependencyType | null,
@@ -45,6 +62,8 @@ type DependencyData<TSplitCondition> = $ReadOnly<{
45
62
  // If left unspecified, then the dependency is unconditionally split.
46
63
  splitCondition?: TSplitCondition,
47
64
  locs: Array<BabelSourceLocation>,
65
+ /** Context for requiring a collection of modules. */
66
+ contextParams?: RequireContextParams,
48
67
  }>;
49
68
 
50
69
  export type MutableInternalDependency<TSplitCondition> = {
@@ -66,6 +85,8 @@ export type State<TSplitCondition> = {
66
85
  dependencyMapIdentifier: ?Identifier,
67
86
  keepRequireNames: boolean,
68
87
  allowOptionalDependencies: AllowOptionalDependencies,
88
+ /** Enable `require.context` statements which can be used to import multiple files in a directory. */
89
+ unstable_allowRequireContext: boolean,
69
90
  };
70
91
 
71
92
  export type Options<TSplitCondition = void> = $ReadOnly<{
@@ -77,6 +98,8 @@ export type Options<TSplitCondition = void> = $ReadOnly<{
77
98
  allowOptionalDependencies: AllowOptionalDependencies,
78
99
  dependencyRegistry?: ModuleDependencyRegistry<TSplitCondition>,
79
100
  dependencyTransformer?: DependencyTransformer<TSplitCondition>,
101
+ /** Enable `require.context` statements which can be used to import multiple files in a directory. */
102
+ unstable_allowRequireContext: boolean,
80
103
  }>;
81
104
 
82
105
  export type CollectedDependencies<+TSplitCondition> = $ReadOnly<{
@@ -87,8 +110,8 @@ export type CollectedDependencies<+TSplitCondition> = $ReadOnly<{
87
110
 
88
111
  // Registry for the dependency of a module.
89
112
  // Defines when dependencies should be collapsed.
90
- // E.g. should a module that's once required optinally and once not
91
- // be tretaed as the smae or different dependencies.
113
+ // E.g. should a module that's once required optionally and once not
114
+ // be treated as the same or different dependencies.
92
115
  export interface ModuleDependencyRegistry<+TSplitCondition> {
93
116
  registerDependency(
94
117
  qualifier: ImportQualifier,
@@ -151,10 +174,14 @@ function collectDependencies<TSplitCondition = void>(
151
174
  dynamicRequires: options.dynamicRequires,
152
175
  keepRequireNames: options.keepRequireNames,
153
176
  allowOptionalDependencies: options.allowOptionalDependencies,
177
+ unstable_allowRequireContext: options.unstable_allowRequireContext,
154
178
  };
155
179
 
156
180
  const visitor = {
157
- CallExpression(path, state): void {
181
+ CallExpression(
182
+ path: NodePath<BabelNodeCallExpression>,
183
+ state: State<TSplitCondition>,
184
+ ): void {
158
185
  if (visited.has(path.node)) {
159
186
  return;
160
187
  }
@@ -199,6 +226,26 @@ function collectDependencies<TSplitCondition = void>(
199
226
  return;
200
227
  }
201
228
 
229
+ // Match `require.context`
230
+ if (
231
+ // Feature gate, defaults to `false`.
232
+ state.unstable_allowRequireContext &&
233
+ callee.type === 'MemberExpression' &&
234
+ // `require`
235
+ callee.object.type === 'Identifier' &&
236
+ callee.object.name === 'require' &&
237
+ // `context`
238
+ callee.property.type === 'Identifier' &&
239
+ callee.property.name === 'context' &&
240
+ !callee.computed &&
241
+ // Ensure `require` refers to the global and not something else.
242
+ !path.scope.getBinding('require')
243
+ ) {
244
+ processRequireContextCall(path, state);
245
+ visited.add(path.node);
246
+ return;
247
+ }
248
+
202
249
  if (
203
250
  name != null &&
204
251
  state.dependencyCalls.has(name) &&
@@ -213,7 +260,7 @@ function collectDependencies<TSplitCondition = void>(
213
260
  ExportNamedDeclaration: collectImports,
214
261
  ExportAllDeclaration: collectImports,
215
262
 
216
- Program(path, state) {
263
+ Program(path: NodePath<BabelNodeProgram>, state: State<TSplitCondition>) {
217
264
  state.asyncRequireModulePathStringLiteral = types.stringLiteral(
218
265
  options.asyncRequireModulePath,
219
266
  );
@@ -251,6 +298,132 @@ function collectDependencies<TSplitCondition = void>(
251
298
  };
252
299
  }
253
300
 
301
+ /** Extract args passed to the `require.context` method. */
302
+ function getRequireContextArgs(
303
+ path: NodePath<CallExpression>,
304
+ ): [string, RequireContextParams] {
305
+ const args = path.get('arguments');
306
+
307
+ let directory: string;
308
+ if (!Array.isArray(args) || args.length < 1) {
309
+ throw new InvalidRequireCallError(path);
310
+ } else {
311
+ const result = args[0].evaluate();
312
+ if (result.confident && typeof result.value === 'string') {
313
+ directory = result.value;
314
+ } else {
315
+ throw new InvalidRequireCallError(
316
+ result.deopt ?? args[0],
317
+ 'First argument of `require.context` should be a string denoting the directory to require.',
318
+ );
319
+ }
320
+ }
321
+
322
+ // Default to requiring through all directories.
323
+ let recursive: boolean = true;
324
+ if (args.length > 1) {
325
+ const result = args[1].evaluate();
326
+ if (result.confident && typeof result.value === 'boolean') {
327
+ recursive = result.value;
328
+ } else if (!(result.confident && typeof result.value === 'undefined')) {
329
+ throw new InvalidRequireCallError(
330
+ result.deopt ?? args[1],
331
+ 'Second argument of `require.context` should be an optional boolean indicating if files should be imported recursively or not.',
332
+ );
333
+ }
334
+ }
335
+
336
+ // Default to all files.
337
+ let filter: ContextFilter = {pattern: '.*', flags: ''};
338
+ if (args.length > 2) {
339
+ // evaluate() to check for undefined (because it's technically a scope lookup)
340
+ // but check the AST for the regex literal, since evaluate() doesn't do regex.
341
+ const result = args[2].evaluate();
342
+ const argNode = args[2].node;
343
+ if (argNode.type === 'RegExpLiteral') {
344
+ // TODO: Handle `new RegExp(...)` -- `argNode.type === 'NewExpression'`
345
+ filter = {
346
+ pattern: argNode.pattern,
347
+ flags: argNode.flags || '',
348
+ };
349
+ } else if (!(result.confident && typeof result.value === 'undefined')) {
350
+ throw new InvalidRequireCallError(
351
+ args[2],
352
+ `Third argument of \`require.context\` should be an optional RegExp pattern matching all of the files to import, instead found node of type: ${argNode.type}.`,
353
+ );
354
+ }
355
+ }
356
+
357
+ // Default to `sync`.
358
+ let mode: ContextMode = 'sync';
359
+ if (args.length > 3) {
360
+ const result = args[3].evaluate();
361
+ if (result.confident && typeof result.value === 'string') {
362
+ mode = getContextMode(args[3], result.value);
363
+ } else if (!(result.confident && typeof result.value === 'undefined')) {
364
+ throw new InvalidRequireCallError(
365
+ result.deopt ?? args[3],
366
+ 'Fourth argument of `require.context` should be an optional string "mode" denoting how the modules will be resolved.',
367
+ );
368
+ }
369
+ }
370
+
371
+ if (args.length > 4) {
372
+ throw new InvalidRequireCallError(
373
+ path,
374
+ `Too many arguments provided to \`require.context\` call. Expected 4, got: ${args.length}`,
375
+ );
376
+ }
377
+
378
+ return [
379
+ directory,
380
+ {
381
+ recursive,
382
+ filter,
383
+ mode,
384
+ },
385
+ ];
386
+ }
387
+
388
+ function getContextMode(path: NodePath<>, mode: string): ContextMode {
389
+ if (
390
+ mode === 'sync' ||
391
+ mode === 'eager' ||
392
+ mode === 'lazy' ||
393
+ mode === 'lazy-once'
394
+ ) {
395
+ return mode;
396
+ }
397
+ throw new InvalidRequireCallError(
398
+ path,
399
+ `require.context "${mode}" mode is not supported. Expected one of: sync, eager, lazy, lazy-once`,
400
+ );
401
+ }
402
+
403
+ function processRequireContextCall<TSplitCondition>(
404
+ path: NodePath<CallExpression>,
405
+ state: State<TSplitCondition>,
406
+ ): void {
407
+ const [directory, contextParams] = getRequireContextArgs(path);
408
+ const transformer = state.dependencyTransformer;
409
+ const dep = registerDependency(
410
+ state,
411
+ {
412
+ // We basically want to "import" every file in a folder and then filter them out with the given `filter` RegExp.
413
+ name: directory,
414
+ // Capture the matching context
415
+ contextParams,
416
+ asyncType: null,
417
+ optional: isOptionalDependency(directory, path, state),
418
+ },
419
+ path,
420
+ );
421
+
422
+ // require() the generated module representing this context
423
+ path.get('callee').replaceWith(types.identifier('require'));
424
+ transformer.transformSyncRequire(path, dep, state);
425
+ }
426
+
254
427
  function collectImports<TSplitCondition>(
255
428
  path: NodePath<>,
256
429
  state: State<TSplitCondition>,
@@ -344,6 +517,7 @@ export type ImportQualifier = $ReadOnly<{
344
517
  asyncType: AsyncDependencyType | null,
345
518
  splitCondition?: NodePath<>,
346
519
  optional: boolean,
520
+ contextParams?: RequireContextParams,
347
521
  }>;
348
522
 
349
523
  function registerDependency<TSplitCondition>(
@@ -352,7 +526,6 @@ function registerDependency<TSplitCondition>(
352
526
  path: NodePath<>,
353
527
  ): InternalDependency<TSplitCondition> {
354
528
  const dependency = state.dependencyRegistry.registerDependency(qualifier);
355
-
356
529
  const loc = getNearestLocFromPath(path);
357
530
  if (loc != null) {
358
531
  dependency.locs.push(loc);
@@ -419,14 +592,20 @@ function getModuleNameFromCallArgs(path: NodePath<CallExpression>): ?string {
419
592
 
420
593
  return null;
421
594
  }
595
+
422
596
  collectDependencies.getModuleNameFromCallArgs = getModuleNameFromCallArgs;
423
597
 
424
598
  class InvalidRequireCallError extends Error {
425
- constructor({node}: any) {
599
+ constructor({node}: NodePath<>, message?: string) {
426
600
  const line = node.loc && node.loc.start && node.loc.start.line;
427
601
 
428
602
  super(
429
- `Invalid call at line ${line || '<unknown>'}: ${generate(node).code}`,
603
+ [
604
+ `Invalid call at line ${line || '<unknown>'}: ${generate(node).code}`,
605
+ message,
606
+ ]
607
+ .filter(Boolean)
608
+ .join('\n'),
430
609
  );
431
610
  }
432
611
  }
@@ -469,9 +648,11 @@ const DefaultDependencyTransformer: DependencyTransformer<mixed> = {
469
648
  state: State<mixed>,
470
649
  ): void {
471
650
  const moduleIDExpression = createModuleIDExpression(dependency, state);
472
- path.node.arguments = state.keepRequireNames
473
- ? [moduleIDExpression, types.stringLiteral(dependency.name)]
474
- : [moduleIDExpression];
651
+ path.node.arguments = [moduleIDExpression];
652
+ // Always add the debug name argument last
653
+ if (state.keepRequireNames) {
654
+ path.node.arguments.push(types.stringLiteral(dependency.name));
655
+ }
475
656
  },
476
657
 
477
658
  transformImportCall(
@@ -546,6 +727,50 @@ function createModuleNameLiteral(dependency: InternalDependency<mixed>) {
546
727
  return types.stringLiteral(dependency.name);
547
728
  }
548
729
 
730
+ /**
731
+ * Given an import qualifier, return a key used to register the dependency.
732
+ * Generally this return the `ImportQualifier.name` property, but more
733
+ * attributes can be appended to distinguish various combinations that would
734
+ * otherwise conflict.
735
+ *
736
+ * For example, the following case would have collision issues if they all utilized the `name` property:
737
+ * ```
738
+ * require('./foo');
739
+ * require.context('./foo');
740
+ * require.context('./foo', true, /something/);
741
+ * require.context('./foo', false, /something/);
742
+ * require.context('./foo', false, /something/, 'lazy');
743
+ * ```
744
+ *
745
+ * This method should be utilized by `registerDependency`.
746
+ */
747
+ function getKeyForDependency(qualifier: ImportQualifier): string {
748
+ let key = qualifier.name;
749
+
750
+ const {asyncType} = qualifier;
751
+ if (asyncType) {
752
+ key += ['', asyncType].join('\0');
753
+ }
754
+
755
+ const {contextParams} = qualifier;
756
+ // Add extra qualifiers when using `require.context` to prevent collisions.
757
+ if (contextParams) {
758
+ // NOTE(EvanBacon): Keep this synchronized with `RequireContextParams`, if any other properties are added
759
+ // then this key algorithm should be updated to account for those properties.
760
+ // Example: `./directory__true__/foobar/m__lazy`
761
+ key += [
762
+ '',
763
+ 'context',
764
+ String(contextParams.recursive),
765
+ String(contextParams.filter.pattern),
766
+ String(contextParams.filter.flags),
767
+ contextParams.mode,
768
+ // Join together and append to the name:
769
+ ].join('\0');
770
+ }
771
+ return key;
772
+ }
773
+
549
774
  class DefaultModuleDependencyRegistry<TSplitCondition = void>
550
775
  implements ModuleDependencyRegistry<TSplitCondition>
551
776
  {
@@ -554,8 +779,9 @@ class DefaultModuleDependencyRegistry<TSplitCondition = void>
554
779
  registerDependency(
555
780
  qualifier: ImportQualifier,
556
781
  ): InternalDependency<TSplitCondition> {
782
+ const key = getKeyForDependency(qualifier);
557
783
  let dependency: ?InternalDependency<TSplitCondition> =
558
- this._dependencies.get(qualifier.name);
784
+ this._dependencies.get(key);
559
785
 
560
786
  if (dependency == null) {
561
787
  const newDependency: MutableInternalDependency<TSplitCondition> = {
@@ -563,22 +789,30 @@ class DefaultModuleDependencyRegistry<TSplitCondition = void>
563
789
  asyncType: qualifier.asyncType,
564
790
  locs: [],
565
791
  index: this._dependencies.size,
792
+ key: crypto.createHash('sha1').update(key).digest('base64'),
566
793
  };
567
794
 
568
795
  if (qualifier.optional) {
569
796
  newDependency.isOptional = true;
570
797
  }
798
+ if (qualifier.contextParams) {
799
+ newDependency.contextParams = qualifier.contextParams;
800
+ }
571
801
 
572
802
  dependency = newDependency;
573
- this._dependencies.set(qualifier.name, dependency);
574
803
  } else {
575
- const original = dependency;
576
- dependency = collapseDependencies(original, qualifier);
577
- if (original !== dependency) {
578
- this._dependencies.set(qualifier.name, dependency);
804
+ if (dependency.isOptional && !qualifier.optional) {
805
+ // A previously optionally required dependency was required non-optionally.
806
+ // Mark it non optional for the whole module
807
+ dependency = {
808
+ ...dependency,
809
+ isOptional: false,
810
+ };
579
811
  }
580
812
  }
581
813
 
814
+ this._dependencies.set(key, dependency);
815
+
582
816
  return dependency;
583
817
  }
584
818
 
@@ -587,36 +821,4 @@ class DefaultModuleDependencyRegistry<TSplitCondition = void>
587
821
  }
588
822
  }
589
823
 
590
- function collapseDependencies<TSplitCondition>(
591
- dependency: InternalDependency<TSplitCondition>,
592
- qualifier: ImportQualifier,
593
- ): InternalDependency<TSplitCondition> {
594
- let collapsed = dependency;
595
-
596
- // A previously optionally required dependency was required non-optionaly.
597
- // Mark it non optional for the whole module
598
- if (collapsed.isOptional && !qualifier.optional) {
599
- collapsed = {
600
- ...dependency,
601
- isOptional: false,
602
- };
603
- }
604
-
605
- // A previously asynchronously (or prefetch) required module was required synchronously.
606
- // Make the dependency sync.
607
- if (collapsed.asyncType != null && qualifier.asyncType == null) {
608
- collapsed = {...dependency, asyncType: null};
609
- }
610
-
611
- // A prefetched dependency was required async in the module. Mark it as async.
612
- if (collapsed.asyncType === 'prefetch' && qualifier.asyncType === 'async') {
613
- collapsed = {
614
- ...dependency,
615
- asyncType: 'async',
616
- };
617
- }
618
-
619
- return collapsed;
620
- }
621
-
622
824
  module.exports = collectDependencies;
package/src/Server.js CHANGED
@@ -453,6 +453,8 @@ class Server {
453
453
  delete: deleteFn,
454
454
  finish,
455
455
  }) {
456
+ /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by
457
+ * Flow's LTI update could not be added via codemod */
456
458
  return async function requestProcessor(req, res, bundleOptions) {
457
459
  const { entryFile, graphOptions, transformOptions, serializerOptions } =
458
460
  splitBundleOptions(bundleOptions);
@@ -660,6 +662,8 @@ class Server {
660
662
 
661
663
  const serializer =
662
664
  this._config.serializer.customSerializer ||
665
+ /* $FlowFixMe[missing-local-annot] The type annotation(s) required by
666
+ * Flow's LTI update could not be added via codemod */
663
667
  ((...args) => bundleToString(baseJSBundle(...args)).code);
664
668
 
665
669
  const bundle = await serializer(
@@ -9,6 +9,8 @@
9
9
  */
10
10
 
11
11
  'use strict';
12
+
13
+ import type {StackFrameOutput} from './Server/symbolicate';
12
14
  import type {AssetData} from './Assets';
13
15
  import type {ExplodedSourceMap} from './DeltaBundler/Serializers/getExplodedSourceMap';
14
16
  import type {RamBundleInfo} from './DeltaBundler/Serializers/getRamBundleInfo';
@@ -513,6 +515,8 @@ class Server {
513
515
  +delete?: (context: ProcessDeleteContext) => Promise<void>,
514
516
  +finish: (context: ProcessEndContext<T>) => void,
515
517
  }) {
518
+ /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by
519
+ * Flow's LTI update could not be added via codemod */
516
520
  return async function requestProcessor(
517
521
  req: IncomingMessage,
518
522
  res: ServerResponse,
@@ -733,6 +737,8 @@ class Server {
733
737
 
734
738
  const serializer =
735
739
  this._config.serializer.customSerializer ||
740
+ /* $FlowFixMe[missing-local-annot] The type annotation(s) required by
741
+ * Flow's LTI update could not be added via codemod */
736
742
  ((...args) => bundleToString(baseJSBundle(...args)).code);
737
743
 
738
744
  const bundle = await serializer(
@@ -1011,7 +1017,10 @@ class Server {
1011
1017
  });
1012
1018
 
1013
1019
  async _symbolicate(req: IncomingMessage, res: ServerResponse) {
1014
- const getCodeFrame = (urls, symbolicatedStack) => {
1020
+ const getCodeFrame = (
1021
+ urls: Set<string>,
1022
+ symbolicatedStack: $ReadOnlyArray<StackFrameOutput>,
1023
+ ) => {
1015
1024
  for (let i = 0; i < symbolicatedStack.length; i++) {
1016
1025
  const {collapse, column, file, lineNumber} = symbolicatedStack[i];
1017
1026
  const fileAbsolute = path.resolve(this._config.projectRoot, file ?? '');
@@ -1161,7 +1170,7 @@ class Server {
1161
1170
  }
1162
1171
 
1163
1172
  async _resolveRelativePath(
1164
- filePath,
1173
+ filePath: string,
1165
1174
  {
1166
1175
  transformOptions,
1167
1176
  relativeTo,
@@ -27,7 +27,7 @@ exports.watchFile = async function (
27
27
 
28
28
  exports.makeAsyncCommand =
29
29
  (
30
- command: (argv: YargArguments) => Promise<mixed>,
30
+ command: (argv: YargArguments) => Promise<void>,
31
31
  ): ((argv: YargArguments) => void) =>
32
32
  (argv: YargArguments) => {
33
33
  Promise.resolve(command(argv)).catch(error => {
@@ -22,8 +22,7 @@ const updateReporter = new TerminalReporter(term);
22
22
 
23
23
  module.exports = () => ({
24
24
  command: "build <entry>",
25
- description:
26
- "Generates a JavaScript bundle containing the specified entrypoint and its descendants",
25
+ desc: "Generates a JavaScript bundle containing the specified entrypoint and its descendants",
27
26
  builder: (yargs) => {
28
27
  yargs.option("project-roots", {
29
28
  alias: "P",
@@ -13,6 +13,7 @@
13
13
  import type {RunBuildOptions} from '../index';
14
14
  import type {YargArguments} from 'metro-config/src/configTypes.flow';
15
15
  import typeof Yargs from 'yargs';
16
+ import type {ModuleObject} from 'yargs';
16
17
 
17
18
  const {makeAsyncCommand} = require('../cli-utils');
18
19
  const TerminalReporter = require('../lib/TerminalReporter');
@@ -22,16 +23,12 @@ const {Terminal} = require('metro-core');
22
23
  const term = new Terminal(process.stdout);
23
24
  const updateReporter = new TerminalReporter(term);
24
25
 
25
- module.exports = (): ({
26
- builder: (yargs: Yargs) => void,
27
- command: string,
28
- description: string,
29
- handler: (argv: YargArguments) => void,
30
- }) => ({
26
+ module.exports = (): {
27
+ ...ModuleObject,
28
+ handler: Function,
29
+ } => ({
31
30
  command: 'build <entry>',
32
-
33
- description:
34
- 'Generates a JavaScript bundle containing the specified entrypoint and its descendants',
31
+ desc: 'Generates a JavaScript bundle containing the specified entrypoint and its descendants',
35
32
 
36
33
  builder: (yargs: Yargs): void => {
37
34
  yargs.option('project-roots', {
@@ -73,7 +73,7 @@ async function dependencies(args, config) {
73
73
 
74
74
  module.exports = () => ({
75
75
  command: "get-dependencies",
76
- description: "List dependencies",
76
+ desc: "List dependencies",
77
77
  builder: (yargs) => {
78
78
  yargs.option("entry-file", {
79
79
  type: "string",
@@ -17,7 +17,8 @@ const { promisify } = require("util");
17
17
 
18
18
  module.exports = () => ({
19
19
  command: "serve",
20
- description: "Starts Metro on the given port, building bundles on the fly",
20
+ aliases: ["start"],
21
+ desc: "Starts Metro on the given port, building bundles on the fly",
21
22
  builder: (yargs) => {
22
23
  yargs.option("project-roots", {
23
24
  alias: "P",
@@ -13,20 +13,19 @@
13
13
  import type {RunServerOptions} from '../index';
14
14
  import type {YargArguments} from 'metro-config/src/configTypes.flow';
15
15
  import typeof Yargs from 'yargs';
16
+ import type {ModuleObject} from 'yargs';
16
17
 
17
18
  const {makeAsyncCommand, watchFile} = require('../cli-utils');
18
19
  const {loadConfig, resolveConfig} = require('metro-config');
19
20
  const {promisify} = require('util');
20
21
 
21
- module.exports = (): ({
22
- builder: (yargs: Yargs) => void,
23
- command: $TEMPORARY$string<'serve'>,
24
- description: string,
25
- handler: (argv: YargArguments) => void,
26
- }) => ({
22
+ module.exports = (): {
23
+ ...ModuleObject,
24
+ handler: Function,
25
+ } => ({
27
26
  command: 'serve',
28
-
29
- description: 'Starts Metro on the given port, building bundles on the fly',
27
+ aliases: ['start'],
28
+ desc: 'Starts Metro on the given port, building bundles on the fly',
30
29
 
31
30
  builder: (yargs: Yargs): void => {
32
31
  yargs.option('project-roots', {
package/src/index.flow.js CHANGED
@@ -355,22 +355,25 @@ exports.buildGraph = async function (
355
355
 
356
356
  exports.attachMetroCli = function (
357
357
  yargs,
358
- { build = {}, serve = {}, dependencies = {} } = {}
358
+ // $FlowFixMe[prop-missing]
359
+ {
360
+ build = {},
361
+ serve = {},
362
+ dependencies = {}
363
+ } = {} // prettier-ignore
359
364
  ) {
365
+ yargs.strict();
366
+
360
367
  if (build) {
361
- const { command, description, builder, handler } = makeBuildCommand();
362
- yargs.command(command, description, builder, handler);
368
+ yargs.command(makeBuildCommand());
363
369
  }
364
370
 
365
371
  if (serve) {
366
- const { command, description, builder, handler } = makeServeCommand();
367
- yargs.command(command, description, builder, handler);
372
+ yargs.command(makeServeCommand());
368
373
  }
369
374
 
370
375
  if (dependencies) {
371
- const { command, description, builder, handler } =
372
- makeDependenciesCommand();
373
- yargs.command(command, description, builder, handler);
376
+ yargs.command(makeDependenciesCommand());
374
377
  }
375
378
 
376
379
  return yargs;
@@ -448,20 +448,23 @@ exports.attachMetroCli = function (
448
448
  serve: ServeCommandOptions,
449
449
  dependencies: any,
450
450
  ...
451
- } = {},
451
+ }
452
+ // prettier-ignore
453
+ // $FlowFixMe[prop-missing]
454
+ = {},
452
455
  ): Yargs {
456
+ yargs.strict();
457
+
453
458
  if (build) {
454
- const {command, description, builder, handler} = makeBuildCommand();
455
- yargs.command(command, description, builder, handler);
459
+ yargs.command(makeBuildCommand());
456
460
  }
457
461
  if (serve) {
458
- const {command, description, builder, handler} = makeServeCommand();
459
- yargs.command(command, description, builder, handler);
462
+ yargs.command(makeServeCommand());
460
463
  }
461
464
  if (dependencies) {
462
- const {command, description, builder, handler} = makeDependenciesCommand();
463
- yargs.command(command, description, builder, handler);
465
+ yargs.command(makeDependenciesCommand());
464
466
  }
467
+
465
468
  return yargs;
466
469
  };
467
470