metro 0.81.2 → 0.82.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.
- package/package.json +16 -16
- package/src/Bundler.js +4 -9
- package/src/Bundler.js.flow +4 -11
- package/src/DeltaBundler/Transformer.js +5 -12
- package/src/DeltaBundler/Transformer.js.flow +7 -17
- package/src/DeltaBundler/types.flow.js.flow +5 -0
- package/src/HmrServer.js +1 -0
- package/src/HmrServer.js.flow +1 -0
- package/src/ModuleGraph/worker/collectDependencies.js +22 -5
- package/src/ModuleGraph/worker/collectDependencies.js.flow +42 -9
- package/src/ModuleGraph/worker/importLocationsPlugin.js +51 -0
- package/src/ModuleGraph/worker/importLocationsPlugin.js.flow +82 -0
- package/src/Server.js +2 -1
- package/src/Server.js.flow +2 -2
- package/src/node-haste/DependencyGraph/ModuleResolution.js +2 -0
- package/src/node-haste/DependencyGraph/ModuleResolution.js.flow +2 -0
- package/src/node-haste/DependencyGraph.d.ts +1 -1
- package/src/node-haste/DependencyGraph.js +9 -17
- package/src/node-haste/DependencyGraph.js.flow +9 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "metro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.82.0",
|
|
4
4
|
"description": "🚇 The JavaScript bundler for React Native.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": "src/cli.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"chalk": "^4.0.0",
|
|
33
33
|
"ci-info": "^2.0.0",
|
|
34
34
|
"connect": "^3.6.5",
|
|
35
|
-
"debug": "^
|
|
35
|
+
"debug": "^4.4.0",
|
|
36
36
|
"error-stack-parser": "^2.0.6",
|
|
37
37
|
"flow-enums-runtime": "^0.0.6",
|
|
38
38
|
"graceful-fs": "^4.2.4",
|
|
@@ -42,18 +42,18 @@
|
|
|
42
42
|
"jest-worker": "^29.7.0",
|
|
43
43
|
"jsc-safe-url": "^0.2.2",
|
|
44
44
|
"lodash.throttle": "^4.1.1",
|
|
45
|
-
"metro-babel-transformer": "0.
|
|
46
|
-
"metro-cache": "0.
|
|
47
|
-
"metro-cache-key": "0.
|
|
48
|
-
"metro-config": "0.
|
|
49
|
-
"metro-core": "0.
|
|
50
|
-
"metro-file-map": "0.
|
|
51
|
-
"metro-resolver": "0.
|
|
52
|
-
"metro-runtime": "0.
|
|
53
|
-
"metro-source-map": "0.
|
|
54
|
-
"metro-symbolicate": "0.
|
|
55
|
-
"metro-transform-plugins": "0.
|
|
56
|
-
"metro-transform-worker": "0.
|
|
45
|
+
"metro-babel-transformer": "0.82.0",
|
|
46
|
+
"metro-cache": "0.82.0",
|
|
47
|
+
"metro-cache-key": "0.82.0",
|
|
48
|
+
"metro-config": "0.82.0",
|
|
49
|
+
"metro-core": "0.82.0",
|
|
50
|
+
"metro-file-map": "0.82.0",
|
|
51
|
+
"metro-resolver": "0.82.0",
|
|
52
|
+
"metro-runtime": "0.82.0",
|
|
53
|
+
"metro-source-map": "0.82.0",
|
|
54
|
+
"metro-symbolicate": "0.82.0",
|
|
55
|
+
"metro-transform-plugins": "0.82.0",
|
|
56
|
+
"metro-transform-worker": "0.82.0",
|
|
57
57
|
"mime-types": "^2.1.27",
|
|
58
58
|
"nullthrows": "^1.1.1",
|
|
59
59
|
"serialize-error": "^2.1.0",
|
|
@@ -71,8 +71,8 @@
|
|
|
71
71
|
"dedent": "^0.7.0",
|
|
72
72
|
"jest-snapshot": "^29.7.0",
|
|
73
73
|
"jest-snapshot-serializer-raw": "^1.2.0",
|
|
74
|
-
"metro-babel-register": "0.
|
|
75
|
-
"metro-memory-fs": "0.
|
|
74
|
+
"metro-babel-register": "0.82.0",
|
|
75
|
+
"metro-memory-fs": "0.82.0",
|
|
76
76
|
"mock-req": "^0.2.0",
|
|
77
77
|
"mock-res": "^0.6.0",
|
|
78
78
|
"stack-trace": "^0.0.10"
|
package/src/Bundler.js
CHANGED
|
@@ -11,15 +11,10 @@ class Bundler {
|
|
|
11
11
|
config.reporter.update({
|
|
12
12
|
type: "transformer_load_started",
|
|
13
13
|
});
|
|
14
|
-
this._transformer = new Transformer(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
unstable_getOrComputeSha1: (filePath) =>
|
|
19
|
-
this._depGraph.unstable_getOrComputeSha1(filePath),
|
|
20
|
-
}
|
|
21
|
-
: (...args) => this._depGraph.getSha1(...args)
|
|
22
|
-
);
|
|
14
|
+
this._transformer = new Transformer(config, {
|
|
15
|
+
getOrComputeSha1: (filePath) =>
|
|
16
|
+
this._depGraph.getOrComputeSha1(filePath),
|
|
17
|
+
});
|
|
23
18
|
config.reporter.update({
|
|
24
19
|
type: "transformer_load_done",
|
|
25
20
|
});
|
package/src/Bundler.js.flow
CHANGED
|
@@ -36,17 +36,10 @@ class Bundler {
|
|
|
36
36
|
.ready()
|
|
37
37
|
.then(() => {
|
|
38
38
|
config.reporter.update({type: 'transformer_load_started'});
|
|
39
|
-
this._transformer = new Transformer(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// once lazy SHA1 is stable. This will be a breaking change.
|
|
44
|
-
{
|
|
45
|
-
unstable_getOrComputeSha1: filePath =>
|
|
46
|
-
this._depGraph.unstable_getOrComputeSha1(filePath),
|
|
47
|
-
}
|
|
48
|
-
: (...args) => this._depGraph.getSha1(...args),
|
|
49
|
-
);
|
|
39
|
+
this._transformer = new Transformer(config, {
|
|
40
|
+
getOrComputeSha1: filePath =>
|
|
41
|
+
this._depGraph.getOrComputeSha1(filePath),
|
|
42
|
+
});
|
|
50
43
|
config.reporter.update({type: 'transformer_load_done'});
|
|
51
44
|
})
|
|
52
45
|
.catch(error => {
|
|
@@ -11,14 +11,11 @@ const fs = require("fs");
|
|
|
11
11
|
const { Cache, stableHash } = require("metro-cache");
|
|
12
12
|
const path = require("path");
|
|
13
13
|
class Transformer {
|
|
14
|
-
constructor(config,
|
|
14
|
+
constructor(config, opts) {
|
|
15
15
|
this._config = config;
|
|
16
16
|
this._config.watchFolders.forEach(verifyRootExists);
|
|
17
17
|
this._cache = new Cache(config.cacheStores);
|
|
18
|
-
this._getSha1 =
|
|
19
|
-
typeof getSha1FnOrOpts === "function"
|
|
20
|
-
? getSha1FnOrOpts
|
|
21
|
-
: getSha1FnOrOpts.unstable_getOrComputeSha1;
|
|
18
|
+
this._getSha1 = opts.getOrComputeSha1;
|
|
22
19
|
const {
|
|
23
20
|
getTransformOptions: _getTransformOptions,
|
|
24
21
|
transformVariants: _transformVariants,
|
|
@@ -95,13 +92,9 @@ class Transformer {
|
|
|
95
92
|
content = fileBuffer;
|
|
96
93
|
} else {
|
|
97
94
|
const result = await this._getSha1(filePath);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
sha1 = result.sha1;
|
|
102
|
-
if (result.content) {
|
|
103
|
-
content = result.content;
|
|
104
|
-
}
|
|
95
|
+
sha1 = result.sha1;
|
|
96
|
+
if (result.content) {
|
|
97
|
+
content = result.content;
|
|
105
98
|
}
|
|
106
99
|
}
|
|
107
100
|
let fullKey = Buffer.concat([partialKey, Buffer.from(sha1, "hex")]);
|
|
@@ -24,30 +24,24 @@ const fs = require('fs');
|
|
|
24
24
|
const {Cache, stableHash} = require('metro-cache');
|
|
25
25
|
const path = require('path');
|
|
26
26
|
|
|
27
|
-
type
|
|
28
|
-
type EagerSha1Fn = string => string;
|
|
27
|
+
type GetOrComputeSha1Fn = string => Promise<{content?: Buffer, sha1: string}>;
|
|
29
28
|
|
|
30
29
|
class Transformer {
|
|
31
30
|
_config: ConfigT;
|
|
32
31
|
_cache: Cache<TransformResult<>>;
|
|
33
32
|
_baseHash: string;
|
|
34
|
-
_getSha1:
|
|
33
|
+
_getSha1: GetOrComputeSha1Fn;
|
|
35
34
|
_workerFarm: WorkerFarm;
|
|
36
35
|
|
|
37
36
|
constructor(
|
|
38
37
|
config: ConfigT,
|
|
39
|
-
|
|
40
|
-
| $ReadOnly<{unstable_getOrComputeSha1: LazySha1Fn}>
|
|
41
|
-
| EagerSha1Fn,
|
|
38
|
+
opts: $ReadOnly<{getOrComputeSha1: GetOrComputeSha1Fn}>,
|
|
42
39
|
) {
|
|
43
40
|
this._config = config;
|
|
44
41
|
|
|
45
42
|
this._config.watchFolders.forEach(verifyRootExists);
|
|
46
43
|
this._cache = new Cache(config.cacheStores);
|
|
47
|
-
this._getSha1 =
|
|
48
|
-
typeof getSha1FnOrOpts === 'function'
|
|
49
|
-
? getSha1FnOrOpts
|
|
50
|
-
: getSha1FnOrOpts.unstable_getOrComputeSha1;
|
|
44
|
+
this._getSha1 = opts.getOrComputeSha1;
|
|
51
45
|
|
|
52
46
|
// Remove the transformer config params that we don't want to pass to the
|
|
53
47
|
// transformer. We should change the config object and move them away so we
|
|
@@ -147,13 +141,9 @@ class Transformer {
|
|
|
147
141
|
content = fileBuffer;
|
|
148
142
|
} else {
|
|
149
143
|
const result = await this._getSha1(filePath);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
sha1 = result.sha1;
|
|
154
|
-
if (result.content) {
|
|
155
|
-
content = result.content;
|
|
156
|
-
}
|
|
144
|
+
sha1 = result.sha1;
|
|
145
|
+
if (result.content) {
|
|
146
|
+
content = result.content;
|
|
157
147
|
}
|
|
158
148
|
}
|
|
159
149
|
|
|
@@ -44,6 +44,11 @@ export type TransformResultDependency = $ReadOnly<{
|
|
|
44
44
|
* If not null, this dependency is due to a dynamic `import()` or `__prefetchImport()` call.
|
|
45
45
|
*/
|
|
46
46
|
asyncType: AsyncDependencyType | null,
|
|
47
|
+
/**
|
|
48
|
+
* True if the dependency is declared with a static "import x from 'y'" or
|
|
49
|
+
* an import() call.
|
|
50
|
+
*/
|
|
51
|
+
isESMImport: boolean,
|
|
47
52
|
/**
|
|
48
53
|
* The dependency is enclosed in a try/catch block.
|
|
49
54
|
*/
|
package/src/HmrServer.js
CHANGED
package/src/HmrServer.js.flow
CHANGED
|
@@ -21,6 +21,7 @@ function collectDependencies(ast, options) {
|
|
|
21
21
|
keepRequireNames: options.keepRequireNames,
|
|
22
22
|
allowOptionalDependencies: options.allowOptionalDependencies,
|
|
23
23
|
unstable_allowRequireContext: options.unstable_allowRequireContext,
|
|
24
|
+
unstable_isESMImportAtSource: options.unstable_isESMImportAtSource ?? null,
|
|
24
25
|
};
|
|
25
26
|
const visitor = {
|
|
26
27
|
CallExpression(path, state) {
|
|
@@ -32,12 +33,14 @@ function collectDependencies(ast, options) {
|
|
|
32
33
|
if (isImport(callee)) {
|
|
33
34
|
processImportCall(path, state, {
|
|
34
35
|
asyncType: "async",
|
|
36
|
+
isESMImport: true,
|
|
35
37
|
});
|
|
36
38
|
return;
|
|
37
39
|
}
|
|
38
40
|
if (name === "__prefetchImport" && !path.scope.getBinding(name)) {
|
|
39
41
|
processImportCall(path, state, {
|
|
40
42
|
asyncType: "prefetch",
|
|
43
|
+
isESMImport: true,
|
|
41
44
|
});
|
|
42
45
|
return;
|
|
43
46
|
}
|
|
@@ -79,6 +82,7 @@ function collectDependencies(ast, options) {
|
|
|
79
82
|
) {
|
|
80
83
|
processImportCall(path, state, {
|
|
81
84
|
asyncType: "maybeSync",
|
|
85
|
+
isESMImport: true,
|
|
82
86
|
});
|
|
83
87
|
visited.add(path.node);
|
|
84
88
|
return;
|
|
@@ -222,6 +226,7 @@ function processRequireContextCall(path, state) {
|
|
|
222
226
|
name: directory,
|
|
223
227
|
contextParams,
|
|
224
228
|
asyncType: null,
|
|
229
|
+
isESMImport: false,
|
|
225
230
|
optional: isOptionalDependency(directory, path, state),
|
|
226
231
|
},
|
|
227
232
|
path
|
|
@@ -239,6 +244,7 @@ function processResolveWeakCall(path, state) {
|
|
|
239
244
|
{
|
|
240
245
|
name,
|
|
241
246
|
asyncType: "weak",
|
|
247
|
+
isESMImport: false,
|
|
242
248
|
optional: isOptionalDependency(name, path, state),
|
|
243
249
|
},
|
|
244
250
|
path
|
|
@@ -261,6 +267,7 @@ See: https://github.com/facebook/metro/pull/1343`
|
|
|
261
267
|
{
|
|
262
268
|
name: path.node.source.value,
|
|
263
269
|
asyncType: null,
|
|
270
|
+
isESMImport: true,
|
|
264
271
|
optional: false,
|
|
265
272
|
},
|
|
266
273
|
path
|
|
@@ -277,6 +284,7 @@ function processImportCall(path, state, options) {
|
|
|
277
284
|
{
|
|
278
285
|
name,
|
|
279
286
|
asyncType: options.asyncType,
|
|
287
|
+
isESMImport: options.isESMImport,
|
|
280
288
|
optional: isOptionalDependency(name, path, state),
|
|
281
289
|
},
|
|
282
290
|
path
|
|
@@ -309,11 +317,20 @@ function processRequireCall(path, state) {
|
|
|
309
317
|
transformer.transformIllegalDynamicRequire(path, state);
|
|
310
318
|
return;
|
|
311
319
|
}
|
|
320
|
+
let isESMImport = false;
|
|
321
|
+
if (state.unstable_isESMImportAtSource) {
|
|
322
|
+
const isImport = state.unstable_isESMImportAtSource;
|
|
323
|
+
const loc = getNearestLocFromPath(path);
|
|
324
|
+
if (loc) {
|
|
325
|
+
isESMImport = isImport(loc);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
312
328
|
const dep = registerDependency(
|
|
313
329
|
state,
|
|
314
330
|
{
|
|
315
331
|
name,
|
|
316
332
|
asyncType: null,
|
|
333
|
+
isESMImport,
|
|
317
334
|
optional: isOptionalDependency(name, path, state),
|
|
318
335
|
},
|
|
319
336
|
path
|
|
@@ -502,12 +519,11 @@ function createModuleNameLiteral(dependency) {
|
|
|
502
519
|
return types.stringLiteral(dependency.name);
|
|
503
520
|
}
|
|
504
521
|
function getKeyForDependency(qualifier) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
if (asyncType) {
|
|
508
|
-
key +=
|
|
522
|
+
const { asyncType, contextParams, isESMImport, name } = qualifier;
|
|
523
|
+
let key = [name, isESMImport ? "import" : "require"].join("\0");
|
|
524
|
+
if (asyncType != null) {
|
|
525
|
+
key += "\0" + asyncType;
|
|
509
526
|
}
|
|
510
|
-
const { contextParams } = qualifier;
|
|
511
527
|
if (contextParams) {
|
|
512
528
|
key += [
|
|
513
529
|
"",
|
|
@@ -529,6 +545,7 @@ class DependencyRegistry {
|
|
|
529
545
|
const newDependency = {
|
|
530
546
|
name: qualifier.name,
|
|
531
547
|
asyncType: qualifier.asyncType,
|
|
548
|
+
isESMImport: qualifier.isESMImport,
|
|
532
549
|
locs: [],
|
|
533
550
|
index: this._dependencies.size,
|
|
534
551
|
key: crypto.createHash("sha1").update(key).digest("base64"),
|
|
@@ -29,6 +29,7 @@ const {isImport} = types;
|
|
|
29
29
|
|
|
30
30
|
type ImportDependencyOptions = $ReadOnly<{
|
|
31
31
|
asyncType: AsyncDependencyType,
|
|
32
|
+
isESMImport: boolean,
|
|
32
33
|
}>;
|
|
33
34
|
|
|
34
35
|
export type Dependency = $ReadOnly<{
|
|
@@ -56,6 +57,11 @@ type DependencyData = $ReadOnly<{
|
|
|
56
57
|
// If null, then the dependency is synchronous.
|
|
57
58
|
// (ex. `require('foo')`)
|
|
58
59
|
asyncType: AsyncDependencyType | null,
|
|
60
|
+
// If true, the dependency is declared using an ESM import, e.g.
|
|
61
|
+
// "import x from 'y'" or "await import('z')". A resolver should typically
|
|
62
|
+
// use this to assert either "import" or "require" for conditional exports
|
|
63
|
+
// and subpath imports.
|
|
64
|
+
isESMImport: boolean,
|
|
59
65
|
isOptional?: boolean,
|
|
60
66
|
locs: $ReadOnlyArray<BabelSourceLocation>,
|
|
61
67
|
/** Context for requiring a collection of modules. */
|
|
@@ -82,6 +88,7 @@ export type State = {
|
|
|
82
88
|
allowOptionalDependencies: AllowOptionalDependencies,
|
|
83
89
|
/** Enable `require.context` statements which can be used to import multiple files in a directory. */
|
|
84
90
|
unstable_allowRequireContext: boolean,
|
|
91
|
+
unstable_isESMImportAtSource: ?(BabelSourceLocation) => boolean,
|
|
85
92
|
};
|
|
86
93
|
|
|
87
94
|
export type Options = $ReadOnly<{
|
|
@@ -94,6 +101,7 @@ export type Options = $ReadOnly<{
|
|
|
94
101
|
dependencyTransformer?: DependencyTransformer,
|
|
95
102
|
/** Enable `require.context` statements which can be used to import multiple files in a directory. */
|
|
96
103
|
unstable_allowRequireContext: boolean,
|
|
104
|
+
unstable_isESMImportAtSource?: ?(BabelSourceLocation) => boolean,
|
|
97
105
|
}>;
|
|
98
106
|
|
|
99
107
|
export type CollectedDependencies = $ReadOnly<{
|
|
@@ -154,6 +162,7 @@ function collectDependencies(
|
|
|
154
162
|
keepRequireNames: options.keepRequireNames,
|
|
155
163
|
allowOptionalDependencies: options.allowOptionalDependencies,
|
|
156
164
|
unstable_allowRequireContext: options.unstable_allowRequireContext,
|
|
165
|
+
unstable_isESMImportAtSource: options.unstable_isESMImportAtSource ?? null,
|
|
157
166
|
};
|
|
158
167
|
|
|
159
168
|
const visitor = {
|
|
@@ -171,6 +180,7 @@ function collectDependencies(
|
|
|
171
180
|
if (isImport(callee)) {
|
|
172
181
|
processImportCall(path, state, {
|
|
173
182
|
asyncType: 'async',
|
|
183
|
+
isESMImport: true,
|
|
174
184
|
});
|
|
175
185
|
return;
|
|
176
186
|
}
|
|
@@ -178,6 +188,7 @@ function collectDependencies(
|
|
|
178
188
|
if (name === '__prefetchImport' && !path.scope.getBinding(name)) {
|
|
179
189
|
processImportCall(path, state, {
|
|
180
190
|
asyncType: 'prefetch',
|
|
191
|
+
isESMImport: true,
|
|
181
192
|
});
|
|
182
193
|
return;
|
|
183
194
|
}
|
|
@@ -235,6 +246,10 @@ function collectDependencies(
|
|
|
235
246
|
) {
|
|
236
247
|
processImportCall(path, state, {
|
|
237
248
|
asyncType: 'maybeSync',
|
|
249
|
+
// Treat require.unstable_importMaybeSync as an ESM import, like its
|
|
250
|
+
// async "await import()" counterpart. Subject to change while
|
|
251
|
+
// unstable_.
|
|
252
|
+
isESMImport: true,
|
|
238
253
|
});
|
|
239
254
|
visited.add(path.node);
|
|
240
255
|
return;
|
|
@@ -408,6 +423,7 @@ function processRequireContextCall(
|
|
|
408
423
|
// Capture the matching context
|
|
409
424
|
contextParams,
|
|
410
425
|
asyncType: null,
|
|
426
|
+
isESMImport: false,
|
|
411
427
|
optional: isOptionalDependency(directory, path, state),
|
|
412
428
|
},
|
|
413
429
|
path,
|
|
@@ -433,6 +449,7 @@ function processResolveWeakCall(
|
|
|
433
449
|
{
|
|
434
450
|
name,
|
|
435
451
|
asyncType: 'weak',
|
|
452
|
+
isESMImport: false,
|
|
436
453
|
optional: isOptionalDependency(name, path, state),
|
|
437
454
|
},
|
|
438
455
|
path,
|
|
@@ -458,6 +475,7 @@ See: https://github.com/facebook/metro/pull/1343`,
|
|
|
458
475
|
{
|
|
459
476
|
name: path.node.source.value,
|
|
460
477
|
asyncType: null,
|
|
478
|
+
isESMImport: true,
|
|
461
479
|
optional: false,
|
|
462
480
|
},
|
|
463
481
|
path,
|
|
@@ -481,6 +499,7 @@ function processImportCall(
|
|
|
481
499
|
{
|
|
482
500
|
name,
|
|
483
501
|
asyncType: options.asyncType,
|
|
502
|
+
isESMImport: options.isESMImport,
|
|
484
503
|
optional: isOptionalDependency(name, path, state),
|
|
485
504
|
},
|
|
486
505
|
path,
|
|
@@ -523,11 +542,21 @@ function processRequireCall(
|
|
|
523
542
|
return;
|
|
524
543
|
}
|
|
525
544
|
|
|
545
|
+
let isESMImport = false;
|
|
546
|
+
if (state.unstable_isESMImportAtSource) {
|
|
547
|
+
const isImport = state.unstable_isESMImportAtSource;
|
|
548
|
+
const loc = getNearestLocFromPath(path);
|
|
549
|
+
if (loc) {
|
|
550
|
+
isESMImport = isImport(loc);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
526
554
|
const dep = registerDependency(
|
|
527
555
|
state,
|
|
528
556
|
{
|
|
529
557
|
name,
|
|
530
558
|
asyncType: null,
|
|
559
|
+
isESMImport,
|
|
531
560
|
optional: isOptionalDependency(name, path, state),
|
|
532
561
|
},
|
|
533
562
|
path,
|
|
@@ -555,6 +584,7 @@ function getNearestLocFromPath(path: NodePath<>): ?BabelSourceLocation {
|
|
|
555
584
|
export type ImportQualifier = $ReadOnly<{
|
|
556
585
|
name: string,
|
|
557
586
|
asyncType: AsyncDependencyType | null,
|
|
587
|
+
isESMImport: boolean,
|
|
558
588
|
optional: boolean,
|
|
559
589
|
contextParams?: RequireContextParams,
|
|
560
590
|
}>;
|
|
@@ -801,13 +831,16 @@ function createModuleNameLiteral(dependency: InternalDependency) {
|
|
|
801
831
|
|
|
802
832
|
/**
|
|
803
833
|
* Given an import qualifier, return a key used to register the dependency.
|
|
804
|
-
*
|
|
805
|
-
*
|
|
806
|
-
*
|
|
834
|
+
* Attributes can be appended to distinguish various combinations that would
|
|
835
|
+
* otherwise be considered the same dependency edge.
|
|
836
|
+
*
|
|
837
|
+
* For example, the following dependencies would collapse into a single edge
|
|
838
|
+
* if they simply utilized the `name` property:
|
|
807
839
|
*
|
|
808
|
-
* For example, the following case would have collision issues if they all utilized the `name` property:
|
|
809
840
|
* ```
|
|
810
841
|
* require('./foo');
|
|
842
|
+
* import foo from './foo'
|
|
843
|
+
* await import('./foo')
|
|
811
844
|
* require.context('./foo');
|
|
812
845
|
* require.context('./foo', true, /something/);
|
|
813
846
|
* require.context('./foo', false, /something/);
|
|
@@ -817,14 +850,13 @@ function createModuleNameLiteral(dependency: InternalDependency) {
|
|
|
817
850
|
* This method should be utilized by `registerDependency`.
|
|
818
851
|
*/
|
|
819
852
|
function getKeyForDependency(qualifier: ImportQualifier): string {
|
|
820
|
-
|
|
853
|
+
const {asyncType, contextParams, isESMImport, name} = qualifier;
|
|
821
854
|
|
|
822
|
-
|
|
823
|
-
if (asyncType) {
|
|
824
|
-
key +=
|
|
855
|
+
let key = [name, isESMImport ? 'import' : 'require'].join('\0');
|
|
856
|
+
if (asyncType != null) {
|
|
857
|
+
key += '\0' + asyncType;
|
|
825
858
|
}
|
|
826
859
|
|
|
827
|
-
const {contextParams} = qualifier;
|
|
828
860
|
// Add extra qualifiers when using `require.context` to prevent collisions.
|
|
829
861
|
if (contextParams) {
|
|
830
862
|
// NOTE(EvanBacon): Keep this synchronized with `RequireContextParams`, if any other properties are added
|
|
@@ -854,6 +886,7 @@ class DependencyRegistry {
|
|
|
854
886
|
const newDependency: MutableInternalDependency = {
|
|
855
887
|
name: qualifier.name,
|
|
856
888
|
asyncType: qualifier.asyncType,
|
|
889
|
+
isESMImport: qualifier.isESMImport,
|
|
857
890
|
locs: [],
|
|
858
891
|
index: this._dependencies.size,
|
|
859
892
|
key: crypto.createHash('sha1').update(key).digest('base64'),
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const invariant = require("invariant");
|
|
4
|
+
function importLocationsPlugin({ types: t }) {
|
|
5
|
+
const importDeclarationLocs = new Set();
|
|
6
|
+
return {
|
|
7
|
+
visitor: {
|
|
8
|
+
ImportDeclaration(path) {
|
|
9
|
+
if (path.node.importKind !== "type" && path.node.loc != null) {
|
|
10
|
+
importDeclarationLocs.add(locToKey(path.node.loc));
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
ExportDeclaration(path) {
|
|
14
|
+
if (
|
|
15
|
+
path.node.source != null &&
|
|
16
|
+
path.node.exportKind !== "type" &&
|
|
17
|
+
path.node.loc != null
|
|
18
|
+
) {
|
|
19
|
+
importDeclarationLocs.add(locToKey(path.node.loc));
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
pre: ({ path, metadata }) => {
|
|
24
|
+
invariant(
|
|
25
|
+
path && t.isProgram(path.node),
|
|
26
|
+
"path missing or not a program node"
|
|
27
|
+
);
|
|
28
|
+
const metroMetadata = metadata;
|
|
29
|
+
if (!metroMetadata.metro) {
|
|
30
|
+
metroMetadata.metro = {
|
|
31
|
+
unstable_importDeclarationLocs: importDeclarationLocs,
|
|
32
|
+
};
|
|
33
|
+
} else {
|
|
34
|
+
metroMetadata.metro.unstable_importDeclarationLocs =
|
|
35
|
+
importDeclarationLocs;
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const MISSING_LOC = {
|
|
41
|
+
line: -1,
|
|
42
|
+
column: -1,
|
|
43
|
+
};
|
|
44
|
+
function locToKey(loc) {
|
|
45
|
+
const { start = MISSING_LOC, end = MISSING_LOC } = loc;
|
|
46
|
+
return `${start.line},${start.column}:${end.line},${end.column}`;
|
|
47
|
+
}
|
|
48
|
+
module.exports = {
|
|
49
|
+
importLocationsPlugin,
|
|
50
|
+
locToKey,
|
|
51
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @flow strict-local
|
|
8
|
+
* @format
|
|
9
|
+
* @oncall react_native
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
import type {PluginObj} from '@babel/core';
|
|
15
|
+
import typeof * as Types from '@babel/types';
|
|
16
|
+
import type {MetroBabelFileMetadata} from 'metro-babel-transformer';
|
|
17
|
+
|
|
18
|
+
const invariant = require('invariant');
|
|
19
|
+
|
|
20
|
+
type ImportDeclarationLocs = Set<string>;
|
|
21
|
+
|
|
22
|
+
function importLocationsPlugin({types: t}: {types: Types, ...}): PluginObj<> {
|
|
23
|
+
const importDeclarationLocs: ImportDeclarationLocs = new Set();
|
|
24
|
+
return {
|
|
25
|
+
visitor: {
|
|
26
|
+
ImportDeclaration(path) {
|
|
27
|
+
if (
|
|
28
|
+
// Ignore type imports
|
|
29
|
+
path.node.importKind !== 'type' &&
|
|
30
|
+
// loc may not be set if this plugin runs alongside others which
|
|
31
|
+
// inject imports - eg Babel runtime helpers. We don't regard these
|
|
32
|
+
// as source import declarations.
|
|
33
|
+
path.node.loc != null
|
|
34
|
+
) {
|
|
35
|
+
importDeclarationLocs.add(locToKey(path.node.loc));
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
ExportDeclaration(path) {
|
|
39
|
+
if (
|
|
40
|
+
// If `source` is set, this is a re-export, so it declares an ESM
|
|
41
|
+
// dependency.
|
|
42
|
+
path.node.source != null &&
|
|
43
|
+
// Ignore type exports
|
|
44
|
+
path.node.exportKind !== 'type' &&
|
|
45
|
+
// As above, ignore injected imports.
|
|
46
|
+
path.node.loc != null
|
|
47
|
+
) {
|
|
48
|
+
importDeclarationLocs.add(locToKey(path.node.loc));
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
pre: ({path, metadata}) => {
|
|
53
|
+
invariant(
|
|
54
|
+
path && t.isProgram(path.node),
|
|
55
|
+
'path missing or not a program node',
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// $FlowFixMe[prop-missing] Babel `File` is not generically typed
|
|
59
|
+
const metroMetadata: MetroBabelFileMetadata = metadata;
|
|
60
|
+
|
|
61
|
+
// Set the result on a metadata property
|
|
62
|
+
if (!metroMetadata.metro) {
|
|
63
|
+
metroMetadata.metro = {
|
|
64
|
+
unstable_importDeclarationLocs: importDeclarationLocs,
|
|
65
|
+
};
|
|
66
|
+
} else {
|
|
67
|
+
metroMetadata.metro.unstable_importDeclarationLocs =
|
|
68
|
+
importDeclarationLocs;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Very simple serialisation of a source location. This should remain opaque to
|
|
75
|
+
// the caller.
|
|
76
|
+
const MISSING_LOC = {line: -1, column: -1};
|
|
77
|
+
function locToKey(loc: BabelSourceLocation): string {
|
|
78
|
+
const {start = MISSING_LOC, end = MISSING_LOC} = loc;
|
|
79
|
+
return `${start.line},${start.column}:${end.line},${end.column}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = {importLocationsPlugin, locToKey};
|
package/src/Server.js
CHANGED
|
@@ -450,7 +450,7 @@ class Server {
|
|
|
450
450
|
const depGraph = await this._bundler.getBundler().getDependencyGraph();
|
|
451
451
|
const filePath = path.join(rootDir, relativePathname);
|
|
452
452
|
try {
|
|
453
|
-
depGraph.
|
|
453
|
+
await depGraph.getOrComputeSha1(filePath);
|
|
454
454
|
} catch {
|
|
455
455
|
res.writeHead(404);
|
|
456
456
|
res.end();
|
|
@@ -1107,6 +1107,7 @@ class Server {
|
|
|
1107
1107
|
key: filePath,
|
|
1108
1108
|
locs: [],
|
|
1109
1109
|
asyncType: null,
|
|
1110
|
+
isESMImport: false,
|
|
1110
1111
|
},
|
|
1111
1112
|
}).filePath;
|
|
1112
1113
|
}
|
package/src/Server.js.flow
CHANGED
|
@@ -630,7 +630,7 @@ class Server {
|
|
|
630
630
|
const depGraph = await this._bundler.getBundler().getDependencyGraph();
|
|
631
631
|
const filePath = path.join(rootDir, relativePathname);
|
|
632
632
|
try {
|
|
633
|
-
depGraph.
|
|
633
|
+
await depGraph.getOrComputeSha1(filePath);
|
|
634
634
|
} catch {
|
|
635
635
|
res.writeHead(404);
|
|
636
636
|
res.end();
|
|
@@ -1420,7 +1420,7 @@ class Server {
|
|
|
1420
1420
|
: this._config.projectRoot;
|
|
1421
1421
|
return resolutionFn(`${rootDir}/.`, {
|
|
1422
1422
|
name: filePath,
|
|
1423
|
-
data: {key: filePath, locs: [], asyncType: null},
|
|
1423
|
+
data: {key: filePath, locs: [], asyncType: null, isESMImport: false},
|
|
1424
1424
|
}).filePath;
|
|
1425
1425
|
}
|
|
1426
1426
|
|
|
@@ -33,6 +33,7 @@ class ModuleResolver {
|
|
|
33
33
|
data: {
|
|
34
34
|
key: this._options.emptyModulePath,
|
|
35
35
|
asyncType: null,
|
|
36
|
+
isESMImport: false,
|
|
36
37
|
locs: [],
|
|
37
38
|
},
|
|
38
39
|
},
|
|
@@ -80,6 +81,7 @@ class ModuleResolver {
|
|
|
80
81
|
doesFileExist,
|
|
81
82
|
extraNodeModules,
|
|
82
83
|
fileSystemLookup,
|
|
84
|
+
isESMImport: dependency.data.isESMImport,
|
|
83
85
|
mainFields,
|
|
84
86
|
nodeModulesPaths,
|
|
85
87
|
preferNativePlatform,
|
|
@@ -118,6 +118,7 @@ class ModuleResolver<TPackage: Packageish> {
|
|
|
118
118
|
data: {
|
|
119
119
|
key: this._options.emptyModulePath,
|
|
120
120
|
asyncType: null,
|
|
121
|
+
isESMImport: false,
|
|
121
122
|
locs: [],
|
|
122
123
|
},
|
|
123
124
|
},
|
|
@@ -165,6 +166,7 @@ class ModuleResolver<TPackage: Packageish> {
|
|
|
165
166
|
doesFileExist,
|
|
166
167
|
extraNodeModules,
|
|
167
168
|
fileSystemLookup,
|
|
169
|
+
isESMImport: dependency.data.isESMImport,
|
|
168
170
|
mainFields,
|
|
169
171
|
nodeModulesPaths,
|
|
170
172
|
preferNativePlatform,
|
|
@@ -34,7 +34,7 @@ export default class DependencyGraph extends EventEmitter {
|
|
|
34
34
|
): Promise<DependencyGraph>;
|
|
35
35
|
|
|
36
36
|
getAllFiles(): string[];
|
|
37
|
-
|
|
37
|
+
getOrComputeSha1(filename: string): Promise<{sha1: string; content?: Buffer}>;
|
|
38
38
|
getWatcher(): EventEmitter;
|
|
39
39
|
end(): void;
|
|
40
40
|
|
|
@@ -24,13 +24,6 @@ function getOrCreateMap(map, field) {
|
|
|
24
24
|
}
|
|
25
25
|
return subMap;
|
|
26
26
|
}
|
|
27
|
-
const missingSha1Error = (mixedPath) =>
|
|
28
|
-
new Error(`Failed to get the SHA-1 for: ${mixedPath}.
|
|
29
|
-
Potential causes:
|
|
30
|
-
1) The file is not watched. Ensure it is under the configured \`projectRoot\` or \`watchFolders\`.
|
|
31
|
-
2) Check \`blockList\` in your metro.config.js and make sure it isn't excluding the file path.
|
|
32
|
-
3) The file may have been deleted since it was resolved - try refreshing your app.
|
|
33
|
-
4) Otherwise, this is a bug in Metro or the configured resolver - please report it.`);
|
|
34
27
|
class DependencyGraph extends EventEmitter {
|
|
35
28
|
constructor(config, options) {
|
|
36
29
|
super();
|
|
@@ -183,17 +176,15 @@ class DependencyGraph extends EventEmitter {
|
|
|
183
176
|
getAllFiles() {
|
|
184
177
|
return nullthrows(this._fileSystem).getAllFiles();
|
|
185
178
|
}
|
|
186
|
-
|
|
187
|
-
const sha1 = this._fileSystem.getSha1(filename);
|
|
188
|
-
if (!sha1) {
|
|
189
|
-
throw missingSha1Error(filename);
|
|
190
|
-
}
|
|
191
|
-
return sha1;
|
|
192
|
-
}
|
|
193
|
-
async unstable_getOrComputeSha1(mixedPath) {
|
|
179
|
+
async getOrComputeSha1(mixedPath) {
|
|
194
180
|
const result = await this._fileSystem.getOrComputeSha1(mixedPath);
|
|
195
181
|
if (!result || !result.sha1) {
|
|
196
|
-
throw
|
|
182
|
+
throw new Error(`Failed to get the SHA-1 for: ${mixedPath}.
|
|
183
|
+
Potential causes:
|
|
184
|
+
1) The file is not watched. Ensure it is under the configured \`projectRoot\` or \`watchFolders\`.
|
|
185
|
+
2) Check \`blockList\` in your metro.config.js and make sure it isn't excluding the file path.
|
|
186
|
+
3) The file may have been deleted since it was resolved - try refreshing your app.
|
|
187
|
+
4) Otherwise, this is a bug in Metro or the configured resolver - please report it.`);
|
|
197
188
|
}
|
|
198
189
|
return result;
|
|
199
190
|
}
|
|
@@ -232,7 +223,8 @@ class DependencyGraph extends EventEmitter {
|
|
|
232
223
|
const resolverOptionsKey =
|
|
233
224
|
JSON.stringify(resolverOptions ?? {}, canonicalize) ?? "";
|
|
234
225
|
const originKey = isSensitiveToOriginFolder ? path.dirname(from) : "";
|
|
235
|
-
const targetKey =
|
|
226
|
+
const targetKey =
|
|
227
|
+
to + (dependency.data.isESMImport === true ? "\0esm" : "\0cjs");
|
|
236
228
|
const platformKey = platform ?? NULL_PLATFORM;
|
|
237
229
|
const mapByResolverOptions = this._resolutionCache;
|
|
238
230
|
const mapByOrigin = getOrCreateMap(
|
|
@@ -55,14 +55,6 @@ function getOrCreateMap<T>(
|
|
|
55
55
|
return subMap;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const missingSha1Error = (mixedPath: string) =>
|
|
59
|
-
new Error(`Failed to get the SHA-1 for: ${mixedPath}.
|
|
60
|
-
Potential causes:
|
|
61
|
-
1) The file is not watched. Ensure it is under the configured \`projectRoot\` or \`watchFolders\`.
|
|
62
|
-
2) Check \`blockList\` in your metro.config.js and make sure it isn't excluding the file path.
|
|
63
|
-
3) The file may have been deleted since it was resolved - try refreshing your app.
|
|
64
|
-
4) Otherwise, this is a bug in Metro or the configured resolver - please report it.`);
|
|
65
|
-
|
|
66
58
|
class DependencyGraph extends EventEmitter {
|
|
67
59
|
_config: ConfigT;
|
|
68
60
|
_haste: MetroFileMap;
|
|
@@ -264,23 +256,20 @@ class DependencyGraph extends EventEmitter {
|
|
|
264
256
|
return nullthrows(this._fileSystem).getAllFiles();
|
|
265
257
|
}
|
|
266
258
|
|
|
267
|
-
getSha1(filename: string): string {
|
|
268
|
-
const sha1 = this._fileSystem.getSha1(filename);
|
|
269
|
-
if (!sha1) {
|
|
270
|
-
throw missingSha1Error(filename);
|
|
271
|
-
}
|
|
272
|
-
return sha1;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
259
|
/**
|
|
276
260
|
* Used when watcher.unstable_lazySha1 is true
|
|
277
261
|
*/
|
|
278
|
-
async
|
|
262
|
+
async getOrComputeSha1(
|
|
279
263
|
mixedPath: string,
|
|
280
264
|
): Promise<{content?: Buffer, sha1: string}> {
|
|
281
265
|
const result = await this._fileSystem.getOrComputeSha1(mixedPath);
|
|
282
266
|
if (!result || !result.sha1) {
|
|
283
|
-
throw
|
|
267
|
+
throw new Error(`Failed to get the SHA-1 for: ${mixedPath}.
|
|
268
|
+
Potential causes:
|
|
269
|
+
1) The file is not watched. Ensure it is under the configured \`projectRoot\` or \`watchFolders\`.
|
|
270
|
+
2) Check \`blockList\` in your metro.config.js and make sure it isn't excluding the file path.
|
|
271
|
+
3) The file may have been deleted since it was resolved - try refreshing your app.
|
|
272
|
+
4) Otherwise, this is a bug in Metro or the configured resolver - please report it.`);
|
|
284
273
|
}
|
|
285
274
|
return result;
|
|
286
275
|
}
|
|
@@ -339,7 +328,8 @@ class DependencyGraph extends EventEmitter {
|
|
|
339
328
|
const resolverOptionsKey =
|
|
340
329
|
JSON.stringify(resolverOptions ?? {}, canonicalize) ?? '';
|
|
341
330
|
const originKey = isSensitiveToOriginFolder ? path.dirname(from) : '';
|
|
342
|
-
const targetKey =
|
|
331
|
+
const targetKey =
|
|
332
|
+
to + (dependency.data.isESMImport === true ? '\0esm' : '\0cjs');
|
|
343
333
|
const platformKey = platform ?? NULL_PLATFORM;
|
|
344
334
|
|
|
345
335
|
// Traverse the resolver cache, which is a tree of maps
|