metro 0.63.0 → 0.64.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 +22 -22
- package/src/Assets.js +8 -2
- package/src/Bundler/util.js +17 -10
- package/src/Bundler/util.js.flow +13 -12
- package/src/Bundler.js +4 -0
- package/src/DeltaBundler/DeltaCalculator.js +6 -1
- package/src/DeltaBundler/Serializers/baseBytecodeBundle.js +10 -0
- package/src/DeltaBundler/Serializers/baseJSBundle.js +5 -0
- package/src/DeltaBundler/Serializers/getAllFiles.js +6 -0
- package/src/DeltaBundler/Serializers/getAssets.js +4 -0
- package/src/DeltaBundler/Serializers/getRamBundleInfo.js +6 -0
- package/src/DeltaBundler/Serializers/helpers/bytecode.js +5 -0
- package/src/DeltaBundler/Serializers/helpers/getSourceMapInfo.js +5 -0
- package/src/DeltaBundler/Serializers/helpers/processBytecodeModules.js +2 -0
- package/src/DeltaBundler/Serializers/helpers/processModules.js +2 -0
- package/src/DeltaBundler/Serializers/hmrJSBundle.js +8 -2
- package/src/DeltaBundler/Serializers/sourceMapGenerator.js +4 -0
- package/src/DeltaBundler/Serializers/sourceMapObject.js +4 -0
- package/src/DeltaBundler/Transformer.js +16 -0
- package/src/DeltaBundler/Worker.js +4 -0
- package/src/DeltaBundler/WorkerFarm.js +9 -0
- package/src/DeltaBundler/computeDelta.js +5 -0
- package/src/DeltaBundler/getTransformCacheKey.js +1 -1
- package/src/DeltaBundler/mergeDeltas.js +5 -0
- package/src/DeltaBundler/traverseDependencies.js +14 -0
- package/src/DeltaBundler.js +6 -1
- package/src/HmrServer.js +18 -0
- package/src/IncrementalBundler.js +16 -0
- package/src/ModuleGraph/node-haste/Package.js +5 -0
- package/src/ModuleGraph/node-haste/node-haste.flow.js.flow +0 -2
- package/src/ModuleGraph/node-haste/node-haste.js +3 -0
- package/src/ModuleGraph/node-haste/node-haste.js.flow +4 -2
- package/src/ModuleGraph/output/indexed-ram-bundle.js +10 -0
- package/src/ModuleGraph/output/multiple-files-ram-bundle.js +10 -0
- package/src/ModuleGraph/output/reverse-dependency-map-references.js +33 -9
- package/src/ModuleGraph/output/reverse-dependency-map-references.js.flow +28 -13
- package/src/ModuleGraph/output/util.js +59 -25
- package/src/ModuleGraph/output/util.js.flow +59 -23
- package/src/ModuleGraph/test-helpers.js +74 -2
- package/src/ModuleGraph/types.flow.js.flow +13 -8
- package/src/ModuleGraph/worker/JsFileWrapping.js +90 -12
- package/src/ModuleGraph/worker/JsFileWrapping.js.flow +34 -20
- package/src/ModuleGraph/worker/collectDependencies.js +329 -205
- package/src/ModuleGraph/worker/collectDependencies.js.flow +397 -209
- package/src/ModuleGraph/worker/generate.js.flow +2 -2
- package/src/ModuleGraph/worker/generateImportNames.js +2 -1
- package/src/ModuleGraph/worker/generateImportNames.js.flow +3 -4
- package/src/ModuleGraph/worker/mergeSourceMaps.js +5 -0
- package/src/Server/symbolicate.js +14 -0
- package/src/Server.js +16 -3
- package/src/Server.js.flow +1 -0
- package/src/cli-utils.js +4 -0
- package/src/cli.js +0 -0
- package/src/commands/build.js +10 -0
- package/src/commands/build.js.flow +2 -0
- package/src/commands/dependencies.js +4 -0
- package/src/commands/serve.js +5 -0
- package/src/commands/serve.js.flow +2 -0
- package/src/index.js +13 -1
- package/src/index.js.flow +2 -0
- package/src/integration_tests/basic_bundle/import-export/index.js +19 -2
- package/src/integration_tests/execBundle.js +2 -2
- package/src/integration_tests/metro.config.js +4 -0
- package/src/lib/BatchProcessor.js +1 -1
- package/src/lib/TerminalReporter.js +10 -0
- package/src/lib/attachWebsocketServer.js +5 -1
- package/src/lib/bundleToBytecode.js +7 -3
- package/src/lib/bundleToString.js +6 -1
- package/src/lib/debounceAsyncQueue.js +4 -0
- package/src/lib/getAppendScripts.js +2 -0
- package/src/lib/getPreludeCode.js +2 -0
- package/src/lib/getPrependedScripts.js +11 -0
- package/src/lib/logToConsole.js +1 -2
- package/src/lib/parseOptionsFromUrl.js +5 -0
- package/src/lib/reporting.js +1 -1
- package/src/lib/splitBundleOptions.js +1 -1
- package/src/lib/transformHelpers.js +19 -3
- package/src/lib/transformHelpers.js.flow +4 -4
- package/src/node-haste/DependencyGraph/ModuleResolution.js +10 -2
- package/src/node-haste/DependencyGraph/ModuleResolution.js.flow +6 -5
- package/src/node-haste/DependencyGraph.js +11 -2
- package/src/node-haste/DependencyGraph.js.flow +5 -0
- package/src/node-haste/Package.js +2 -0
- package/src/shared/output/RamBundle/as-assets.js +1 -2
- package/src/shared/output/RamBundle/as-indexed-file.js +2 -0
- package/src/shared/output/RamBundle/util.js +2 -2
- package/src/shared/output/RamBundle.js +9 -0
- package/src/shared/output/bundle.js +9 -0
- package/src/ModuleGraph/worker/optimizeDependencies.js +0 -122
- package/src/ModuleGraph/worker/optimizeDependencies.js.flow +0 -128
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
|
+
const invariant = require('invariant');
|
|
13
14
|
const nullthrows = require('nullthrows');
|
|
14
15
|
|
|
15
16
|
const generate = require('@babel/generator').default;
|
|
@@ -17,101 +18,114 @@ const template = require('@babel/template').default;
|
|
|
17
18
|
const traverse = require('@babel/traverse').default;
|
|
18
19
|
const types = require('@babel/types');
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
const {isImport} = types;
|
|
22
|
+
|
|
23
|
+
import type {NodePath} from '@babel/traverse';
|
|
24
|
+
import type {CallExpression, Identifier, StringLiteral} from '@babel/types';
|
|
21
25
|
import type {
|
|
22
26
|
AllowOptionalDependencies,
|
|
23
27
|
AsyncDependencyType,
|
|
24
28
|
} from 'metro/src/DeltaBundler/types.flow.js';
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
+jsResource?: boolean,
|
|
32
|
-
+isOptional?: boolean,
|
|
33
|
-
|};
|
|
30
|
+
type ImportDependencyOptions = $ReadOnly<{
|
|
31
|
+
asyncType: AsyncDependencyType,
|
|
32
|
+
jsResource?: boolean,
|
|
33
|
+
splitCondition?: NodePath<>,
|
|
34
|
+
}>;
|
|
34
35
|
|
|
35
|
-
type
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
export type Dependency<TSplitCondition> = $ReadOnly<{
|
|
37
|
+
data: DependencyData<TSplitCondition>,
|
|
38
|
+
name: string,
|
|
39
|
+
}>;
|
|
39
40
|
|
|
40
|
-
type
|
|
41
|
+
type DependencyData<TSplitCondition> = $ReadOnly<{
|
|
42
|
+
// If null, then the dependency is synchronous.
|
|
43
|
+
// (ex. `require('foo')`)
|
|
41
44
|
asyncType: AsyncDependencyType | null,
|
|
42
45
|
isOptional?: boolean,
|
|
46
|
+
// If left unspecified, then the dependency is unconditionally split.
|
|
47
|
+
splitCondition?: TSplitCondition,
|
|
43
48
|
locs: Array<BabelSourceLocation>,
|
|
44
|
-
|
|
49
|
+
}>;
|
|
45
50
|
|
|
46
|
-
type
|
|
47
|
-
|
|
51
|
+
export type MutableInternalDependency<TSplitCondition> = {
|
|
52
|
+
...DependencyData<TSplitCondition>,
|
|
48
53
|
index: number,
|
|
49
|
-
|
|
54
|
+
name: string,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type InternalDependency<TSplitCondition> = $ReadOnly<
|
|
58
|
+
MutableInternalDependency<TSplitCondition>,
|
|
59
|
+
>;
|
|
50
60
|
|
|
51
|
-
type State = {
|
|
52
|
-
asyncRequireModulePathStringLiteral: ?
|
|
53
|
-
dependency: number,
|
|
61
|
+
export type State<TSplitCondition> = {
|
|
62
|
+
asyncRequireModulePathStringLiteral: ?StringLiteral,
|
|
54
63
|
dependencyCalls: Set<string>,
|
|
55
|
-
|
|
56
|
-
|
|
64
|
+
dependencyRegistry: ModuleDependencyRegistry<TSplitCondition>,
|
|
65
|
+
dependencyTransformer: DependencyTransformer<TSplitCondition>,
|
|
57
66
|
dynamicRequires: DynamicRequiresBehavior,
|
|
58
67
|
dependencyMapIdentifier: ?Identifier,
|
|
59
68
|
keepRequireNames: boolean,
|
|
60
|
-
disableRequiresTransform: boolean,
|
|
61
69
|
allowOptionalDependencies: AllowOptionalDependencies,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
export type Options = {|
|
|
65
|
-
+asyncRequireModulePath: string,
|
|
66
|
-
+dependencyMapName?: string,
|
|
67
|
-
+dynamicRequires: DynamicRequiresBehavior,
|
|
68
|
-
+inlineableCalls: $ReadOnlyArray<string>,
|
|
69
|
-
+keepRequireNames: boolean,
|
|
70
|
-
+disableRequiresTransform?: boolean,
|
|
71
|
-
+allowOptionalDependencies: AllowOptionalDependencies,
|
|
72
|
-
|};
|
|
73
|
-
|
|
74
|
-
export type CollectedDependencies = {|
|
|
75
|
-
+ast: Ast,
|
|
76
|
-
+dependencyMapName: string,
|
|
77
|
-
+dependencies: $ReadOnlyArray<Dependency>,
|
|
78
|
-
|};
|
|
70
|
+
};
|
|
79
71
|
|
|
80
|
-
export type
|
|
72
|
+
export type Options<TSplitCondition = void> = $ReadOnly<{
|
|
73
|
+
asyncRequireModulePath: string,
|
|
74
|
+
dependencyMapName?: string,
|
|
75
|
+
dynamicRequires: DynamicRequiresBehavior,
|
|
76
|
+
inlineableCalls: $ReadOnlyArray<string>,
|
|
77
|
+
keepRequireNames: boolean,
|
|
78
|
+
allowOptionalDependencies: AllowOptionalDependencies,
|
|
79
|
+
dependencyRegistry?: ModuleDependencyRegistry<TSplitCondition>,
|
|
80
|
+
dependencyTransformer?: DependencyTransformer<TSplitCondition>,
|
|
81
|
+
}>;
|
|
82
|
+
|
|
83
|
+
export type CollectedDependencies<+TSplitCondition> = $ReadOnly<{
|
|
84
|
+
ast: BabelNodeFile,
|
|
85
|
+
dependencyMapName: string,
|
|
86
|
+
dependencies: $ReadOnlyArray<Dependency<TSplitCondition>>,
|
|
87
|
+
}>;
|
|
88
|
+
|
|
89
|
+
// Registry for the dependency of a module.
|
|
90
|
+
// Defines when dependencies should be collapsed.
|
|
91
|
+
// E.g. should a module that's once required optinally and once not
|
|
92
|
+
// be tretaed as the smae or different dependencies.
|
|
93
|
+
export interface ModuleDependencyRegistry<+TSplitCondition> {
|
|
94
|
+
registerDependency(
|
|
95
|
+
qualifier: ImportQualifier,
|
|
96
|
+
): InternalDependency<TSplitCondition>;
|
|
97
|
+
getDependencies(): Array<InternalDependency<TSplitCondition>>;
|
|
98
|
+
}
|
|
81
99
|
|
|
82
|
-
export
|
|
100
|
+
export interface DependencyTransformer<-TSplitCondition> {
|
|
101
|
+
transformSyncRequire(
|
|
102
|
+
path: NodePath<CallExpression>,
|
|
103
|
+
dependency: InternalDependency<TSplitCondition>,
|
|
104
|
+
state: State<TSplitCondition>,
|
|
105
|
+
): void;
|
|
106
|
+
transformImportCall(
|
|
107
|
+
path: NodePath<>,
|
|
108
|
+
dependency: InternalDependency<TSplitCondition>,
|
|
109
|
+
state: State<TSplitCondition>,
|
|
110
|
+
): void;
|
|
111
|
+
transformJSResource(
|
|
112
|
+
path: NodePath<>,
|
|
113
|
+
dependency: InternalDependency<TSplitCondition>,
|
|
114
|
+
state: State<TSplitCondition>,
|
|
115
|
+
): void;
|
|
116
|
+
transformPrefetch(
|
|
117
|
+
path: NodePath<>,
|
|
118
|
+
dependency: InternalDependency<TSplitCondition>,
|
|
119
|
+
state: State<TSplitCondition>,
|
|
120
|
+
): void;
|
|
121
|
+
transformIllegalDynamicRequire(
|
|
122
|
+
path: NodePath<>,
|
|
123
|
+
state: State<TSplitCondition>,
|
|
124
|
+
): void;
|
|
125
|
+
}
|
|
83
126
|
|
|
84
127
|
export type DynamicRequiresBehavior = 'throwAtRuntime' | 'reject';
|
|
85
128
|
|
|
86
|
-
/**
|
|
87
|
-
* Produces a Babel template that will throw at runtime when the require call
|
|
88
|
-
* is reached. This makes dynamic require errors catchable by libraries that
|
|
89
|
-
* want to use them.
|
|
90
|
-
*/
|
|
91
|
-
const dynamicRequireErrorTemplate = template(`
|
|
92
|
-
(function(line) {
|
|
93
|
-
throw new Error(
|
|
94
|
-
'Dynamic require defined at line ' + line + '; not supported by Metro',
|
|
95
|
-
);
|
|
96
|
-
})(LINE)
|
|
97
|
-
`);
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Produces a Babel template that transforms an "import(...)" call into a
|
|
101
|
-
* "require(...)" call to the asyncRequire specified.
|
|
102
|
-
*/
|
|
103
|
-
const makeAsyncRequireTemplate = template(`
|
|
104
|
-
require(ASYNC_REQUIRE_MODULE_PATH)(MODULE_ID, MODULE_NAME)
|
|
105
|
-
`);
|
|
106
|
-
|
|
107
|
-
const makeAsyncPrefetchTemplate = template(`
|
|
108
|
-
require(ASYNC_REQUIRE_MODULE_PATH).prefetch(MODULE_ID, MODULE_NAME)
|
|
109
|
-
`);
|
|
110
|
-
|
|
111
|
-
const makeJSResourceTemplate = template(`
|
|
112
|
-
require(ASYNC_REQUIRE_MODULE_PATH).resource(MODULE_ID, MODULE_NAME)
|
|
113
|
-
`);
|
|
114
|
-
|
|
115
129
|
/**
|
|
116
130
|
* Transform all the calls to `require()` and `import()` in a file into ID-
|
|
117
131
|
* independent code, and return the list of dependencies. For example, a call
|
|
@@ -121,62 +135,78 @@ const makeJSResourceTemplate = template(`
|
|
|
121
135
|
*
|
|
122
136
|
* The second argument is only provided for debugging purposes.
|
|
123
137
|
*/
|
|
124
|
-
function collectDependencies(
|
|
125
|
-
ast:
|
|
126
|
-
options: Options
|
|
127
|
-
): CollectedDependencies {
|
|
138
|
+
function collectDependencies<TSplitCondition = void>(
|
|
139
|
+
ast: BabelNodeFile,
|
|
140
|
+
options: Options<TSplitCondition>,
|
|
141
|
+
): CollectedDependencies<TSplitCondition> {
|
|
128
142
|
const visited = new WeakSet();
|
|
129
143
|
|
|
130
|
-
const state: State = {
|
|
144
|
+
const state: State<TSplitCondition> = {
|
|
131
145
|
asyncRequireModulePathStringLiteral: null,
|
|
132
|
-
dependency: 0,
|
|
133
146
|
dependencyCalls: new Set(),
|
|
134
|
-
|
|
135
|
-
|
|
147
|
+
dependencyRegistry:
|
|
148
|
+
options.dependencyRegistry ?? new DefaultModuleDependencyRegistry(),
|
|
149
|
+
dependencyTransformer:
|
|
150
|
+
options.dependencyTransformer ?? DefaultDependencyTransformer,
|
|
136
151
|
dependencyMapIdentifier: null,
|
|
137
152
|
dynamicRequires: options.dynamicRequires,
|
|
138
153
|
keepRequireNames: options.keepRequireNames,
|
|
139
|
-
disableRequiresTransform: !!options.disableRequiresTransform,
|
|
140
154
|
allowOptionalDependencies: options.allowOptionalDependencies,
|
|
141
155
|
};
|
|
142
156
|
|
|
143
157
|
const visitor = {
|
|
144
|
-
CallExpression(path
|
|
158
|
+
CallExpression(path, state): void {
|
|
145
159
|
if (visited.has(path.node)) {
|
|
146
160
|
return;
|
|
147
161
|
}
|
|
148
162
|
|
|
149
|
-
const callee = path.
|
|
150
|
-
const name = callee.
|
|
163
|
+
const callee = path.node.callee;
|
|
164
|
+
const name = callee.type === 'Identifier' ? callee.name : null;
|
|
151
165
|
|
|
152
|
-
if (
|
|
166
|
+
if (isImport(callee)) {
|
|
153
167
|
processImportCall(path, state, {
|
|
154
|
-
|
|
168
|
+
asyncType: 'async',
|
|
155
169
|
});
|
|
156
170
|
return;
|
|
157
171
|
}
|
|
158
172
|
|
|
159
173
|
if (name === '__prefetchImport' && !path.scope.getBinding(name)) {
|
|
160
174
|
processImportCall(path, state, {
|
|
161
|
-
|
|
175
|
+
asyncType: 'prefetch',
|
|
176
|
+
});
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (name === '__jsResource' && !path.scope.getBinding(name)) {
|
|
181
|
+
processImportCall(path, state, {
|
|
182
|
+
asyncType: 'async',
|
|
183
|
+
jsResource: true,
|
|
162
184
|
});
|
|
163
185
|
return;
|
|
164
186
|
}
|
|
165
187
|
|
|
166
188
|
if (
|
|
167
|
-
|
|
168
|
-
name === '__conditionallySplitJSResource') &&
|
|
189
|
+
name === '__conditionallySplitJSResource' &&
|
|
169
190
|
!path.scope.getBinding(name)
|
|
170
191
|
) {
|
|
192
|
+
const args = path.get('arguments');
|
|
193
|
+
invariant(Array.isArray(args), 'Expected arguments to be an array');
|
|
194
|
+
|
|
171
195
|
processImportCall(path, state, {
|
|
172
|
-
|
|
196
|
+
asyncType: 'async',
|
|
173
197
|
jsResource: true,
|
|
198
|
+
splitCondition: args[1],
|
|
174
199
|
});
|
|
175
200
|
return;
|
|
176
201
|
}
|
|
177
202
|
|
|
178
|
-
if (
|
|
179
|
-
|
|
203
|
+
if (
|
|
204
|
+
name != null &&
|
|
205
|
+
state.dependencyCalls.has(name) &&
|
|
206
|
+
!path.scope.getBinding(name)
|
|
207
|
+
) {
|
|
208
|
+
processRequireCall(path, state);
|
|
209
|
+
visited.add(path.node);
|
|
180
210
|
}
|
|
181
211
|
},
|
|
182
212
|
|
|
@@ -184,7 +214,7 @@ function collectDependencies(
|
|
|
184
214
|
ExportNamedDeclaration: collectImports,
|
|
185
215
|
ExportAllDeclaration: collectImports,
|
|
186
216
|
|
|
187
|
-
Program(path
|
|
217
|
+
Program(path, state) {
|
|
188
218
|
state.asyncRequireModulePathStringLiteral = types.stringLiteral(
|
|
189
219
|
options.asyncRequireModulePath,
|
|
190
220
|
);
|
|
@@ -205,11 +235,15 @@ function collectDependencies(
|
|
|
205
235
|
|
|
206
236
|
traverse(ast, visitor, null, state);
|
|
207
237
|
|
|
238
|
+
const collectedDependencies = state.dependencyRegistry.getDependencies();
|
|
208
239
|
// Compute the list of dependencies.
|
|
209
|
-
const dependencies = new Array(
|
|
240
|
+
const dependencies = new Array(collectedDependencies.length);
|
|
210
241
|
|
|
211
|
-
for (const
|
|
212
|
-
dependencies[
|
|
242
|
+
for (const {index, name, ...dependencyData} of collectedDependencies) {
|
|
243
|
+
dependencies[index] = {
|
|
244
|
+
name,
|
|
245
|
+
data: dependencyData,
|
|
246
|
+
};
|
|
213
247
|
}
|
|
214
248
|
|
|
215
249
|
return {
|
|
@@ -219,165 +253,128 @@ function collectDependencies(
|
|
|
219
253
|
};
|
|
220
254
|
}
|
|
221
255
|
|
|
222
|
-
function collectImports(
|
|
256
|
+
function collectImports<TSplitCondition>(
|
|
257
|
+
path: NodePath<>,
|
|
258
|
+
state: State<TSplitCondition>,
|
|
259
|
+
): void {
|
|
223
260
|
if (path.node.source) {
|
|
224
|
-
|
|
261
|
+
registerDependency(
|
|
225
262
|
state,
|
|
226
|
-
path.node.source.value,
|
|
227
263
|
{
|
|
228
|
-
|
|
264
|
+
name: path.node.source.value,
|
|
265
|
+
asyncType: null,
|
|
266
|
+
optional: false,
|
|
229
267
|
},
|
|
230
268
|
path,
|
|
231
269
|
);
|
|
232
|
-
|
|
233
|
-
dep.data.asyncType = null;
|
|
234
270
|
}
|
|
235
271
|
}
|
|
236
272
|
|
|
237
|
-
function processImportCall(
|
|
273
|
+
function processImportCall<TSplitCondition>(
|
|
274
|
+
path: NodePath<CallExpression>,
|
|
275
|
+
state: State<TSplitCondition>,
|
|
276
|
+
options: ImportDependencyOptions,
|
|
277
|
+
): void {
|
|
238
278
|
const name = getModuleNameFromCallArgs(path);
|
|
239
279
|
|
|
240
280
|
if (name == null) {
|
|
241
281
|
throw new InvalidRequireCallError(path);
|
|
242
282
|
}
|
|
243
283
|
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
return path;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const ASYNC_REQUIRE_MODULE_PATH = state.asyncRequireModulePathStringLiteral;
|
|
257
|
-
const MODULE_ID = types.memberExpression(
|
|
258
|
-
state.dependencyMapIdentifier,
|
|
259
|
-
types.numericLiteral(dep.index),
|
|
260
|
-
true,
|
|
284
|
+
const dep = registerDependency(
|
|
285
|
+
state,
|
|
286
|
+
{
|
|
287
|
+
name,
|
|
288
|
+
asyncType: options.asyncType,
|
|
289
|
+
splitCondition: options.splitCondition,
|
|
290
|
+
optional: isOptionalDependency(name, path, state),
|
|
291
|
+
},
|
|
292
|
+
path,
|
|
261
293
|
);
|
|
262
|
-
|
|
294
|
+
|
|
295
|
+
const transformer = state.dependencyTransformer;
|
|
263
296
|
|
|
264
297
|
if (options.jsResource) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
MODULE_ID,
|
|
269
|
-
MODULE_NAME,
|
|
270
|
-
}),
|
|
271
|
-
);
|
|
272
|
-
} else if (!options.prefetchOnly) {
|
|
273
|
-
path.replaceWith(
|
|
274
|
-
makeAsyncRequireTemplate({
|
|
275
|
-
ASYNC_REQUIRE_MODULE_PATH,
|
|
276
|
-
MODULE_ID,
|
|
277
|
-
MODULE_NAME,
|
|
278
|
-
}),
|
|
279
|
-
);
|
|
298
|
+
transformer.transformJSResource(path, dep, state);
|
|
299
|
+
} else if (options.asyncType === 'async') {
|
|
300
|
+
transformer.transformImportCall(path, dep, state);
|
|
280
301
|
} else {
|
|
281
|
-
|
|
282
|
-
makeAsyncPrefetchTemplate({
|
|
283
|
-
ASYNC_REQUIRE_MODULE_PATH,
|
|
284
|
-
MODULE_ID,
|
|
285
|
-
MODULE_NAME,
|
|
286
|
-
}),
|
|
287
|
-
);
|
|
302
|
+
transformer.transformPrefetch(path, dep, state);
|
|
288
303
|
}
|
|
289
|
-
|
|
290
|
-
return path;
|
|
291
304
|
}
|
|
292
305
|
|
|
293
|
-
function processRequireCall(
|
|
306
|
+
function processRequireCall<TSplitCondition>(
|
|
307
|
+
path: NodePath<CallExpression>,
|
|
308
|
+
state: State<TSplitCondition>,
|
|
309
|
+
): void {
|
|
294
310
|
const name = getModuleNameFromCallArgs(path);
|
|
295
311
|
|
|
312
|
+
const transformer = state.dependencyTransformer;
|
|
313
|
+
|
|
296
314
|
if (name == null) {
|
|
297
315
|
if (state.dynamicRequires === 'reject') {
|
|
298
316
|
throw new InvalidRequireCallError(path);
|
|
299
317
|
}
|
|
300
318
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
LINE: '' + path.node.loc.start.line,
|
|
304
|
-
}),
|
|
305
|
-
);
|
|
306
|
-
return path;
|
|
319
|
+
transformer.transformIllegalDynamicRequire(path, state);
|
|
320
|
+
return;
|
|
307
321
|
}
|
|
308
322
|
|
|
309
|
-
const dep =
|
|
323
|
+
const dep = registerDependency(
|
|
310
324
|
state,
|
|
311
|
-
|
|
312
|
-
|
|
325
|
+
{
|
|
326
|
+
name,
|
|
327
|
+
asyncType: null,
|
|
328
|
+
optional: isOptionalDependency(name, path, state),
|
|
329
|
+
},
|
|
313
330
|
path,
|
|
314
331
|
);
|
|
315
|
-
dep.data.asyncType = null;
|
|
316
|
-
|
|
317
|
-
if (state.disableRequiresTransform) {
|
|
318
|
-
return path;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const moduleIDExpression = types.memberExpression(
|
|
322
|
-
state.dependencyMapIdentifier,
|
|
323
|
-
types.numericLiteral(dep.index),
|
|
324
|
-
true,
|
|
325
|
-
);
|
|
326
|
-
|
|
327
|
-
path.node.arguments = state.keepRequireNames
|
|
328
|
-
? [moduleIDExpression, types.stringLiteral(name)]
|
|
329
|
-
: [moduleIDExpression];
|
|
330
332
|
|
|
331
|
-
|
|
333
|
+
transformer.transformSyncRequire(path, dep, state);
|
|
332
334
|
}
|
|
333
335
|
|
|
334
|
-
function getNearestLocFromPath(path:
|
|
335
|
-
|
|
336
|
-
|
|
336
|
+
function getNearestLocFromPath(path: NodePath<>): ?BabelSourceLocation {
|
|
337
|
+
let current = path;
|
|
338
|
+
while (current && !current.node.loc) {
|
|
339
|
+
current = current.parentPath;
|
|
337
340
|
}
|
|
338
|
-
return
|
|
341
|
+
return current?.node.loc;
|
|
339
342
|
}
|
|
340
343
|
|
|
341
|
-
|
|
342
|
-
state: State,
|
|
344
|
+
export type ImportQualifier = $ReadOnly<{
|
|
343
345
|
name: string,
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
let index = state.dependencyIndexes.get(name);
|
|
349
|
-
let data: ?InternalDependencyData = state.dependencyData.get(name);
|
|
350
|
-
|
|
351
|
-
if (!data) {
|
|
352
|
-
index = state.dependency++;
|
|
353
|
-
data = {asyncType: 'async', locs: []};
|
|
354
|
-
|
|
355
|
-
if (options.prefetchOnly) {
|
|
356
|
-
data.asyncType = 'prefetch';
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
if (options.isOptional) {
|
|
360
|
-
data.isOptional = true;
|
|
361
|
-
}
|
|
346
|
+
asyncType: AsyncDependencyType | null,
|
|
347
|
+
splitCondition?: NodePath<>,
|
|
348
|
+
optional: boolean,
|
|
349
|
+
}>;
|
|
362
350
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
351
|
+
function registerDependency<TSplitCondition>(
|
|
352
|
+
state: State<TSplitCondition>,
|
|
353
|
+
qualifier: ImportQualifier,
|
|
354
|
+
path: NodePath<>,
|
|
355
|
+
): InternalDependency<TSplitCondition> {
|
|
356
|
+
const dependency = state.dependencyRegistry.registerDependency(qualifier);
|
|
366
357
|
|
|
358
|
+
const loc = getNearestLocFromPath(path);
|
|
367
359
|
if (loc != null) {
|
|
368
|
-
|
|
360
|
+
dependency.locs.push(loc);
|
|
369
361
|
}
|
|
370
362
|
|
|
371
|
-
return
|
|
363
|
+
return dependency;
|
|
372
364
|
}
|
|
373
365
|
|
|
374
|
-
|
|
366
|
+
function isOptionalDependency<TSplitCondition>(
|
|
375
367
|
name: string,
|
|
376
|
-
path:
|
|
377
|
-
state: State
|
|
378
|
-
): boolean
|
|
368
|
+
path: NodePath<>,
|
|
369
|
+
state: State<TSplitCondition>,
|
|
370
|
+
): boolean {
|
|
379
371
|
const {allowOptionalDependencies} = state;
|
|
380
372
|
|
|
373
|
+
// The async require module is a 'built-in'. Resolving should never fail -> treat it as non-optional.
|
|
374
|
+
if (name === state.asyncRequireModulePathStringLiteral?.value) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
|
|
381
378
|
const isExcluded = () =>
|
|
382
379
|
Array.isArray(allowOptionalDependencies.exclude) &&
|
|
383
380
|
allowOptionalDependencies.exclude.includes(name);
|
|
@@ -394,7 +391,11 @@ const isOptionalDependency = (
|
|
|
394
391
|
if (p.node.type === 'BlockStatement') {
|
|
395
392
|
// A single-level should have the tryStatement immediately followed BlockStatement
|
|
396
393
|
// with the key 'block' to distinguish from the finally block, which has key = 'finalizer'
|
|
397
|
-
return
|
|
394
|
+
return (
|
|
395
|
+
p.parentPath != null &&
|
|
396
|
+
p.parentPath.node.type === 'TryStatement' &&
|
|
397
|
+
p.key === 'block'
|
|
398
|
+
);
|
|
398
399
|
}
|
|
399
400
|
sCount += 1;
|
|
400
401
|
}
|
|
@@ -402,16 +403,17 @@ const isOptionalDependency = (
|
|
|
402
403
|
}
|
|
403
404
|
|
|
404
405
|
return false;
|
|
405
|
-
}
|
|
406
|
+
}
|
|
406
407
|
|
|
407
|
-
function getModuleNameFromCallArgs(path:
|
|
408
|
+
function getModuleNameFromCallArgs(path: NodePath<CallExpression>): ?string {
|
|
408
409
|
const expectedCount =
|
|
409
410
|
path.node.callee.name === '__conditionallySplitJSResource' ? 2 : 1;
|
|
410
|
-
|
|
411
|
+
const args = path.get('arguments');
|
|
412
|
+
if (!Array.isArray(args) || args.length !== expectedCount) {
|
|
411
413
|
throw new InvalidRequireCallError(path);
|
|
412
414
|
}
|
|
413
415
|
|
|
414
|
-
const result =
|
|
416
|
+
const result = args[0].evaluate();
|
|
415
417
|
|
|
416
418
|
if (result.confident && typeof result.value === 'string') {
|
|
417
419
|
return result.value;
|
|
@@ -433,4 +435,190 @@ class InvalidRequireCallError extends Error {
|
|
|
433
435
|
|
|
434
436
|
collectDependencies.InvalidRequireCallError = InvalidRequireCallError;
|
|
435
437
|
|
|
438
|
+
/**
|
|
439
|
+
* Produces a Babel template that will throw at runtime when the require call
|
|
440
|
+
* is reached. This makes dynamic require errors catchable by libraries that
|
|
441
|
+
* want to use them.
|
|
442
|
+
*/
|
|
443
|
+
const dynamicRequireErrorTemplate = template.statement(`
|
|
444
|
+
(function(line) {
|
|
445
|
+
throw new Error(
|
|
446
|
+
'Dynamic require defined at line ' + line + '; not supported by Metro',
|
|
447
|
+
);
|
|
448
|
+
})(LINE)
|
|
449
|
+
`);
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Produces a Babel template that transforms an "import(...)" call into a
|
|
453
|
+
* "require(...)" call to the asyncRequire specified.
|
|
454
|
+
*/
|
|
455
|
+
const makeAsyncRequireTemplate = template.statement(`
|
|
456
|
+
require(ASYNC_REQUIRE_MODULE_PATH)(MODULE_ID, MODULE_NAME)
|
|
457
|
+
`);
|
|
458
|
+
|
|
459
|
+
const makeAsyncPrefetchTemplate = template.statement(`
|
|
460
|
+
require(ASYNC_REQUIRE_MODULE_PATH).prefetch(MODULE_ID, MODULE_NAME)
|
|
461
|
+
`);
|
|
462
|
+
|
|
463
|
+
const makeJSResourceTemplate = template.statement(`
|
|
464
|
+
require(ASYNC_REQUIRE_MODULE_PATH).resource(MODULE_ID, MODULE_NAME)
|
|
465
|
+
`);
|
|
466
|
+
|
|
467
|
+
const DefaultDependencyTransformer: DependencyTransformer<mixed> = {
|
|
468
|
+
transformSyncRequire(
|
|
469
|
+
path: NodePath<CallExpression>,
|
|
470
|
+
dependency: InternalDependency<mixed>,
|
|
471
|
+
state: State<mixed>,
|
|
472
|
+
): void {
|
|
473
|
+
const moduleIDExpression = createModuleIDExpression(dependency, state);
|
|
474
|
+
path.node.arguments = state.keepRequireNames
|
|
475
|
+
? [moduleIDExpression, types.stringLiteral(dependency.name)]
|
|
476
|
+
: [moduleIDExpression];
|
|
477
|
+
},
|
|
478
|
+
|
|
479
|
+
transformImportCall(
|
|
480
|
+
path: NodePath<>,
|
|
481
|
+
dependency: InternalDependency<mixed>,
|
|
482
|
+
state: State<mixed>,
|
|
483
|
+
): void {
|
|
484
|
+
path.replaceWith(
|
|
485
|
+
makeAsyncRequireTemplate({
|
|
486
|
+
ASYNC_REQUIRE_MODULE_PATH: nullthrows(
|
|
487
|
+
state.asyncRequireModulePathStringLiteral,
|
|
488
|
+
),
|
|
489
|
+
MODULE_ID: createModuleIDExpression(dependency, state),
|
|
490
|
+
MODULE_NAME: createModuleNameLiteral(dependency),
|
|
491
|
+
}),
|
|
492
|
+
);
|
|
493
|
+
},
|
|
494
|
+
|
|
495
|
+
transformJSResource(
|
|
496
|
+
path: NodePath<>,
|
|
497
|
+
dependency: InternalDependency<mixed>,
|
|
498
|
+
state: State<mixed>,
|
|
499
|
+
): void {
|
|
500
|
+
path.replaceWith(
|
|
501
|
+
makeJSResourceTemplate({
|
|
502
|
+
ASYNC_REQUIRE_MODULE_PATH: nullthrows(
|
|
503
|
+
state.asyncRequireModulePathStringLiteral,
|
|
504
|
+
),
|
|
505
|
+
MODULE_ID: createModuleIDExpression(dependency, state),
|
|
506
|
+
MODULE_NAME: createModuleNameLiteral(dependency),
|
|
507
|
+
}),
|
|
508
|
+
);
|
|
509
|
+
},
|
|
510
|
+
|
|
511
|
+
transformPrefetch(
|
|
512
|
+
path: NodePath<>,
|
|
513
|
+
dependency: InternalDependency<mixed>,
|
|
514
|
+
state: State<mixed>,
|
|
515
|
+
): void {
|
|
516
|
+
path.replaceWith(
|
|
517
|
+
makeAsyncPrefetchTemplate({
|
|
518
|
+
ASYNC_REQUIRE_MODULE_PATH: nullthrows(
|
|
519
|
+
state.asyncRequireModulePathStringLiteral,
|
|
520
|
+
),
|
|
521
|
+
MODULE_ID: createModuleIDExpression(dependency, state),
|
|
522
|
+
MODULE_NAME: createModuleNameLiteral(dependency),
|
|
523
|
+
}),
|
|
524
|
+
);
|
|
525
|
+
},
|
|
526
|
+
|
|
527
|
+
transformIllegalDynamicRequire(path: NodePath<>, state: State<mixed>): void {
|
|
528
|
+
path.replaceWith(
|
|
529
|
+
dynamicRequireErrorTemplate({
|
|
530
|
+
LINE: types.numericLiteral(path.node.loc?.start.line ?? 0),
|
|
531
|
+
}),
|
|
532
|
+
);
|
|
533
|
+
},
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
function createModuleIDExpression(
|
|
537
|
+
dependency: InternalDependency<mixed>,
|
|
538
|
+
state: State<mixed>,
|
|
539
|
+
) {
|
|
540
|
+
return types.memberExpression(
|
|
541
|
+
nullthrows(state.dependencyMapIdentifier),
|
|
542
|
+
types.numericLiteral(dependency.index),
|
|
543
|
+
true,
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function createModuleNameLiteral(dependency: InternalDependency<mixed>) {
|
|
548
|
+
return types.stringLiteral(dependency.name);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
class DefaultModuleDependencyRegistry<TSplitCondition = void>
|
|
552
|
+
implements ModuleDependencyRegistry<TSplitCondition> {
|
|
553
|
+
_dependencies: Map<string, InternalDependency<TSplitCondition>> = new Map();
|
|
554
|
+
|
|
555
|
+
registerDependency(
|
|
556
|
+
qualifier: ImportQualifier,
|
|
557
|
+
): InternalDependency<TSplitCondition> {
|
|
558
|
+
let dependency: ?InternalDependency<TSplitCondition> = this._dependencies.get(
|
|
559
|
+
qualifier.name,
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
if (dependency == null) {
|
|
563
|
+
const newDependency: MutableInternalDependency<TSplitCondition> = {
|
|
564
|
+
name: qualifier.name,
|
|
565
|
+
asyncType: qualifier.asyncType,
|
|
566
|
+
locs: [],
|
|
567
|
+
index: this._dependencies.size,
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
if (qualifier.optional) {
|
|
571
|
+
newDependency.isOptional = true;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
dependency = newDependency;
|
|
575
|
+
this._dependencies.set(qualifier.name, dependency);
|
|
576
|
+
} else {
|
|
577
|
+
const original = dependency;
|
|
578
|
+
dependency = collapseDependencies(original, qualifier);
|
|
579
|
+
if (original !== dependency) {
|
|
580
|
+
this._dependencies.set(qualifier.name, dependency);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
return dependency;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
getDependencies(): Array<InternalDependency<TSplitCondition>> {
|
|
588
|
+
return Array.from(this._dependencies.values());
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function collapseDependencies<TSplitCondition>(
|
|
593
|
+
dependency: InternalDependency<TSplitCondition>,
|
|
594
|
+
qualifier: ImportQualifier,
|
|
595
|
+
): InternalDependency<TSplitCondition> {
|
|
596
|
+
let collapsed = dependency;
|
|
597
|
+
|
|
598
|
+
// A previously optionally required dependency was required non-optionaly.
|
|
599
|
+
// Mark it non optional for the whole module
|
|
600
|
+
if (collapsed.isOptional && !qualifier.optional) {
|
|
601
|
+
collapsed = {
|
|
602
|
+
...dependency,
|
|
603
|
+
isOptional: false,
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// A previously asynchronously (or prefetch) required module was required synchronously.
|
|
608
|
+
// Make the dependency sync.
|
|
609
|
+
if (collapsed.asyncType != null && qualifier.asyncType == null) {
|
|
610
|
+
collapsed = {...dependency, asyncType: null};
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// A prefetched dependency was required async in the module. Mark it as async.
|
|
614
|
+
if (collapsed.asyncType === 'prefetch' && qualifier.asyncType === 'async') {
|
|
615
|
+
collapsed = {
|
|
616
|
+
...dependency,
|
|
617
|
+
asyncType: 'async',
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
return collapsed;
|
|
622
|
+
}
|
|
623
|
+
|
|
436
624
|
module.exports = collectDependencies;
|