metro 0.71.3 → 0.72.2

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 (108) hide show
  1. package/package.json +23 -21
  2. package/src/Assets.js +3 -2
  3. package/src/Assets.js.flow +3 -2
  4. package/src/Bundler.js +11 -2
  5. package/src/Bundler.js.flow +7 -1
  6. package/src/DeltaBundler/DeltaCalculator.js +83 -21
  7. package/src/DeltaBundler/DeltaCalculator.js.flow +61 -8
  8. package/src/DeltaBundler/Serializers/baseBytecodeBundle.js +3 -2
  9. package/src/DeltaBundler/Serializers/baseBytecodeBundle.js.flow +2 -1
  10. package/src/DeltaBundler/Serializers/helpers/bytecode.js +2 -2
  11. package/src/DeltaBundler/Serializers/helpers/bytecode.js.flow +2 -1
  12. package/src/DeltaBundler/Serializers/hmrJSBundle.js.flow +1 -3
  13. package/src/DeltaBundler/Transformer.js +27 -4
  14. package/src/DeltaBundler/Transformer.js.flow +18 -2
  15. package/src/DeltaBundler/Worker.flow.js +45 -1
  16. package/src/DeltaBundler/Worker.flow.js.flow +42 -1
  17. package/src/DeltaBundler/WorkerFarm.js +3 -2
  18. package/src/DeltaBundler/WorkerFarm.js.flow +3 -1
  19. package/src/DeltaBundler/graphOperations.js +170 -63
  20. package/src/DeltaBundler/graphOperations.js.flow +144 -64
  21. package/src/DeltaBundler/types.flow.js.flow +11 -5
  22. package/src/DeltaBundler.js.flow +1 -1
  23. package/src/HmrServer.js +11 -5
  24. package/src/HmrServer.js.flow +11 -4
  25. package/src/IncrementalBundler.js +22 -4
  26. package/src/IncrementalBundler.js.flow +23 -4
  27. package/src/ModuleGraph/node-haste/HasteFS.js.flow +1 -1
  28. package/src/ModuleGraph/node-haste/Package.js.flow +5 -5
  29. package/src/ModuleGraph/node-haste/node-haste.js +19 -8
  30. package/src/ModuleGraph/node-haste/node-haste.js.flow +43 -16
  31. package/src/ModuleGraph/output/indexed-ram-bundle.js.flow +5 -13
  32. package/src/ModuleGraph/output/multiple-files-ram-bundle.js.flow +4 -14
  33. package/src/ModuleGraph/output/util.js +3 -4
  34. package/src/ModuleGraph/output/util.js.flow +3 -3
  35. package/src/ModuleGraph/types.flow.js.flow +28 -5
  36. package/src/ModuleGraph/worker/collectDependencies.js +19 -30
  37. package/src/ModuleGraph/worker/collectDependencies.js.flow +23 -41
  38. package/src/Server.js +90 -32
  39. package/src/Server.js.flow +143 -44
  40. package/src/cli-utils.js.flow +1 -1
  41. package/src/commands/build.js +1 -2
  42. package/src/commands/build.js.flow +6 -9
  43. package/src/commands/dependencies.js +1 -1
  44. package/src/commands/serve.js +2 -1
  45. package/src/commands/serve.js.flow +7 -8
  46. package/src/index.flow.js +27 -16
  47. package/src/index.flow.js.flow +24 -15
  48. package/src/integration_tests/basic_bundle/require-context/conflict.js +25 -0
  49. package/src/integration_tests/basic_bundle/require-context/conflict.js.flow +27 -0
  50. package/src/integration_tests/basic_bundle/require-context/empty.js +29 -0
  51. package/src/integration_tests/basic_bundle/require-context/empty.js.flow +26 -0
  52. package/src/integration_tests/basic_bundle/require-context/matching.js +26 -0
  53. package/src/integration_tests/basic_bundle/require-context/matching.js.flow +27 -0
  54. package/src/integration_tests/basic_bundle/require-context/mode-eager.js +22 -0
  55. package/src/integration_tests/basic_bundle/require-context/mode-eager.js.flow +24 -0
  56. package/src/integration_tests/basic_bundle/require-context/mode-lazy-once.js +22 -0
  57. package/src/integration_tests/basic_bundle/require-context/mode-lazy-once.js.flow +24 -0
  58. package/src/integration_tests/basic_bundle/require-context/mode-lazy.js +22 -0
  59. package/src/integration_tests/basic_bundle/require-context/mode-lazy.js.flow +24 -0
  60. package/src/integration_tests/basic_bundle/require-context/mode-sync.js +20 -0
  61. package/src/integration_tests/basic_bundle/require-context/mode-sync.js.flow +22 -0
  62. package/src/integration_tests/basic_bundle/require-context/subdir/a.js +12 -0
  63. package/src/integration_tests/basic_bundle/require-context/subdir/a.js.flow +11 -0
  64. package/src/integration_tests/basic_bundle/require-context/subdir/b.js +18 -0
  65. package/src/integration_tests/basic_bundle/require-context/subdir/b.js.flow +11 -0
  66. package/src/integration_tests/basic_bundle/require-context/subdir/c.js +12 -0
  67. package/src/integration_tests/basic_bundle/require-context/subdir/c.js.flow +11 -0
  68. package/src/integration_tests/basic_bundle/require-context/subdir/nested/d.js +12 -0
  69. package/src/integration_tests/basic_bundle/require-context/subdir/nested/d.js.flow +11 -0
  70. package/src/integration_tests/basic_bundle/require-context/subdir-conflict/index.js +12 -0
  71. package/src/integration_tests/basic_bundle/require-context/subdir-conflict/index.js.flow +11 -0
  72. package/src/integration_tests/basic_bundle/require-context/utils.js +29 -0
  73. package/src/integration_tests/basic_bundle/require-context/utils.js.flow +44 -0
  74. package/src/lib/CountingSet.js +1 -0
  75. package/src/lib/CountingSet.js.flow +1 -0
  76. package/src/lib/RamBundleParser.js +1 -0
  77. package/src/lib/RamBundleParser.js.flow +1 -0
  78. package/src/lib/bundleToBytecode.js +3 -2
  79. package/src/lib/bundleToBytecode.js.flow +2 -2
  80. package/src/lib/contextModule.js +80 -0
  81. package/src/lib/contextModule.js.flow +86 -0
  82. package/src/lib/contextModuleTemplates.js +186 -0
  83. package/src/lib/contextModuleTemplates.js.flow +148 -0
  84. package/src/lib/getGraphId.js +17 -3
  85. package/src/lib/getGraphId.js.flow +12 -9
  86. package/src/lib/getPrependedScripts.js +15 -5
  87. package/src/lib/getPrependedScripts.js.flow +8 -1
  88. package/src/lib/parseCustomResolverOptions.js +26 -0
  89. package/src/lib/parseCustomResolverOptions.js.flow +38 -0
  90. package/src/lib/parseOptionsFromUrl.js +3 -0
  91. package/src/lib/parseOptionsFromUrl.js.flow +9 -18
  92. package/src/lib/splitBundleOptions.js +3 -0
  93. package/src/lib/splitBundleOptions.js.flow +3 -0
  94. package/src/lib/transformHelpers.js +68 -22
  95. package/src/lib/transformHelpers.js.flow +73 -16
  96. package/src/node-haste/DependencyGraph/ModuleResolution.js +19 -2
  97. package/src/node-haste/DependencyGraph/ModuleResolution.js.flow +8 -2
  98. package/src/node-haste/DependencyGraph/createHasteMap.js +7 -1
  99. package/src/node-haste/DependencyGraph/createHasteMap.js.flow +7 -1
  100. package/src/node-haste/DependencyGraph.js +39 -9
  101. package/src/node-haste/DependencyGraph.js.flow +63 -12
  102. package/src/node-haste/Module.js +1 -0
  103. package/src/node-haste/Module.js.flow +1 -0
  104. package/src/shared/output/bundle.flow.js +67 -0
  105. package/src/shared/output/bundle.flow.js.flow +89 -0
  106. package/src/shared/output/bundle.js +8 -55
  107. package/src/shared/output/bundle.js.flow +8 -75
  108. package/src/shared/types.flow.js.flow +7 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metro",
3
- "version": "0.71.3",
3
+ "version": "0.72.2",
4
4
  "description": "🚇 The JavaScript bundler for React Native.",
5
5
  "main": "src/index.js",
6
6
  "bin": "src/cli.js",
@@ -36,22 +36,22 @@
36
36
  "invariant": "^2.2.4",
37
37
  "jest-worker": "^27.2.0",
38
38
  "lodash.throttle": "^4.1.1",
39
- "metro-babel-transformer": "0.71.3",
40
- "metro-cache": "0.71.3",
41
- "metro-cache-key": "0.71.3",
42
- "metro-config": "0.71.3",
43
- "metro-core": "0.71.3",
44
- "metro-file-map": "0.71.3",
45
- "metro-hermes-compiler": "0.71.3",
46
- "metro-inspector-proxy": "0.71.3",
47
- "metro-minify-uglify": "0.71.3",
48
- "metro-react-native-babel-preset": "0.71.3",
49
- "metro-resolver": "0.71.3",
50
- "metro-runtime": "0.71.3",
51
- "metro-source-map": "0.71.3",
52
- "metro-symbolicate": "0.71.3",
53
- "metro-transform-plugins": "0.71.3",
54
- "metro-transform-worker": "0.71.3",
39
+ "metro-babel-transformer": "0.72.2",
40
+ "metro-cache": "0.72.2",
41
+ "metro-cache-key": "0.72.2",
42
+ "metro-config": "0.72.2",
43
+ "metro-core": "0.72.2",
44
+ "metro-file-map": "0.72.2",
45
+ "metro-hermes-compiler": "0.72.2",
46
+ "metro-inspector-proxy": "0.72.2",
47
+ "metro-minify-uglify": "0.72.2",
48
+ "metro-react-native-babel-preset": "0.72.2",
49
+ "metro-resolver": "0.72.2",
50
+ "metro-runtime": "0.72.2",
51
+ "metro-source-map": "0.72.2",
52
+ "metro-symbolicate": "0.72.2",
53
+ "metro-transform-plugins": "0.72.2",
54
+ "metro-transform-worker": "0.72.2",
55
55
  "mime-types": "^2.1.27",
56
56
  "node-fetch": "^2.2.0",
57
57
  "nullthrows": "^1.1.1",
@@ -70,10 +70,12 @@
70
70
  "babel-jest": "^26.6.3",
71
71
  "dedent": "^0.7.0",
72
72
  "jest-snapshot": "^26.5.2",
73
- "metro-babel-register": "0.71.3",
74
- "metro-memory-fs": "0.71.3",
75
- "metro-react-native-babel-preset": "0.71.3",
76
- "metro-react-native-babel-transformer": "0.71.3",
73
+ "metro-babel-register": "0.72.2",
74
+ "metro-memory-fs": "0.72.2",
75
+ "metro-react-native-babel-preset": "0.72.2",
76
+ "metro-react-native-babel-transformer": "0.72.2",
77
+ "mock-req": "^0.2.0",
78
+ "mock-res": "^0.6.0",
77
79
  "stack-trace": "^0.0.10"
78
80
  },
79
81
  "license": "MIT"
package/src/Assets.js CHANGED
@@ -108,8 +108,9 @@ async function getAbsoluteAssetRecord(assetPath, platform = null) {
108
108
 
109
109
  if (!record) {
110
110
  throw new Error(
111
- /* $FlowFixMe: platform can be null */
112
- `Asset not found: ${assetPath} for platform: ${platform}`
111
+ `Asset not found: ${assetPath} for platform: ${
112
+ platform !== null && platform !== void 0 ? platform : "(unspecified)"
113
+ }`
113
114
  );
114
115
  }
115
116
 
@@ -154,8 +154,9 @@ async function getAbsoluteAssetRecord(
154
154
 
155
155
  if (!record) {
156
156
  throw new Error(
157
- /* $FlowFixMe: platform can be null */
158
- `Asset not found: ${assetPath} for platform: ${platform}`,
157
+ `Asset not found: ${assetPath} for platform: ${
158
+ platform ?? '(unspecified)'
159
+ }`,
159
160
  );
160
161
  }
161
162
 
package/src/Bundler.js CHANGED
@@ -55,11 +55,20 @@ class Bundler {
55
55
  return this._depGraph;
56
56
  }
57
57
 
58
- async transformFile(filePath, transformOptions) {
58
+ async transformFile(
59
+ filePath,
60
+ transformOptions,
61
+ /** Optionally provide the file contents, this can be used to provide virtual contents for a file. */
62
+ fileBuffer
63
+ ) {
59
64
  // We need to be sure that the DependencyGraph has been initialized.
60
65
  // TODO: Remove this ugly hack!
61
66
  await this._depGraph.ready();
62
- return this._transformer.transformFile(filePath, transformOptions);
67
+ return this._transformer.transformFile(
68
+ filePath,
69
+ transformOptions,
70
+ fileBuffer
71
+ );
63
72
  } // Waits for the bundler to become ready.
64
73
 
65
74
  async ready() {
@@ -68,12 +68,18 @@ class Bundler {
68
68
  async transformFile(
69
69
  filePath: string,
70
70
  transformOptions: TransformOptions,
71
+ /** Optionally provide the file contents, this can be used to provide virtual contents for a file. */
72
+ fileBuffer?: Buffer,
71
73
  ): Promise<TransformResultWithSource<>> {
72
74
  // We need to be sure that the DependencyGraph has been initialized.
73
75
  // TODO: Remove this ugly hack!
74
76
  await this._depGraph.ready();
75
77
 
76
- return this._transformer.transformFile(filePath, transformOptions);
78
+ return this._transformer.transformFile(
79
+ filePath,
80
+ transformOptions,
81
+ fileBuffer,
82
+ );
77
83
  }
78
84
 
79
85
  // Waits for the bundler to become ready.
@@ -9,12 +9,7 @@
9
9
  */
10
10
  "use strict";
11
11
 
12
- const {
13
- createGraph,
14
- initialTraverseDependencies,
15
- reorderGraph,
16
- traverseDependencies,
17
- } = require("./graphOperations");
12
+ var _graphOperations = require("./graphOperations");
18
13
 
19
14
  const { EventEmitter } = require("events");
20
15
  /**
@@ -27,12 +22,13 @@ const { EventEmitter } = require("events");
27
22
  class DeltaCalculator extends EventEmitter {
28
23
  _deletedFiles = new Set();
29
24
  _modifiedFiles = new Set();
25
+ _addedFiles = new Set();
30
26
 
31
27
  constructor(entryPoints, changeEventSource, options) {
32
28
  super();
33
29
  this._options = options;
34
30
  this._changeEventSource = changeEventSource;
35
- this._graph = createGraph({
31
+ this._graph = (0, _graphOperations.createGraph)({
36
32
  entryPoints,
37
33
  transformOptions: this._options.transformOptions,
38
34
  });
@@ -51,12 +47,13 @@ class DeltaCalculator extends EventEmitter {
51
47
 
52
48
  this.removeAllListeners(); // Clean up all the cache data structures to deallocate memory.
53
49
 
54
- this._graph = createGraph({
50
+ this._graph = (0, _graphOperations.createGraph)({
55
51
  entryPoints: this._graph.entryPoints,
56
52
  transformOptions: this._options.transformOptions,
57
53
  });
58
54
  this._modifiedFiles = new Set();
59
55
  this._deletedFiles = new Set();
56
+ this._addedFiles = new Set();
60
57
  }
61
58
  /**
62
59
  * Main method to calculate the delta of modules. It returns a DeltaResult,
@@ -75,13 +72,16 @@ class DeltaCalculator extends EventEmitter {
75
72
  const modifiedFiles = this._modifiedFiles;
76
73
  this._modifiedFiles = new Set();
77
74
  const deletedFiles = this._deletedFiles;
78
- this._deletedFiles = new Set(); // Concurrent requests should reuse the same bundling process. To do so,
75
+ this._deletedFiles = new Set();
76
+ const addedFiles = this._addedFiles;
77
+ this._addedFiles = new Set(); // Concurrent requests should reuse the same bundling process. To do so,
79
78
  // this method stores the promise as an instance variable, and then it's
80
79
  // removed after it gets resolved.
81
80
 
82
81
  this._currentBuildPromise = this._getChangedDependencies(
83
82
  modifiedFiles,
84
- deletedFiles
83
+ deletedFiles,
84
+ addedFiles
85
85
  );
86
86
  let result;
87
87
  const numDependencies = this._graph.dependencies.size;
@@ -94,7 +94,8 @@ class DeltaCalculator extends EventEmitter {
94
94
  // do so, asking for a delta after an error will produce an empty Delta,
95
95
  // which is not correct.
96
96
  modifiedFiles.forEach((file) => this._modifiedFiles.add(file));
97
- deletedFiles.forEach((file) => this._deletedFiles.add(file)); // If after an error the number of modules has changed, we could be in
97
+ deletedFiles.forEach((file) => this._deletedFiles.add(file));
98
+ addedFiles.forEach((file) => this._addedFiles.add(file)); // If after an error the number of modules has changed, we could be in
98
99
  // a weird state. As a safe net we clean the dependency modules to force
99
100
  // a clean traversal of the graph next time.
100
101
 
@@ -108,7 +109,7 @@ class DeltaCalculator extends EventEmitter {
108
109
  } // Return all the modules if the client requested a reset delta.
109
110
 
110
111
  if (reset) {
111
- reorderGraph(this._graph, {
112
+ (0, _graphOperations.reorderGraph)(this._graph, {
112
113
  shallow,
113
114
  });
114
115
  return {
@@ -143,23 +144,68 @@ class DeltaCalculator extends EventEmitter {
143
144
  */
144
145
 
145
146
  _handleFileChange = ({ type, filePath }) => {
146
- if (type === "delete") {
147
- this._deletedFiles.add(filePath);
147
+ let state;
148
+
149
+ if (this._deletedFiles.has(filePath)) {
150
+ state = "deleted";
151
+ } else if (this._modifiedFiles.has(filePath)) {
152
+ state = "modified";
153
+ } else if (this._addedFiles.has(filePath)) {
154
+ state = "added";
155
+ }
156
+
157
+ let nextState;
148
158
 
149
- this._modifiedFiles.delete(filePath);
159
+ if (type === "delete") {
160
+ nextState = "deleted";
161
+ } else if (type === "add") {
162
+ // A deleted+added file is modified
163
+ nextState = state === "deleted" ? "modified" : "added";
150
164
  } else {
151
- this._deletedFiles.delete(filePath);
165
+ // type === 'change'
166
+ // An added+modified file is added
167
+ nextState = state === "added" ? "added" : "modified";
168
+ }
169
+
170
+ switch (nextState) {
171
+ case "deleted":
172
+ this._deletedFiles.add(filePath);
173
+
174
+ this._modifiedFiles.delete(filePath);
175
+
176
+ this._addedFiles.delete(filePath);
177
+
178
+ break;
152
179
 
153
- this._modifiedFiles.add(filePath);
180
+ case "added":
181
+ this._addedFiles.add(filePath);
182
+
183
+ this._deletedFiles.delete(filePath);
184
+
185
+ this._modifiedFiles.delete(filePath);
186
+
187
+ break;
188
+
189
+ case "modified":
190
+ this._modifiedFiles.add(filePath);
191
+
192
+ this._deletedFiles.delete(filePath);
193
+
194
+ this._addedFiles.delete(filePath);
195
+
196
+ break;
197
+
198
+ default:
199
+ nextState;
154
200
  } // Notify users that there is a change in some of the bundle files. This
155
201
  // way the client can choose to refetch the bundle.
156
202
 
157
203
  this.emit("change");
158
204
  };
159
205
 
160
- async _getChangedDependencies(modifiedFiles, deletedFiles) {
206
+ async _getChangedDependencies(modifiedFiles, deletedFiles, addedFiles) {
161
207
  if (!this._graph.dependencies.size) {
162
- const { added } = await initialTraverseDependencies(
208
+ const { added } = await (0, _graphOperations.initialTraverseDependencies)(
163
209
  this._graph,
164
210
  this._options
165
211
  );
@@ -184,7 +230,22 @@ class DeltaCalculator extends EventEmitter {
184
230
  }
185
231
  });
186
232
  }
187
- }); // We only want to process files that are in the bundle.
233
+ }); // NOTE(EvanBacon): This check adds extra complexity so we feature gate it
234
+ // to enable users to opt out.
235
+
236
+ if (this._options.unstable_allowRequireContext) {
237
+ // Check if any added or removed files are matched in a context module.
238
+ // We only need to do this for added files because (1) deleted files will have a context
239
+ // module as an inverse dependency, (2) modified files don't invalidate the contents
240
+ // of the context module.
241
+ addedFiles.forEach((filePath) => {
242
+ (0, _graphOperations.markModifiedContextModules)(
243
+ this._graph,
244
+ filePath,
245
+ modifiedFiles
246
+ );
247
+ });
248
+ } // We only want to process files that are in the bundle.
188
249
 
189
250
  const modifiedDependencies = Array.from(modifiedFiles).filter((filePath) =>
190
251
  this._graph.dependencies.has(filePath)
@@ -199,7 +260,8 @@ class DeltaCalculator extends EventEmitter {
199
260
  };
200
261
  }
201
262
 
202
- const { added, modified, deleted } = await traverseDependencies(
263
+ const { added, modified, deleted } = await (0,
264
+ _graphOperations.traverseDependencies)(
203
265
  modifiedDependencies,
204
266
  this._graph,
205
267
  this._options
@@ -10,14 +10,15 @@
10
10
 
11
11
  'use strict';
12
12
 
13
- import type {DeltaResult, Graph, Options} from './types.flow';
14
-
15
- const {
13
+ import {
16
14
  createGraph,
17
15
  initialTraverseDependencies,
16
+ markModifiedContextModules,
18
17
  reorderGraph,
19
18
  traverseDependencies,
20
- } = require('./graphOperations');
19
+ } from './graphOperations';
20
+ import type {DeltaResult, Graph, Options} from './types.flow';
21
+
21
22
  const {EventEmitter} = require('events');
22
23
 
23
24
  /**
@@ -33,6 +34,7 @@ class DeltaCalculator<T> extends EventEmitter {
33
34
  _currentBuildPromise: ?Promise<DeltaResult<T>>;
34
35
  _deletedFiles: Set<string> = new Set();
35
36
  _modifiedFiles: Set<string> = new Set();
37
+ _addedFiles: Set<string> = new Set();
36
38
 
37
39
  _graph: Graph<T>;
38
40
 
@@ -72,6 +74,7 @@ class DeltaCalculator<T> extends EventEmitter {
72
74
  });
73
75
  this._modifiedFiles = new Set();
74
76
  this._deletedFiles = new Set();
77
+ this._addedFiles = new Set();
75
78
  }
76
79
 
77
80
  /**
@@ -99,6 +102,8 @@ class DeltaCalculator<T> extends EventEmitter {
99
102
  this._modifiedFiles = new Set();
100
103
  const deletedFiles = this._deletedFiles;
101
104
  this._deletedFiles = new Set();
105
+ const addedFiles = this._addedFiles;
106
+ this._addedFiles = new Set();
102
107
 
103
108
  // Concurrent requests should reuse the same bundling process. To do so,
104
109
  // this method stores the promise as an instance variable, and then it's
@@ -106,6 +111,7 @@ class DeltaCalculator<T> extends EventEmitter {
106
111
  this._currentBuildPromise = this._getChangedDependencies(
107
112
  modifiedFiles,
108
113
  deletedFiles,
114
+ addedFiles,
109
115
  );
110
116
 
111
117
  let result;
@@ -121,6 +127,7 @@ class DeltaCalculator<T> extends EventEmitter {
121
127
  // which is not correct.
122
128
  modifiedFiles.forEach((file: string) => this._modifiedFiles.add(file));
123
129
  deletedFiles.forEach((file: string) => this._deletedFiles.add(file));
130
+ addedFiles.forEach((file: string) => this._addedFiles.add(file));
124
131
 
125
132
  // If after an error the number of modules has changed, we could be in
126
133
  // a weird state. As a safe net we clean the dependency modules to force
@@ -177,12 +184,45 @@ class DeltaCalculator<T> extends EventEmitter {
177
184
  filePath: string,
178
185
  ...
179
186
  }): mixed => {
187
+ let state: void | 'deleted' | 'modified' | 'added';
188
+ if (this._deletedFiles.has(filePath)) {
189
+ state = 'deleted';
190
+ } else if (this._modifiedFiles.has(filePath)) {
191
+ state = 'modified';
192
+ } else if (this._addedFiles.has(filePath)) {
193
+ state = 'added';
194
+ }
195
+
196
+ let nextState: 'deleted' | 'modified' | 'added';
180
197
  if (type === 'delete') {
181
- this._deletedFiles.add(filePath);
182
- this._modifiedFiles.delete(filePath);
198
+ nextState = 'deleted';
199
+ } else if (type === 'add') {
200
+ // A deleted+added file is modified
201
+ nextState = state === 'deleted' ? 'modified' : 'added';
183
202
  } else {
184
- this._deletedFiles.delete(filePath);
185
- this._modifiedFiles.add(filePath);
203
+ // type === 'change'
204
+ // An added+modified file is added
205
+ nextState = state === 'added' ? 'added' : 'modified';
206
+ }
207
+
208
+ switch (nextState) {
209
+ case 'deleted':
210
+ this._deletedFiles.add(filePath);
211
+ this._modifiedFiles.delete(filePath);
212
+ this._addedFiles.delete(filePath);
213
+ break;
214
+ case 'added':
215
+ this._addedFiles.add(filePath);
216
+ this._deletedFiles.delete(filePath);
217
+ this._modifiedFiles.delete(filePath);
218
+ break;
219
+ case 'modified':
220
+ this._modifiedFiles.add(filePath);
221
+ this._deletedFiles.delete(filePath);
222
+ this._addedFiles.delete(filePath);
223
+ break;
224
+ default:
225
+ (nextState: empty);
186
226
  }
187
227
 
188
228
  // Notify users that there is a change in some of the bundle files. This
@@ -193,6 +233,7 @@ class DeltaCalculator<T> extends EventEmitter {
193
233
  async _getChangedDependencies(
194
234
  modifiedFiles: Set<string>,
195
235
  deletedFiles: Set<string>,
236
+ addedFiles: Set<string>,
196
237
  ): Promise<DeltaResult<T>> {
197
238
  if (!this._graph.dependencies.size) {
198
239
  const {added} = await initialTraverseDependencies(
@@ -224,6 +265,18 @@ class DeltaCalculator<T> extends EventEmitter {
224
265
  }
225
266
  });
226
267
 
268
+ // NOTE(EvanBacon): This check adds extra complexity so we feature gate it
269
+ // to enable users to opt out.
270
+ if (this._options.unstable_allowRequireContext) {
271
+ // Check if any added or removed files are matched in a context module.
272
+ // We only need to do this for added files because (1) deleted files will have a context
273
+ // module as an inverse dependency, (2) modified files don't invalidate the contents
274
+ // of the context module.
275
+ addedFiles.forEach(filePath => {
276
+ markModifiedContextModules(this._graph, filePath, modifiedFiles);
277
+ });
278
+ }
279
+
227
280
  // We only want to process files that are in the bundle.
228
281
  const modifiedDependencies = Array.from(modifiedFiles).filter(
229
282
  (filePath: string) => this._graph.dependencies.has(filePath),
@@ -15,8 +15,6 @@ const { getJsOutput } = require("./helpers/js");
15
15
 
16
16
  const processBytecodeModules = require("./helpers/processBytecodeModules");
17
17
 
18
- const { compile } = require("metro-hermes-compiler");
19
-
20
18
  function baseBytecodeBundle(entryPoint, preModules, graph, options) {
21
19
  for (const module of graph.dependencies.values()) {
22
20
  options.createModuleId(module.path);
@@ -36,6 +34,9 @@ function baseBytecodeBundle(entryPoint, preModules, graph, options) {
36
34
  const modules = [...graph.dependencies.values()].sort(
37
35
  (a, b) => options.createModuleId(a.path) - options.createModuleId(b.path)
38
36
  );
37
+
38
+ const { compile } = require("metro-hermes-compiler");
39
+
39
40
  const post = processBytecodeModules(
40
41
  getAppendScripts(
41
42
  entryPoint,
@@ -21,7 +21,6 @@ import type {BytecodeBundle} from 'metro-runtime/src/modules/types.flow';
21
21
  const getAppendScripts = require('../../lib/getAppendScripts');
22
22
  const {getJsOutput} = require('./helpers/js');
23
23
  const processBytecodeModules = require('./helpers/processBytecodeModules');
24
- const {compile} = require('metro-hermes-compiler');
25
24
 
26
25
  function baseBytecodeBundle(
27
26
  entryPoint: string,
@@ -50,6 +49,8 @@ function baseBytecodeBundle(
50
49
  options.createModuleId(a.path) - options.createModuleId(b.path),
51
50
  );
52
51
 
52
+ const {compile} = require('metro-hermes-compiler');
53
+
53
54
  const post = processBytecodeModules(
54
55
  getAppendScripts(
55
56
  entryPoint,
@@ -11,8 +11,6 @@
11
11
 
12
12
  const invariant = require("invariant");
13
13
 
14
- const { compile } = require("metro-hermes-compiler");
15
-
16
14
  const path = require("path");
17
15
 
18
16
  function wrapModule(module, options) {
@@ -39,6 +37,8 @@ function wrapModule(module, options) {
39
37
  );
40
38
  }
41
39
 
40
+ const { compile } = require("metro-hermes-compiler");
41
+
42
42
  const headerCode = `globalThis.$$METRO_D=[${params.join(",")}];`;
43
43
  return [
44
44
  compile(headerCode, {
@@ -14,7 +14,6 @@ import type {Module} from '../../types.flow';
14
14
  import type {BytecodeOutput} from 'metro-transform-worker';
15
15
 
16
16
  const invariant = require('invariant');
17
- const {compile} = require('metro-hermes-compiler');
18
17
  const path = require('path');
19
18
 
20
19
  export type Options = {
@@ -48,6 +47,8 @@ function wrapModule(module: Module<>, options: Options): Array<Buffer> {
48
47
  );
49
48
  }
50
49
 
50
+ const {compile} = require('metro-hermes-compiler');
51
+
51
52
  const headerCode = `globalThis.$$METRO_D=[${params.join(',')}];`;
52
53
  return [
53
54
  compile(headerCode, {
@@ -36,9 +36,7 @@ function generateModules(
36
36
  for (const module of sourceModules) {
37
37
  if (isJsModule(module)) {
38
38
  // Construct a bundle URL for this specific module only
39
- const getURL = (
40
- extension: $TEMPORARY$string<'bundle'> | $TEMPORARY$string<'map'>,
41
- ) => {
39
+ const getURL = (extension: 'bundle' | 'map') => {
42
40
  options.clientUrl.pathname = path.relative(
43
41
  options.projectRoot,
44
42
  path.join(
@@ -9,6 +9,12 @@
9
9
  */
10
10
  "use strict";
11
11
 
12
+ var _crypto = _interopRequireDefault(require("crypto"));
13
+
14
+ function _interopRequireDefault(obj) {
15
+ return obj && obj.__esModule ? obj : { default: obj };
16
+ }
17
+
12
18
  const getTransformCacheKey = require("./getTransformCacheKey");
13
19
 
14
20
  const WorkerFarm = require("./WorkerFarm");
@@ -53,7 +59,7 @@ class Transformer {
53
59
  this._baseHash = stableHash([globalCacheKey]).toString("binary");
54
60
  }
55
61
 
56
- async transformFile(filePath, transformerOptions) {
62
+ async transformFile(filePath, transformerOptions, fileBuffer) {
57
63
  const cache = this._cache;
58
64
  const {
59
65
  customTransformOptions,
@@ -100,8 +106,17 @@ class Transformer {
100
106
  unstable_disableES6Transforms,
101
107
  unstable_transformProfile,
102
108
  ]);
103
-
104
- const sha1 = this._getSha1(filePath);
109
+ let sha1;
110
+
111
+ if (fileBuffer) {
112
+ // Shortcut for virtual modules which provide the contents with the filename.
113
+ sha1 = _crypto.default
114
+ .createHash("sha1")
115
+ .update(fileBuffer)
116
+ .digest("hex");
117
+ } else {
118
+ sha1 = this._getSha1(filePath);
119
+ }
105
120
 
106
121
  let fullKey = Buffer.concat([partialKey, Buffer.from(sha1, "hex")]);
107
122
  const result = await cache.get(fullKey); // A valid result from the cache is used directly; otherwise we call into
@@ -112,7 +127,11 @@ class Transformer {
112
127
  result,
113
128
  sha1,
114
129
  }
115
- : await this._workerFarm.transform(localPath, transformerOptions); // Only re-compute the full key if the SHA-1 changed. This is because
130
+ : await this._workerFarm.transform(
131
+ localPath,
132
+ transformerOptions,
133
+ fileBuffer
134
+ ); // Only re-compute the full key if the SHA-1 changed. This is because
116
135
  // references are used by the cache implementation in a weak map to keep
117
136
  // track of the cache that returned the result.
118
137
 
@@ -125,6 +144,10 @@ class Transformer {
125
144
  ...data.result,
126
145
 
127
146
  getSource() {
147
+ if (fileBuffer) {
148
+ return fileBuffer;
149
+ }
150
+
128
151
  return fs.readFileSync(filePath);
129
152
  },
130
153
  };
@@ -13,6 +13,7 @@
13
13
  import type {TransformResult, TransformResultWithSource} from '../DeltaBundler';
14
14
  import type {TransformerConfig, TransformOptions} from './Worker';
15
15
  import type {ConfigT} from 'metro-config/src/configTypes.flow';
16
+ import crypto from 'crypto';
16
17
 
17
18
  const getTransformCacheKey = require('./getTransformCacheKey');
18
19
  const WorkerFarm = require('./WorkerFarm');
@@ -66,6 +67,7 @@ class Transformer {
66
67
  async transformFile(
67
68
  filePath: string,
68
69
  transformerOptions: TransformOptions,
70
+ fileBuffer?: Buffer,
69
71
  ): Promise<TransformResultWithSource<>> {
70
72
  const cache = this._cache;
71
73
 
@@ -119,7 +121,14 @@ class Transformer {
119
121
  unstable_transformProfile,
120
122
  ]);
121
123
 
122
- const sha1 = this._getSha1(filePath);
124
+ let sha1: string;
125
+ if (fileBuffer) {
126
+ // Shortcut for virtual modules which provide the contents with the filename.
127
+ sha1 = crypto.createHash('sha1').update(fileBuffer).digest('hex');
128
+ } else {
129
+ sha1 = this._getSha1(filePath);
130
+ }
131
+
123
132
  let fullKey = Buffer.concat([partialKey, Buffer.from(sha1, 'hex')]);
124
133
  const result = await cache.get(fullKey);
125
134
 
@@ -127,7 +136,11 @@ class Transformer {
127
136
  // the transformer to computed the corresponding result.
128
137
  const data = result
129
138
  ? {result, sha1}
130
- : await this._workerFarm.transform(localPath, transformerOptions);
139
+ : await this._workerFarm.transform(
140
+ localPath,
141
+ transformerOptions,
142
+ fileBuffer,
143
+ );
131
144
 
132
145
  // Only re-compute the full key if the SHA-1 changed. This is because
133
146
  // references are used by the cache implementation in a weak map to keep
@@ -141,6 +154,9 @@ class Transformer {
141
154
  return {
142
155
  ...data.result,
143
156
  getSource(): Buffer {
157
+ if (fileBuffer) {
158
+ return fileBuffer;
159
+ }
144
160
  return fs.readFileSync(filePath);
145
161
  },
146
162
  };