metro 0.71.2 → 0.72.1

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 (91) hide show
  1. package/package.json +21 -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 +85 -21
  7. package/src/DeltaBundler/DeltaCalculator.js.flow +63 -8
  8. package/src/DeltaBundler/Serializers/hmrJSBundle.js.flow +1 -3
  9. package/src/DeltaBundler/Transformer.js +27 -4
  10. package/src/DeltaBundler/Transformer.js.flow +18 -2
  11. package/src/DeltaBundler/Worker.flow.js +45 -1
  12. package/src/DeltaBundler/Worker.flow.js.flow +42 -1
  13. package/src/DeltaBundler/WorkerFarm.js +3 -2
  14. package/src/DeltaBundler/WorkerFarm.js.flow +5 -3
  15. package/src/DeltaBundler/graphOperations.js +170 -63
  16. package/src/DeltaBundler/graphOperations.js.flow +144 -64
  17. package/src/DeltaBundler/types.flow.js.flow +11 -5
  18. package/src/HmrServer.js +2 -0
  19. package/src/HmrServer.js.flow +2 -0
  20. package/src/IncrementalBundler.js +6 -0
  21. package/src/IncrementalBundler.js.flow +6 -0
  22. package/src/ModuleGraph/node-haste/HasteFS.js.flow +1 -1
  23. package/src/ModuleGraph/node-haste/node-haste.js +14 -7
  24. package/src/ModuleGraph/node-haste/node-haste.js.flow +35 -10
  25. package/src/ModuleGraph/output/indexed-ram-bundle.js.flow +5 -13
  26. package/src/ModuleGraph/output/multiple-files-ram-bundle.js.flow +4 -14
  27. package/src/ModuleGraph/output/util.js +1 -0
  28. package/src/ModuleGraph/output/util.js.flow +4 -3
  29. package/src/ModuleGraph/silent-console.js +5 -4
  30. package/src/ModuleGraph/silent-console.js.flow +8 -4
  31. package/src/ModuleGraph/worker/collectDependencies.js +19 -30
  32. package/src/ModuleGraph/worker/collectDependencies.js.flow +28 -43
  33. package/src/Server.js +8 -0
  34. package/src/Server.js.flow +48 -12
  35. package/src/cli-utils.js.flow +1 -1
  36. package/src/commands/build.js +1 -2
  37. package/src/commands/build.js.flow +6 -9
  38. package/src/commands/dependencies.js +1 -1
  39. package/src/commands/serve.js +2 -1
  40. package/src/commands/serve.js.flow +7 -8
  41. package/src/index.flow.js +11 -8
  42. package/src/index.flow.js.flow +10 -7
  43. package/src/integration_tests/basic_bundle/require-context/conflict.js +25 -0
  44. package/src/integration_tests/basic_bundle/require-context/conflict.js.flow +27 -0
  45. package/src/integration_tests/basic_bundle/require-context/empty.js +29 -0
  46. package/src/integration_tests/basic_bundle/require-context/empty.js.flow +26 -0
  47. package/src/integration_tests/basic_bundle/require-context/matching.js +26 -0
  48. package/src/integration_tests/basic_bundle/require-context/matching.js.flow +27 -0
  49. package/src/integration_tests/basic_bundle/require-context/mode-eager.js +22 -0
  50. package/src/integration_tests/basic_bundle/require-context/mode-eager.js.flow +24 -0
  51. package/src/integration_tests/basic_bundle/require-context/mode-lazy-once.js +22 -0
  52. package/src/integration_tests/basic_bundle/require-context/mode-lazy-once.js.flow +24 -0
  53. package/src/integration_tests/basic_bundle/require-context/mode-lazy.js +22 -0
  54. package/src/integration_tests/basic_bundle/require-context/mode-lazy.js.flow +24 -0
  55. package/src/integration_tests/basic_bundle/require-context/mode-sync.js +20 -0
  56. package/src/integration_tests/basic_bundle/require-context/mode-sync.js.flow +22 -0
  57. package/src/integration_tests/basic_bundle/require-context/subdir/a.js +12 -0
  58. package/src/integration_tests/basic_bundle/require-context/subdir/a.js.flow +11 -0
  59. package/src/integration_tests/basic_bundle/require-context/subdir/b.js +18 -0
  60. package/src/integration_tests/basic_bundle/require-context/subdir/b.js.flow +11 -0
  61. package/src/integration_tests/basic_bundle/require-context/subdir/c.js +12 -0
  62. package/src/integration_tests/basic_bundle/require-context/subdir/c.js.flow +11 -0
  63. package/src/integration_tests/basic_bundle/require-context/subdir/nested/d.js +12 -0
  64. package/src/integration_tests/basic_bundle/require-context/subdir/nested/d.js.flow +11 -0
  65. package/src/integration_tests/basic_bundle/require-context/subdir-conflict/index.js +12 -0
  66. package/src/integration_tests/basic_bundle/require-context/subdir-conflict/index.js.flow +11 -0
  67. package/src/integration_tests/basic_bundle/require-context/utils.js +29 -0
  68. package/src/integration_tests/basic_bundle/require-context/utils.js.flow +44 -0
  69. package/src/lib/CountingSet.js +1 -0
  70. package/src/lib/CountingSet.js.flow +1 -0
  71. package/src/lib/contextModule.js +80 -0
  72. package/src/lib/contextModule.js.flow +86 -0
  73. package/src/lib/contextModuleTemplates.js +186 -0
  74. package/src/lib/contextModuleTemplates.js.flow +148 -0
  75. package/src/lib/getGraphId.js +2 -1
  76. package/src/lib/getGraphId.js.flow +3 -0
  77. package/src/lib/getPrependedScripts.js +2 -0
  78. package/src/lib/getPrependedScripts.js.flow +2 -0
  79. package/src/lib/parseOptionsFromUrl.js.flow +7 -18
  80. package/src/lib/transformHelpers.js +41 -9
  81. package/src/lib/transformHelpers.js.flow +46 -9
  82. package/src/node-haste/DependencyGraph/ModuleResolution.js +1 -0
  83. package/src/node-haste/DependencyGraph/ModuleResolution.js.flow +3 -2
  84. package/src/node-haste/DependencyGraph/createHasteMap.js +7 -1
  85. package/src/node-haste/DependencyGraph/createHasteMap.js.flow +8 -2
  86. package/src/node-haste/DependencyGraph.js +7 -0
  87. package/src/node-haste/DependencyGraph.js.flow +17 -2
  88. package/src/shared/output/bundle.flow.js +67 -0
  89. package/src/shared/output/bundle.flow.js.flow +89 -0
  90. package/src/shared/output/bundle.js +8 -55
  91. package/src/shared/output/bundle.js.flow +8 -75
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metro",
3
- "version": "0.71.2",
3
+ "version": "0.72.1",
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.2",
40
- "metro-cache": "0.71.2",
41
- "metro-cache-key": "0.71.2",
42
- "metro-config": "0.71.2",
43
- "metro-core": "0.71.2",
44
- "metro-file-map": "0.71.2",
45
- "metro-hermes-compiler": "0.71.2",
46
- "metro-inspector-proxy": "0.71.2",
47
- "metro-minify-uglify": "0.71.2",
48
- "metro-react-native-babel-preset": "0.71.2",
49
- "metro-resolver": "0.71.2",
50
- "metro-runtime": "0.71.2",
51
- "metro-source-map": "0.71.2",
52
- "metro-symbolicate": "0.71.2",
53
- "metro-transform-plugins": "0.71.2",
54
- "metro-transform-worker": "0.71.2",
39
+ "metro-babel-transformer": "0.72.1",
40
+ "metro-cache": "0.72.1",
41
+ "metro-cache-key": "0.72.1",
42
+ "metro-config": "0.72.1",
43
+ "metro-core": "0.72.1",
44
+ "metro-file-map": "0.72.1",
45
+ "metro-hermes-compiler": "0.72.1",
46
+ "metro-inspector-proxy": "0.72.1",
47
+ "metro-minify-uglify": "0.72.1",
48
+ "metro-react-native-babel-preset": "0.72.1",
49
+ "metro-resolver": "0.72.1",
50
+ "metro-runtime": "0.72.1",
51
+ "metro-source-map": "0.72.1",
52
+ "metro-symbolicate": "0.72.1",
53
+ "metro-transform-plugins": "0.72.1",
54
+ "metro-transform-worker": "0.72.1",
55
55
  "mime-types": "^2.1.27",
56
56
  "node-fetch": "^2.2.0",
57
57
  "nullthrows": "^1.1.1",
@@ -70,10 +70,10 @@
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.2",
74
- "metro-memory-fs": "0.71.2",
75
- "metro-react-native-babel-preset": "0.71.2",
76
- "metro-react-native-babel-transformer": "0.71.2",
73
+ "metro-babel-register": "0.72.1",
74
+ "metro-memory-fs": "0.72.1",
75
+ "metro-react-native-babel-preset": "0.72.1",
76
+ "metro-react-native-babel-transformer": "0.72.1",
77
77
  "stack-trace": "^0.0.10"
78
78
  },
79
79
  "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 {
@@ -130,6 +131,8 @@ class DeltaCalculator extends EventEmitter {
130
131
  getGraph() {
131
132
  return this._graph;
132
133
  }
134
+ /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
135
+ * LTI update could not be added via codemod */
133
136
 
134
137
  _handleMultipleFileChanges = ({ eventsQueue }) => {
135
138
  eventsQueue.forEach(this._handleFileChange);
@@ -141,23 +144,68 @@ class DeltaCalculator extends EventEmitter {
141
144
  */
142
145
 
143
146
  _handleFileChange = ({ type, filePath }) => {
144
- if (type === "delete") {
145
- 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;
146
158
 
147
- 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";
148
164
  } else {
149
- 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;
150
179
 
151
- 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;
152
200
  } // Notify users that there is a change in some of the bundle files. This
153
201
  // way the client can choose to refetch the bundle.
154
202
 
155
203
  this.emit("change");
156
204
  };
157
205
 
158
- async _getChangedDependencies(modifiedFiles, deletedFiles) {
206
+ async _getChangedDependencies(modifiedFiles, deletedFiles, addedFiles) {
159
207
  if (!this._graph.dependencies.size) {
160
- const { added } = await initialTraverseDependencies(
208
+ const { added } = await (0, _graphOperations.initialTraverseDependencies)(
161
209
  this._graph,
162
210
  this._options
163
211
  );
@@ -182,7 +230,22 @@ class DeltaCalculator extends EventEmitter {
182
230
  }
183
231
  });
184
232
  }
185
- }); // 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.
186
249
 
187
250
  const modifiedDependencies = Array.from(modifiedFiles).filter((filePath) =>
188
251
  this._graph.dependencies.has(filePath)
@@ -197,7 +260,8 @@ class DeltaCalculator extends EventEmitter {
197
260
  };
198
261
  }
199
262
 
200
- const { added, modified, deleted } = await traverseDependencies(
263
+ const { added, modified, deleted } = await (0,
264
+ _graphOperations.traverseDependencies)(
201
265
  modifiedDependencies,
202
266
  this._graph,
203
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
@@ -158,6 +165,8 @@ class DeltaCalculator<T> extends EventEmitter {
158
165
  return this._graph;
159
166
  }
160
167
 
168
+ /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
169
+ * LTI update could not be added via codemod */
161
170
  _handleMultipleFileChanges = ({eventsQueue}) => {
162
171
  eventsQueue.forEach(this._handleFileChange);
163
172
  };
@@ -175,12 +184,45 @@ class DeltaCalculator<T> extends EventEmitter {
175
184
  filePath: string,
176
185
  ...
177
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';
178
197
  if (type === 'delete') {
179
- this._deletedFiles.add(filePath);
180
- 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';
181
202
  } else {
182
- this._deletedFiles.delete(filePath);
183
- 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);
184
226
  }
185
227
 
186
228
  // Notify users that there is a change in some of the bundle files. This
@@ -191,6 +233,7 @@ class DeltaCalculator<T> extends EventEmitter {
191
233
  async _getChangedDependencies(
192
234
  modifiedFiles: Set<string>,
193
235
  deletedFiles: Set<string>,
236
+ addedFiles: Set<string>,
194
237
  ): Promise<DeltaResult<T>> {
195
238
  if (!this._graph.dependencies.size) {
196
239
  const {added} = await initialTraverseDependencies(
@@ -222,6 +265,18 @@ class DeltaCalculator<T> extends EventEmitter {
222
265
  }
223
266
  });
224
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
+
225
280
  // We only want to process files that are in the bundle.
226
281
  const modifiedDependencies = Array.from(modifiedFiles).filter(
227
282
  (filePath: string) => this._graph.dependencies.has(filePath),
@@ -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
  };
@@ -17,10 +17,55 @@ const fs = require("fs");
17
17
 
18
18
  const path = require("path");
19
19
 
20
+ /**
21
+ * When the `Buffer` is sent over the worker thread it gets serialized into a JSON object.
22
+ * This helper method will deserialize it if needed.
23
+ *
24
+ * @returns `Buffer` representation of the JSON object.
25
+ * @returns `null` if the given object is nullish or not a serialized `Buffer` object.
26
+ */
27
+ function asDeserializedBuffer(value) {
28
+ if (Buffer.isBuffer(value)) {
29
+ return value;
30
+ }
31
+
32
+ if (value && value.type === "Buffer") {
33
+ return Buffer.from(value.data);
34
+ }
35
+
36
+ return null;
37
+ }
38
+
20
39
  async function transform(
21
40
  filename,
22
41
  transformOptions,
23
42
  projectRoot,
43
+ transformerConfig,
44
+ fileBuffer
45
+ ) {
46
+ let data;
47
+ const fileBufferObject = asDeserializedBuffer(fileBuffer);
48
+
49
+ if (fileBufferObject) {
50
+ data = fileBufferObject;
51
+ } else {
52
+ data = fs.readFileSync(path.resolve(projectRoot, filename));
53
+ }
54
+
55
+ return transformFile(
56
+ filename,
57
+ data,
58
+ transformOptions,
59
+ projectRoot,
60
+ transformerConfig
61
+ );
62
+ }
63
+
64
+ async function transformFile(
65
+ filename,
66
+ data,
67
+ transformOptions,
68
+ projectRoot,
24
69
  transformerConfig
25
70
  ) {
26
71
  // eslint-disable-next-line no-useless-call
@@ -33,7 +78,6 @@ async function transform(
33
78
  log_entry_label: "Transforming file",
34
79
  start_timestamp: process.hrtime(),
35
80
  };
36
- const data = fs.readFileSync(path.resolve(projectRoot, filename));
37
81
  const sha1 = crypto.createHash("sha1").update(data).digest("hex");
38
82
  const result = await Transformer.transform(
39
83
  transformerConfig.transformerConfig,