metro-runtime 0.71.0 → 0.71.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metro-runtime",
3
- "version": "0.71.0",
3
+ "version": "0.71.3",
4
4
  "description": "🚇 Module required for evaluating Metro bundles.",
5
5
  "main": "src",
6
6
  "repository": {
@@ -19,6 +19,7 @@ var modules = clear(); // Don't use a Symbol here, it would pull in an extra pol
19
19
  // additional stuff (e.g. Array.from).
20
20
 
21
21
  const EMPTY = {};
22
+ const CYCLE_DETECTED = {};
22
23
  const { hasOwnProperty } = {};
23
24
 
24
25
  if (__DEV__) {
@@ -110,14 +111,17 @@ function metroRequire(moduleId) {
110
111
  if (initializingIndex !== -1) {
111
112
  const cycle = initializingModuleIds
112
113
  .slice(initializingIndex)
113
- .map((id) => (modules[id] ? modules[id].verboseName : "[unknown]")); // We want to show A -> B -> A:
114
+ .map((id) => (modules[id] ? modules[id].verboseName : "[unknown]"));
114
115
 
115
- cycle.push(cycle[0]);
116
- console.warn(
117
- `Require cycle: ${cycle.join(" -> ")}\n\n` +
118
- "Require cycles are allowed, but can result in uninitialized values. " +
119
- "Consider refactoring to remove the need for a cycle."
120
- );
116
+ if (shouldPrintRequireCycle(cycle)) {
117
+ cycle.push(cycle[0]); // We want to print A -> B -> A:
118
+
119
+ console.warn(
120
+ `Require cycle: ${cycle.join(" -> ")}\n\n` +
121
+ "Require cycles are allowed, but can result in uninitialized values. " +
122
+ "Consider refactoring to remove the need for a cycle."
123
+ );
124
+ }
121
125
  }
122
126
  }
123
127
 
@@ -125,6 +129,21 @@ function metroRequire(moduleId) {
125
129
  return module && module.isInitialized
126
130
  ? module.publicModule.exports
127
131
  : guardedLoadModule(moduleIdReallyIsNumber, module);
132
+ } // We print require cycles unless they match a pattern in the
133
+ // `requireCycleIgnorePatterns` configuration.
134
+
135
+ function shouldPrintRequireCycle(modules) {
136
+ const regExps =
137
+ global[__METRO_GLOBAL_PREFIX__ + "__requireCycleIgnorePatterns"];
138
+
139
+ if (!Array.isArray(regExps)) {
140
+ return true;
141
+ }
142
+
143
+ const isIgnored = (module) =>
144
+ module != null && regExps.some((regExp) => regExp.test(module)); // Print the cycle unless any part of it is ignored
145
+
146
+ return modules.every((module) => !isIgnored(module));
128
147
  }
129
148
 
130
149
  function metroImportDefault(moduleId) {
@@ -473,63 +492,76 @@ if (__DEV__) {
473
492
  // have side effects and lead to confusing and meaningless crashes.
474
493
 
475
494
  let didBailOut = false;
476
- const updatedModuleIDs = topologicalSort(
477
- [id], // Start with the changed module and go upwards
478
- (pendingID) => {
479
- const pendingModule = modules[pendingID];
480
-
481
- if (pendingModule == null) {
482
- // Nothing to do.
483
- return [];
484
- }
495
+ let updatedModuleIDs;
485
496
 
486
- const pendingHot = pendingModule.hot;
497
+ try {
498
+ updatedModuleIDs = topologicalSort(
499
+ [id], // Start with the changed module and go upwards
500
+ (pendingID) => {
501
+ const pendingModule = modules[pendingID];
502
+
503
+ if (pendingModule == null) {
504
+ // Nothing to do.
505
+ return [];
506
+ }
487
507
 
488
- if (pendingHot == null) {
489
- throw new Error(
490
- "[Refresh] Expected module.hot to always exist in DEV."
491
- );
492
- } // A module can be accepted manually from within itself.
508
+ const pendingHot = pendingModule.hot;
493
509
 
494
- let canAccept = pendingHot._didAccept;
510
+ if (pendingHot == null) {
511
+ throw new Error(
512
+ "[Refresh] Expected module.hot to always exist in DEV."
513
+ );
514
+ } // A module can be accepted manually from within itself.
495
515
 
496
- if (!canAccept && Refresh != null) {
497
- // Or React Refresh may mark it accepted based on exports.
498
- const isBoundary = isReactRefreshBoundary(
499
- Refresh,
500
- pendingModule.publicModule.exports
501
- );
516
+ let canAccept = pendingHot._didAccept;
502
517
 
503
- if (isBoundary) {
504
- canAccept = true;
505
- refreshBoundaryIDs.add(pendingID);
518
+ if (!canAccept && Refresh != null) {
519
+ // Or React Refresh may mark it accepted based on exports.
520
+ const isBoundary = isReactRefreshBoundary(
521
+ Refresh,
522
+ pendingModule.publicModule.exports
523
+ );
524
+
525
+ if (isBoundary) {
526
+ canAccept = true;
527
+ refreshBoundaryIDs.add(pendingID);
528
+ }
506
529
  }
507
- }
508
530
 
509
- if (canAccept) {
510
- // Don't look at parents.
511
- return [];
512
- } // If we bubble through the roof, there is no way to do a hot update.
513
- // Bail out altogether. This is the failure case.
514
-
515
- const parentIDs = inverseDependencies[pendingID];
516
-
517
- if (parentIDs.length === 0) {
518
- // Reload the app because the hot reload can't succeed.
519
- // This should work both on web and React Native.
520
- performFullRefresh("No root boundary", {
521
- source: mod,
522
- failed: pendingModule,
523
- });
524
- didBailOut = true;
525
- return [];
526
- } // This module can't handle the update but maybe all its parents can?
527
- // Put them all in the queue to run the same set of checks.
528
-
529
- return parentIDs;
530
- },
531
- () => didBailOut // Should we stop?
532
- ).reverse();
531
+ if (canAccept) {
532
+ // Don't look at parents.
533
+ return [];
534
+ } // If we bubble through the roof, there is no way to do a hot update.
535
+ // Bail out altogether. This is the failure case.
536
+
537
+ const parentIDs = inverseDependencies[pendingID];
538
+
539
+ if (parentIDs.length === 0) {
540
+ // Reload the app because the hot reload can't succeed.
541
+ // This should work both on web and React Native.
542
+ performFullRefresh("No root boundary", {
543
+ source: mod,
544
+ failed: pendingModule,
545
+ });
546
+ didBailOut = true;
547
+ return [];
548
+ } // This module can't handle the update but maybe all its parents can?
549
+ // Put them all in the queue to run the same set of checks.
550
+
551
+ return parentIDs;
552
+ },
553
+ () => didBailOut // Should we stop?
554
+ ).reverse();
555
+ } catch (e) {
556
+ if (e === CYCLE_DETECTED) {
557
+ performFullRefresh("Dependency cycle", {
558
+ source: mod,
559
+ });
560
+ return;
561
+ }
562
+
563
+ throw e;
564
+ }
533
565
 
534
566
  if (didBailOut) {
535
567
  return;
@@ -539,7 +571,6 @@ if (__DEV__) {
539
571
  const seenModuleIDs = new Set();
540
572
 
541
573
  for (let i = 0; i < updatedModuleIDs.length; i++) {
542
- // Don't process twice if we have a cycle.
543
574
  const updatedID = updatedModuleIDs[i];
544
575
 
545
576
  if (seenModuleIDs.has(updatedID)) {
@@ -651,29 +682,35 @@ if (__DEV__) {
651
682
  const topologicalSort = function (roots, getEdges, earlyStop) {
652
683
  const result = [];
653
684
  const visited = new Set();
685
+ const stack = new Set();
654
686
 
655
687
  function traverseDependentNodes(node) {
688
+ if (stack.has(node)) {
689
+ throw CYCLE_DETECTED;
690
+ }
691
+
692
+ if (visited.has(node)) {
693
+ return;
694
+ }
695
+
656
696
  visited.add(node);
697
+ stack.add(node);
657
698
  const dependentNodes = getEdges(node);
658
699
 
659
700
  if (earlyStop(node)) {
701
+ stack.delete(node);
660
702
  return;
661
703
  }
662
704
 
663
705
  dependentNodes.forEach((dependent) => {
664
- if (visited.has(dependent)) {
665
- return;
666
- }
667
-
668
706
  traverseDependentNodes(dependent);
669
707
  });
708
+ stack.delete(node);
670
709
  result.push(node);
671
710
  }
672
711
 
673
712
  roots.forEach((root) => {
674
- if (!visited.has(root)) {
675
- traverseDependentNodes(root);
676
- }
713
+ traverseDependentNodes(root);
677
714
  });
678
715
  return result;
679
716
  };
@@ -74,6 +74,7 @@ var modules = clear();
74
74
  // Don't use a Symbol here, it would pull in an extra polyfill with all sorts of
75
75
  // additional stuff (e.g. Array.from).
76
76
  const EMPTY = {};
77
+ const CYCLE_DETECTED = {};
77
78
  const {hasOwnProperty} = {};
78
79
 
79
80
  if (__DEV__) {
@@ -176,13 +177,15 @@ function metroRequire(moduleId: ModuleID | VerboseModuleNameForDev): Exports {
176
177
  .map((id: number) =>
177
178
  modules[id] ? modules[id].verboseName : '[unknown]',
178
179
  );
179
- // We want to show A -> B -> A:
180
- cycle.push(cycle[0]);
181
- console.warn(
182
- `Require cycle: ${cycle.join(' -> ')}\n\n` +
183
- 'Require cycles are allowed, but can result in uninitialized values. ' +
184
- 'Consider refactoring to remove the need for a cycle.',
185
- );
180
+
181
+ if (shouldPrintRequireCycle(cycle)) {
182
+ cycle.push(cycle[0]); // We want to print A -> B -> A:
183
+ console.warn(
184
+ `Require cycle: ${cycle.join(' -> ')}\n\n` +
185
+ 'Require cycles are allowed, but can result in uninitialized values. ' +
186
+ 'Consider refactoring to remove the need for a cycle.',
187
+ );
188
+ }
186
189
  }
187
190
  }
188
191
 
@@ -193,6 +196,22 @@ function metroRequire(moduleId: ModuleID | VerboseModuleNameForDev): Exports {
193
196
  : guardedLoadModule(moduleIdReallyIsNumber, module);
194
197
  }
195
198
 
199
+ // We print require cycles unless they match a pattern in the
200
+ // `requireCycleIgnorePatterns` configuration.
201
+ function shouldPrintRequireCycle(modules: $ReadOnlyArray<?string>): boolean {
202
+ const regExps =
203
+ global[__METRO_GLOBAL_PREFIX__ + '__requireCycleIgnorePatterns'];
204
+ if (!Array.isArray(regExps)) {
205
+ return true;
206
+ }
207
+
208
+ const isIgnored = (module: ?string) =>
209
+ module != null && regExps.some(regExp => regExp.test(module));
210
+
211
+ // Print the cycle unless any part of it is ignored
212
+ return modules.every(module => !isIgnored(module));
213
+ }
214
+
196
215
  function metroImportDefault(moduleId: ModuleID | VerboseModuleNameForDev) {
197
216
  if (__DEV__ && typeof moduleId === 'string') {
198
217
  const verboseName = moduleId;
@@ -542,56 +561,67 @@ if (__DEV__) {
542
561
  // have side effects and lead to confusing and meaningless crashes.
543
562
 
544
563
  let didBailOut = false;
545
- const updatedModuleIDs = topologicalSort(
546
- [id], // Start with the changed module and go upwards
547
- pendingID => {
548
- const pendingModule = modules[pendingID];
549
- if (pendingModule == null) {
550
- // Nothing to do.
551
- return [];
552
- }
553
- const pendingHot = pendingModule.hot;
554
- if (pendingHot == null) {
555
- throw new Error(
556
- '[Refresh] Expected module.hot to always exist in DEV.',
557
- );
558
- }
559
- // A module can be accepted manually from within itself.
560
- let canAccept = pendingHot._didAccept;
561
- if (!canAccept && Refresh != null) {
562
- // Or React Refresh may mark it accepted based on exports.
563
- const isBoundary = isReactRefreshBoundary(
564
- Refresh,
565
- pendingModule.publicModule.exports,
566
- );
567
- if (isBoundary) {
568
- canAccept = true;
569
- refreshBoundaryIDs.add(pendingID);
564
+ let updatedModuleIDs;
565
+ try {
566
+ updatedModuleIDs = topologicalSort(
567
+ [id], // Start with the changed module and go upwards
568
+ pendingID => {
569
+ const pendingModule = modules[pendingID];
570
+ if (pendingModule == null) {
571
+ // Nothing to do.
572
+ return [];
570
573
  }
571
- }
572
- if (canAccept) {
573
- // Don't look at parents.
574
- return [];
575
- }
576
- // If we bubble through the roof, there is no way to do a hot update.
577
- // Bail out altogether. This is the failure case.
578
- const parentIDs = inverseDependencies[pendingID];
579
- if (parentIDs.length === 0) {
580
- // Reload the app because the hot reload can't succeed.
581
- // This should work both on web and React Native.
582
- performFullRefresh('No root boundary', {
583
- source: mod,
584
- failed: pendingModule,
585
- });
586
- didBailOut = true;
587
- return [];
588
- }
589
- // This module can't handle the update but maybe all its parents can?
590
- // Put them all in the queue to run the same set of checks.
591
- return parentIDs;
592
- },
593
- () => didBailOut, // Should we stop?
594
- ).reverse();
574
+ const pendingHot = pendingModule.hot;
575
+ if (pendingHot == null) {
576
+ throw new Error(
577
+ '[Refresh] Expected module.hot to always exist in DEV.',
578
+ );
579
+ }
580
+ // A module can be accepted manually from within itself.
581
+ let canAccept = pendingHot._didAccept;
582
+ if (!canAccept && Refresh != null) {
583
+ // Or React Refresh may mark it accepted based on exports.
584
+ const isBoundary = isReactRefreshBoundary(
585
+ Refresh,
586
+ pendingModule.publicModule.exports,
587
+ );
588
+ if (isBoundary) {
589
+ canAccept = true;
590
+ refreshBoundaryIDs.add(pendingID);
591
+ }
592
+ }
593
+ if (canAccept) {
594
+ // Don't look at parents.
595
+ return [];
596
+ }
597
+ // If we bubble through the roof, there is no way to do a hot update.
598
+ // Bail out altogether. This is the failure case.
599
+ const parentIDs = inverseDependencies[pendingID];
600
+ if (parentIDs.length === 0) {
601
+ // Reload the app because the hot reload can't succeed.
602
+ // This should work both on web and React Native.
603
+ performFullRefresh('No root boundary', {
604
+ source: mod,
605
+ failed: pendingModule,
606
+ });
607
+ didBailOut = true;
608
+ return [];
609
+ }
610
+ // This module can't handle the update but maybe all its parents can?
611
+ // Put them all in the queue to run the same set of checks.
612
+ return parentIDs;
613
+ },
614
+ () => didBailOut, // Should we stop?
615
+ ).reverse();
616
+ } catch (e) {
617
+ if (e === CYCLE_DETECTED) {
618
+ performFullRefresh('Dependency cycle', {
619
+ source: mod,
620
+ });
621
+ return;
622
+ }
623
+ throw e;
624
+ }
595
625
 
596
626
  if (didBailOut) {
597
627
  return;
@@ -601,7 +631,6 @@ if (__DEV__) {
601
631
  // Run the actual factories.
602
632
  const seenModuleIDs = new Set();
603
633
  for (let i = 0; i < updatedModuleIDs.length; i++) {
604
- // Don't process twice if we have a cycle.
605
634
  const updatedID = updatedModuleIDs[i];
606
635
  if (seenModuleIDs.has(updatedID)) {
607
636
  continue;
@@ -709,24 +738,29 @@ if (__DEV__) {
709
738
  ): Array<T> {
710
739
  const result = [];
711
740
  const visited = new Set();
741
+ const stack = new Set();
712
742
  function traverseDependentNodes(node: T) {
743
+ if (stack.has(node)) {
744
+ throw CYCLE_DETECTED;
745
+ }
746
+ if (visited.has(node)) {
747
+ return;
748
+ }
713
749
  visited.add(node);
750
+ stack.add(node);
714
751
  const dependentNodes = getEdges(node);
715
752
  if (earlyStop(node)) {
753
+ stack.delete(node);
716
754
  return;
717
755
  }
718
756
  dependentNodes.forEach(dependent => {
719
- if (visited.has(dependent)) {
720
- return;
721
- }
722
757
  traverseDependentNodes(dependent);
723
758
  });
759
+ stack.delete(node);
724
760
  result.push(node);
725
761
  }
726
762
  roots.forEach(root => {
727
- if (!visited.has(root)) {
728
- traverseDependentNodes(root);
729
- }
763
+ traverseDependentNodes(root);
730
764
  });
731
765
  return result;
732
766
  };
@@ -806,7 +840,10 @@ if (__DEV__) {
806
840
 
807
841
  const performFullRefresh = (
808
842
  reason: string,
809
- modules: $ReadOnly<{source: ModuleDefinition, failed: ModuleDefinition}>,
843
+ modules: $ReadOnly<{
844
+ source?: ModuleDefinition,
845
+ failed?: ModuleDefinition,
846
+ }>,
810
847
  ) => {
811
848
  /* global window */
812
849
  if (
@@ -830,7 +867,10 @@ if (__DEV__) {
830
867
  };
831
868
 
832
869
  // Modules that only export components become React Refresh boundaries.
833
- var isReactRefreshBoundary = function (Refresh, moduleExports): boolean {
870
+ var isReactRefreshBoundary = function (
871
+ Refresh: any,
872
+ moduleExports: Exports,
873
+ ): boolean {
834
874
  if (Refresh.isLikelyComponentType(moduleExports)) {
835
875
  return true;
836
876
  }
@@ -859,9 +899,9 @@ if (__DEV__) {
859
899
  };
860
900
 
861
901
  var shouldInvalidateReactRefreshBoundary = (
862
- Refresh,
863
- prevExports,
864
- nextExports,
902
+ Refresh: any,
903
+ prevExports: Exports,
904
+ nextExports: Exports,
865
905
  ) => {
866
906
  const prevSignature = getRefreshBoundarySignature(Refresh, prevExports);
867
907
  const nextSignature = getRefreshBoundarySignature(Refresh, nextExports);
@@ -877,7 +917,10 @@ if (__DEV__) {
877
917
  };
878
918
 
879
919
  // When this signature changes, it's unsafe to stop at this refresh boundary.
880
- var getRefreshBoundarySignature = (Refresh, moduleExports): Array<mixed> => {
920
+ var getRefreshBoundarySignature = (
921
+ Refresh: any,
922
+ moduleExports: Exports,
923
+ ): Array<mixed> => {
881
924
  const signature = [];
882
925
  signature.push(Refresh.getFamilyByType(moduleExports));
883
926
  if (moduleExports == null || typeof moduleExports !== 'object') {
@@ -900,7 +943,11 @@ if (__DEV__) {
900
943
  return signature;
901
944
  };
902
945
 
903
- var registerExportsForReactRefresh = (Refresh, moduleExports, moduleID) => {
946
+ var registerExportsForReactRefresh = (
947
+ Refresh: any,
948
+ moduleExports: Exports,
949
+ moduleID: ModuleID,
950
+ ) => {
904
951
  Refresh.register(moduleExports, moduleID + ' %exports%');
905
952
  if (moduleExports == null || typeof moduleExports !== 'object') {
906
953
  // Exit if we can't iterate over exports.