metro 0.76.4 → 0.76.6
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 +24 -23
- package/src/Assets.js +35 -3
- package/src/Assets.js.flow +38 -3
- package/src/Bundler/util.js +0 -96
- package/src/Bundler/util.js.flow +0 -124
- package/src/DeltaBundler/DeltaCalculator.js +7 -0
- package/src/DeltaBundler/DeltaCalculator.js.flow +7 -0
- package/src/DeltaBundler/Graph.js +86 -31
- package/src/DeltaBundler/Graph.js.flow +98 -31
- package/src/DeltaBundler/Serializers/helpers/js.js +4 -1
- package/src/DeltaBundler/Serializers/helpers/js.js.flow +4 -1
- package/src/DeltaBundler/Serializers/hmrJSBundle.js +2 -1
- package/src/DeltaBundler/Serializers/hmrJSBundle.js.flow +2 -1
- package/src/DeltaBundler/Transformer.js +1 -0
- package/src/DeltaBundler/Transformer.js.flow +1 -0
- package/src/DeltaBundler/types.flow.js.flow +9 -7
- package/src/Server.js +32 -14
- package/src/Server.js.flow +45 -20
- package/src/index.d.ts +3 -1
- package/src/index.flow.js +5 -0
- package/src/index.flow.js.flow +8 -0
- package/src/lib/parseOptionsFromUrl.js +4 -3
- package/src/lib/parseOptionsFromUrl.js.flow +4 -3
|
@@ -137,16 +137,23 @@ class Graph {
|
|
|
137
137
|
|
|
138
138
|
// Record the paths that are part of the dependency graph before we start
|
|
139
139
|
// traversing - we'll use this to ensure we don't report modules modified
|
|
140
|
-
// that only exist as part of the graph mid-traversal
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
// that only exist as part of the graph mid-traversal, and to eliminate
|
|
141
|
+
// modules that end up in the same state that they started from the delta.
|
|
142
|
+
const originalModules = new Map();
|
|
143
|
+
for (const path of paths) {
|
|
144
|
+
const originalModule = this.dependencies.get(path);
|
|
145
|
+
if (originalModule) {
|
|
146
|
+
originalModules.set(path, originalModule);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const [path] of originalModules) {
|
|
143
150
|
// Traverse over modules that are part of the dependency graph.
|
|
144
151
|
//
|
|
145
152
|
// Note: A given path may not be part of the graph *at this time*, in
|
|
146
153
|
// particular it may have been removed since we started traversing, but
|
|
147
154
|
// in that case the path will be visited if and when we add it back to
|
|
148
155
|
// the graph in a subsequent iteration.
|
|
149
|
-
if (this.dependencies.
|
|
156
|
+
if (this.dependencies.has(path)) {
|
|
150
157
|
await this._traverseDependenciesForSingleFile(
|
|
151
158
|
path,
|
|
152
159
|
delta,
|
|
@@ -165,17 +172,35 @@ class Graph {
|
|
|
165
172
|
// but may not actually differ, may be new, or may have been deleted after
|
|
166
173
|
// processing. The actually-modified modules are the intersection of
|
|
167
174
|
// delta.modified with the pre-existing paths, minus modules deleted.
|
|
168
|
-
for (const path of
|
|
175
|
+
for (const [path, originalModule] of originalModules) {
|
|
169
176
|
invariant(
|
|
170
177
|
!delta.added.has(path),
|
|
171
178
|
"delta.added has %s, but this path was already in the graph.",
|
|
172
179
|
path
|
|
173
180
|
);
|
|
174
181
|
if (delta.modified.has(path)) {
|
|
175
|
-
//
|
|
176
|
-
//
|
|
182
|
+
// It's expected that a module may be both modified and subsequently
|
|
183
|
+
// deleted - we'll only return it as deleted.
|
|
177
184
|
if (!delta.deleted.has(path)) {
|
|
178
|
-
|
|
185
|
+
// If a module existed before and has not been deleted, it must be
|
|
186
|
+
// in the dependencies map.
|
|
187
|
+
const newModule = nullthrows(this.dependencies.get(path));
|
|
188
|
+
if (
|
|
189
|
+
// Module.dependencies is mutable, so it's not obviously the case
|
|
190
|
+
// that referential equality implies no modification. However, we
|
|
191
|
+
// only mutate dependencies in two cases:
|
|
192
|
+
// 1. Within _processModule. In that case, we always mutate a new
|
|
193
|
+
// module and set a new reference in this.dependencies.
|
|
194
|
+
// 2. During _releaseModule, when recursively removing
|
|
195
|
+
// dependencies. In that case, we immediately discard the module
|
|
196
|
+
// object.
|
|
197
|
+
// TODO: Refactor for more explicit immutability
|
|
198
|
+
newModule !== originalModule ||
|
|
199
|
+
transfromOutputMayDiffer(newModule, originalModule) ||
|
|
200
|
+
!allDependenciesEqual(newModule, originalModule)
|
|
201
|
+
) {
|
|
202
|
+
modified.set(path, newModule);
|
|
203
|
+
}
|
|
179
204
|
}
|
|
180
205
|
}
|
|
181
206
|
}
|
|
@@ -226,10 +251,6 @@ class Graph {
|
|
|
226
251
|
async _processModule(path, delta, options) {
|
|
227
252
|
const resolvedContext = this.#resolvedContexts.get(path);
|
|
228
253
|
|
|
229
|
-
// Mark any module processed as potentially modified. Once we've finished
|
|
230
|
-
// traversing we'll filter this set down.
|
|
231
|
-
delta.modified.add(path);
|
|
232
|
-
|
|
233
254
|
// Transform the file via the given option.
|
|
234
255
|
// TODO: Unbind the transform method from options
|
|
235
256
|
const result = await options.transform(path, resolvedContext);
|
|
@@ -241,47 +262,63 @@ class Graph {
|
|
|
241
262
|
result.dependencies,
|
|
242
263
|
options
|
|
243
264
|
);
|
|
244
|
-
const previousModule = this.dependencies.get(path)
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
...previousModule,
|
|
265
|
+
const previousModule = this.dependencies.get(path);
|
|
266
|
+
const previousDependencies = previousModule?.dependencies ?? new Map();
|
|
267
|
+
const nextModule = {
|
|
268
|
+
...(previousModule ?? {
|
|
269
|
+
inverseDependencies:
|
|
270
|
+
delta.earlyInverseDependencies.get(path) ??
|
|
271
|
+
new _CountingSet.default(),
|
|
272
|
+
path,
|
|
273
|
+
}),
|
|
254
274
|
dependencies: new Map(previousDependencies),
|
|
255
275
|
getSource: result.getSource,
|
|
256
276
|
output: result.output,
|
|
277
|
+
unstable_transformResultKey: result.unstable_transformResultKey,
|
|
257
278
|
};
|
|
258
|
-
|
|
279
|
+
|
|
280
|
+
// Update the module information.
|
|
281
|
+
this.dependencies.set(nextModule.path, nextModule);
|
|
259
282
|
|
|
260
283
|
// Diff dependencies (1/2): remove dependencies that have changed or been removed.
|
|
284
|
+
let dependenciesRemoved = false;
|
|
261
285
|
for (const [key, prevDependency] of previousDependencies) {
|
|
262
286
|
const curDependency = currentDependencies.get(key);
|
|
263
287
|
if (
|
|
264
288
|
!curDependency ||
|
|
265
289
|
!dependenciesEqual(prevDependency, curDependency, options)
|
|
266
290
|
) {
|
|
267
|
-
|
|
291
|
+
dependenciesRemoved = true;
|
|
292
|
+
this._removeDependency(nextModule, key, prevDependency, delta, options);
|
|
268
293
|
}
|
|
269
294
|
}
|
|
270
295
|
|
|
271
296
|
// Diff dependencies (2/2): add dependencies that have changed or been added.
|
|
272
|
-
const
|
|
297
|
+
const addDependencyPromises = [];
|
|
273
298
|
for (const [key, curDependency] of currentDependencies) {
|
|
274
299
|
const prevDependency = previousDependencies.get(key);
|
|
275
300
|
if (
|
|
276
301
|
!prevDependency ||
|
|
277
302
|
!dependenciesEqual(prevDependency, curDependency, options)
|
|
278
303
|
) {
|
|
279
|
-
|
|
280
|
-
this._addDependency(
|
|
304
|
+
addDependencyPromises.push(
|
|
305
|
+
this._addDependency(nextModule, key, curDependency, delta, options)
|
|
281
306
|
);
|
|
282
307
|
}
|
|
283
308
|
}
|
|
284
|
-
|
|
309
|
+
if (
|
|
310
|
+
previousModule &&
|
|
311
|
+
!transfromOutputMayDiffer(previousModule, nextModule) &&
|
|
312
|
+
!dependenciesRemoved &&
|
|
313
|
+
addDependencyPromises.length === 0
|
|
314
|
+
) {
|
|
315
|
+
// We have not operated on nextModule, so restore previousModule
|
|
316
|
+
// to aid diffing.
|
|
317
|
+
this.dependencies.set(previousModule.path, previousModule);
|
|
318
|
+
return previousModule;
|
|
319
|
+
}
|
|
320
|
+
delta.modified.add(path);
|
|
321
|
+
await Promise.all(addDependencyPromises);
|
|
285
322
|
|
|
286
323
|
// Replace dependencies with the correctly-ordered version. As long as all
|
|
287
324
|
// the above promises have resolved, this will be the same map but without
|
|
@@ -291,11 +328,11 @@ class Graph {
|
|
|
291
328
|
|
|
292
329
|
// Catch obvious errors with a cheap assertion.
|
|
293
330
|
invariant(
|
|
294
|
-
|
|
331
|
+
nextModule.dependencies.size === currentDependencies.size,
|
|
295
332
|
"Failed to add the correct dependencies"
|
|
296
333
|
);
|
|
297
|
-
|
|
298
|
-
return
|
|
334
|
+
nextModule.dependencies = currentDependencies;
|
|
335
|
+
return nextModule;
|
|
299
336
|
}
|
|
300
337
|
async _addDependency(parentModule, key, dependency, delta, options) {
|
|
301
338
|
const path = dependency.absolutePath;
|
|
@@ -686,6 +723,18 @@ function dependenciesEqual(a, b, options) {
|
|
|
686
723
|
contextParamsEqual(a.data.data.contextParams, b.data.data.contextParams))
|
|
687
724
|
);
|
|
688
725
|
}
|
|
726
|
+
function allDependenciesEqual(a, b, options) {
|
|
727
|
+
if (a.dependencies.size !== b.dependencies.size) {
|
|
728
|
+
return false;
|
|
729
|
+
}
|
|
730
|
+
for (const [key, depA] of a.dependencies) {
|
|
731
|
+
const depB = b.dependencies.get(key);
|
|
732
|
+
if (!depB || !dependenciesEqual(depA, depB, options)) {
|
|
733
|
+
return false;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
return true;
|
|
737
|
+
}
|
|
689
738
|
function contextParamsEqual(a, b) {
|
|
690
739
|
return (
|
|
691
740
|
a === b ||
|
|
@@ -698,3 +747,9 @@ function contextParamsEqual(a, b) {
|
|
|
698
747
|
a.mode === b.mode)
|
|
699
748
|
);
|
|
700
749
|
}
|
|
750
|
+
function transfromOutputMayDiffer(a, b) {
|
|
751
|
+
return (
|
|
752
|
+
a.unstable_transformResultKey == null ||
|
|
753
|
+
a.unstable_transformResultKey !== b.unstable_transformResultKey
|
|
754
|
+
);
|
|
755
|
+
}
|
|
@@ -176,17 +176,24 @@ export class Graph<T = MixedOutput> {
|
|
|
176
176
|
|
|
177
177
|
// Record the paths that are part of the dependency graph before we start
|
|
178
178
|
// traversing - we'll use this to ensure we don't report modules modified
|
|
179
|
-
// that only exist as part of the graph mid-traversal
|
|
180
|
-
|
|
179
|
+
// that only exist as part of the graph mid-traversal, and to eliminate
|
|
180
|
+
// modules that end up in the same state that they started from the delta.
|
|
181
|
+
const originalModules = new Map<string, Module<T>>();
|
|
182
|
+
for (const path of paths) {
|
|
183
|
+
const originalModule = this.dependencies.get(path);
|
|
184
|
+
if (originalModule) {
|
|
185
|
+
originalModules.set(path, originalModule);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
181
188
|
|
|
182
|
-
for (const path of
|
|
189
|
+
for (const [path] of originalModules) {
|
|
183
190
|
// Traverse over modules that are part of the dependency graph.
|
|
184
191
|
//
|
|
185
192
|
// Note: A given path may not be part of the graph *at this time*, in
|
|
186
193
|
// particular it may have been removed since we started traversing, but
|
|
187
194
|
// in that case the path will be visited if and when we add it back to
|
|
188
195
|
// the graph in a subsequent iteration.
|
|
189
|
-
if (this.dependencies.
|
|
196
|
+
if (this.dependencies.has(path)) {
|
|
190
197
|
await this._traverseDependenciesForSingleFile(
|
|
191
198
|
path,
|
|
192
199
|
delta,
|
|
@@ -208,17 +215,35 @@ export class Graph<T = MixedOutput> {
|
|
|
208
215
|
// but may not actually differ, may be new, or may have been deleted after
|
|
209
216
|
// processing. The actually-modified modules are the intersection of
|
|
210
217
|
// delta.modified with the pre-existing paths, minus modules deleted.
|
|
211
|
-
for (const path of
|
|
218
|
+
for (const [path, originalModule] of originalModules) {
|
|
212
219
|
invariant(
|
|
213
220
|
!delta.added.has(path),
|
|
214
221
|
'delta.added has %s, but this path was already in the graph.',
|
|
215
222
|
path,
|
|
216
223
|
);
|
|
217
224
|
if (delta.modified.has(path)) {
|
|
218
|
-
//
|
|
219
|
-
//
|
|
225
|
+
// It's expected that a module may be both modified and subsequently
|
|
226
|
+
// deleted - we'll only return it as deleted.
|
|
220
227
|
if (!delta.deleted.has(path)) {
|
|
221
|
-
|
|
228
|
+
// If a module existed before and has not been deleted, it must be
|
|
229
|
+
// in the dependencies map.
|
|
230
|
+
const newModule = nullthrows(this.dependencies.get(path));
|
|
231
|
+
if (
|
|
232
|
+
// Module.dependencies is mutable, so it's not obviously the case
|
|
233
|
+
// that referential equality implies no modification. However, we
|
|
234
|
+
// only mutate dependencies in two cases:
|
|
235
|
+
// 1. Within _processModule. In that case, we always mutate a new
|
|
236
|
+
// module and set a new reference in this.dependencies.
|
|
237
|
+
// 2. During _releaseModule, when recursively removing
|
|
238
|
+
// dependencies. In that case, we immediately discard the module
|
|
239
|
+
// object.
|
|
240
|
+
// TODO: Refactor for more explicit immutability
|
|
241
|
+
newModule !== originalModule ||
|
|
242
|
+
transfromOutputMayDiffer(newModule, originalModule) ||
|
|
243
|
+
!allDependenciesEqual(newModule, originalModule)
|
|
244
|
+
) {
|
|
245
|
+
modified.set(path, newModule);
|
|
246
|
+
}
|
|
222
247
|
}
|
|
223
248
|
}
|
|
224
249
|
}
|
|
@@ -290,10 +315,6 @@ export class Graph<T = MixedOutput> {
|
|
|
290
315
|
): Promise<Module<T>> {
|
|
291
316
|
const resolvedContext = this.#resolvedContexts.get(path);
|
|
292
317
|
|
|
293
|
-
// Mark any module processed as potentially modified. Once we've finished
|
|
294
|
-
// traversing we'll filter this set down.
|
|
295
|
-
delta.modified.add(path);
|
|
296
|
-
|
|
297
318
|
// Transform the file via the given option.
|
|
298
319
|
// TODO: Unbind the transform method from options
|
|
299
320
|
const result = await options.transform(path, resolvedContext);
|
|
@@ -306,48 +327,67 @@ export class Graph<T = MixedOutput> {
|
|
|
306
327
|
options,
|
|
307
328
|
);
|
|
308
329
|
|
|
309
|
-
const previousModule = this.dependencies.get(path)
|
|
310
|
-
inverseDependencies:
|
|
311
|
-
delta.earlyInverseDependencies.get(path) || new CountingSet(),
|
|
312
|
-
path,
|
|
313
|
-
};
|
|
314
|
-
const previousDependencies = previousModule.dependencies || new Map();
|
|
330
|
+
const previousModule = this.dependencies.get(path);
|
|
315
331
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
332
|
+
const previousDependencies = previousModule?.dependencies ?? new Map();
|
|
333
|
+
|
|
334
|
+
const nextModule = {
|
|
335
|
+
...(previousModule ?? {
|
|
336
|
+
inverseDependencies:
|
|
337
|
+
delta.earlyInverseDependencies.get(path) ?? new CountingSet(),
|
|
338
|
+
path,
|
|
339
|
+
}),
|
|
319
340
|
dependencies: new Map(previousDependencies),
|
|
320
341
|
getSource: result.getSource,
|
|
321
342
|
output: result.output,
|
|
343
|
+
unstable_transformResultKey: result.unstable_transformResultKey,
|
|
322
344
|
};
|
|
323
|
-
|
|
345
|
+
|
|
346
|
+
// Update the module information.
|
|
347
|
+
this.dependencies.set(nextModule.path, nextModule);
|
|
324
348
|
|
|
325
349
|
// Diff dependencies (1/2): remove dependencies that have changed or been removed.
|
|
350
|
+
let dependenciesRemoved = false;
|
|
326
351
|
for (const [key, prevDependency] of previousDependencies) {
|
|
327
352
|
const curDependency = currentDependencies.get(key);
|
|
328
353
|
if (
|
|
329
354
|
!curDependency ||
|
|
330
355
|
!dependenciesEqual(prevDependency, curDependency, options)
|
|
331
356
|
) {
|
|
332
|
-
|
|
357
|
+
dependenciesRemoved = true;
|
|
358
|
+
this._removeDependency(nextModule, key, prevDependency, delta, options);
|
|
333
359
|
}
|
|
334
360
|
}
|
|
335
361
|
|
|
336
362
|
// Diff dependencies (2/2): add dependencies that have changed or been added.
|
|
337
|
-
const
|
|
363
|
+
const addDependencyPromises = [];
|
|
338
364
|
for (const [key, curDependency] of currentDependencies) {
|
|
339
365
|
const prevDependency = previousDependencies.get(key);
|
|
340
366
|
if (
|
|
341
367
|
!prevDependency ||
|
|
342
368
|
!dependenciesEqual(prevDependency, curDependency, options)
|
|
343
369
|
) {
|
|
344
|
-
|
|
345
|
-
this._addDependency(
|
|
370
|
+
addDependencyPromises.push(
|
|
371
|
+
this._addDependency(nextModule, key, curDependency, delta, options),
|
|
346
372
|
);
|
|
347
373
|
}
|
|
348
374
|
}
|
|
349
375
|
|
|
350
|
-
|
|
376
|
+
if (
|
|
377
|
+
previousModule &&
|
|
378
|
+
!transfromOutputMayDiffer(previousModule, nextModule) &&
|
|
379
|
+
!dependenciesRemoved &&
|
|
380
|
+
addDependencyPromises.length === 0
|
|
381
|
+
) {
|
|
382
|
+
// We have not operated on nextModule, so restore previousModule
|
|
383
|
+
// to aid diffing.
|
|
384
|
+
this.dependencies.set(previousModule.path, previousModule);
|
|
385
|
+
return previousModule;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
delta.modified.add(path);
|
|
389
|
+
|
|
390
|
+
await Promise.all(addDependencyPromises);
|
|
351
391
|
|
|
352
392
|
// Replace dependencies with the correctly-ordered version. As long as all
|
|
353
393
|
// the above promises have resolved, this will be the same map but without
|
|
@@ -357,13 +397,13 @@ export class Graph<T = MixedOutput> {
|
|
|
357
397
|
|
|
358
398
|
// Catch obvious errors with a cheap assertion.
|
|
359
399
|
invariant(
|
|
360
|
-
|
|
400
|
+
nextModule.dependencies.size === currentDependencies.size,
|
|
361
401
|
'Failed to add the correct dependencies',
|
|
362
402
|
);
|
|
363
403
|
|
|
364
|
-
|
|
404
|
+
nextModule.dependencies = currentDependencies;
|
|
365
405
|
|
|
366
|
-
return
|
|
406
|
+
return nextModule;
|
|
367
407
|
}
|
|
368
408
|
|
|
369
409
|
async _addDependency(
|
|
@@ -470,7 +510,10 @@ export class Graph<T = MixedOutput> {
|
|
|
470
510
|
/**
|
|
471
511
|
* Collect a list of context modules which include a given file.
|
|
472
512
|
*/
|
|
473
|
-
markModifiedContextModules(
|
|
513
|
+
markModifiedContextModules(
|
|
514
|
+
filePath: string,
|
|
515
|
+
modifiedPaths: Set<string> | CountingSet<string>,
|
|
516
|
+
) {
|
|
474
517
|
for (const [absolutePath, context] of this.#resolvedContexts) {
|
|
475
518
|
if (
|
|
476
519
|
!modifiedPaths.has(absolutePath) &&
|
|
@@ -814,6 +857,23 @@ function dependenciesEqual(
|
|
|
814
857
|
);
|
|
815
858
|
}
|
|
816
859
|
|
|
860
|
+
function allDependenciesEqual<T>(
|
|
861
|
+
a: Module<T>,
|
|
862
|
+
b: Module<T>,
|
|
863
|
+
options: $ReadOnly<{lazy: boolean, ...}>,
|
|
864
|
+
): boolean {
|
|
865
|
+
if (a.dependencies.size !== b.dependencies.size) {
|
|
866
|
+
return false;
|
|
867
|
+
}
|
|
868
|
+
for (const [key, depA] of a.dependencies) {
|
|
869
|
+
const depB = b.dependencies.get(key);
|
|
870
|
+
if (!depB || !dependenciesEqual(depA, depB, options)) {
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return true;
|
|
875
|
+
}
|
|
876
|
+
|
|
817
877
|
function contextParamsEqual(
|
|
818
878
|
a: ?RequireContextParams,
|
|
819
879
|
b: ?RequireContextParams,
|
|
@@ -829,3 +889,10 @@ function contextParamsEqual(
|
|
|
829
889
|
a.mode === b.mode)
|
|
830
890
|
);
|
|
831
891
|
}
|
|
892
|
+
|
|
893
|
+
function transfromOutputMayDiffer<T>(a: Module<T>, b: Module<T>): boolean {
|
|
894
|
+
return (
|
|
895
|
+
a.unstable_transformResultKey == null ||
|
|
896
|
+
a.unstable_transformResultKey !== b.unstable_transformResultKey
|
|
897
|
+
);
|
|
898
|
+
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"use strict";
|
|
13
13
|
|
|
14
14
|
const invariant = require("invariant");
|
|
15
|
+
const jscSafeUrl = require("jsc-safe-url");
|
|
15
16
|
const { addParamsToDefineCall } = require("metro-transform-plugins");
|
|
16
17
|
const path = require("path");
|
|
17
18
|
function wrapModule(module, options) {
|
|
@@ -41,7 +42,9 @@ function getModuleParams(module, options) {
|
|
|
41
42
|
// Construct a server-relative URL for the split bundle, propagating
|
|
42
43
|
// most parameters from the main bundle's URL.
|
|
43
44
|
|
|
44
|
-
const { searchParams } = new URL(
|
|
45
|
+
const { searchParams } = new URL(
|
|
46
|
+
jscSafeUrl.toNormalUrl(options.sourceUrl)
|
|
47
|
+
);
|
|
45
48
|
searchParams.set("modulesOnly", "true");
|
|
46
49
|
searchParams.set("runModule", "false");
|
|
47
50
|
const bundlePath = path.relative(
|
|
@@ -15,6 +15,7 @@ import type {MixedOutput, Module} from '../../types.flow';
|
|
|
15
15
|
import type {JsOutput} from 'metro-transform-worker';
|
|
16
16
|
|
|
17
17
|
const invariant = require('invariant');
|
|
18
|
+
const jscSafeUrl = require('jsc-safe-url');
|
|
18
19
|
const {addParamsToDefineCall} = require('metro-transform-plugins');
|
|
19
20
|
const path = require('path');
|
|
20
21
|
|
|
@@ -59,7 +60,9 @@ function getModuleParams(module: Module<>, options: Options): Array<mixed> {
|
|
|
59
60
|
// Construct a server-relative URL for the split bundle, propagating
|
|
60
61
|
// most parameters from the main bundle's URL.
|
|
61
62
|
|
|
62
|
-
const {searchParams} = new URL(
|
|
63
|
+
const {searchParams} = new URL(
|
|
64
|
+
jscSafeUrl.toNormalUrl(options.sourceUrl),
|
|
65
|
+
);
|
|
63
66
|
searchParams.set('modulesOnly', 'true');
|
|
64
67
|
searchParams.set('runModule', 'false');
|
|
65
68
|
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"use strict";
|
|
13
13
|
|
|
14
14
|
const { isJsModule, wrapModule } = require("./helpers/js");
|
|
15
|
+
const jscSafeUrl = require("jsc-safe-url");
|
|
15
16
|
const { addParamsToDefineCall } = require("metro-transform-plugins");
|
|
16
17
|
const path = require("path");
|
|
17
18
|
const url = require("url");
|
|
@@ -33,7 +34,7 @@ function generateModules(sourceModules, graph, options) {
|
|
|
33
34
|
return url.format(options.clientUrl);
|
|
34
35
|
};
|
|
35
36
|
const sourceMappingURL = getURL("map");
|
|
36
|
-
const sourceURL = getURL("bundle");
|
|
37
|
+
const sourceURL = jscSafeUrl.toJscSafeUrl(getURL("bundle"));
|
|
37
38
|
const code =
|
|
38
39
|
prepareModule(module, graph, options) +
|
|
39
40
|
`\n//# sourceMappingURL=${sourceMappingURL}\n` +
|
|
@@ -16,6 +16,7 @@ import type {DeltaResult, Module, ReadOnlyGraph} from '../types.flow';
|
|
|
16
16
|
import type {HmrModule} from 'metro-runtime/src/modules/types.flow';
|
|
17
17
|
|
|
18
18
|
const {isJsModule, wrapModule} = require('./helpers/js');
|
|
19
|
+
const jscSafeUrl = require('jsc-safe-url');
|
|
19
20
|
const {addParamsToDefineCall} = require('metro-transform-plugins');
|
|
20
21
|
const path = require('path');
|
|
21
22
|
const url = require('url');
|
|
@@ -53,7 +54,7 @@ function generateModules(
|
|
|
53
54
|
};
|
|
54
55
|
|
|
55
56
|
const sourceMappingURL = getURL('map');
|
|
56
|
-
const sourceURL = getURL('bundle');
|
|
57
|
+
const sourceURL = jscSafeUrl.toJscSafeUrl(getURL('bundle'));
|
|
57
58
|
const code =
|
|
58
59
|
prepareModule(module, graph, options) +
|
|
59
60
|
`\n//# sourceMappingURL=${sourceMappingURL}\n` +
|
|
@@ -61,13 +61,14 @@ export type Dependency = {
|
|
|
61
61
|
+data: TransformResultDependency,
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
-
export type Module<T = MixedOutput> = {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
export type Module<T = MixedOutput> = $ReadOnly<{
|
|
65
|
+
dependencies: Map<string, Dependency>,
|
|
66
|
+
inverseDependencies: CountingSet<string>,
|
|
67
|
+
output: $ReadOnlyArray<T>,
|
|
68
|
+
path: string,
|
|
69
|
+
getSource: () => Buffer,
|
|
70
|
+
unstable_transformResultKey?: ?string,
|
|
71
|
+
}>;
|
|
71
72
|
|
|
72
73
|
export type Dependencies<T = MixedOutput> = Map<string, Module<T>>;
|
|
73
74
|
export type ReadOnlyDependencies<T = MixedOutput> = $ReadOnlyMap<
|
|
@@ -102,6 +103,7 @@ export type {Graph};
|
|
|
102
103
|
export type TransformResult<T = MixedOutput> = $ReadOnly<{
|
|
103
104
|
dependencies: $ReadOnlyArray<TransformResultDependency>,
|
|
104
105
|
output: $ReadOnlyArray<T>,
|
|
106
|
+
unstable_transformResultKey?: ?string,
|
|
105
107
|
}>;
|
|
106
108
|
|
|
107
109
|
export type TransformResultWithSource<T = MixedOutput> = $ReadOnly<{
|
package/src/Server.js
CHANGED
|
@@ -34,6 +34,8 @@ const { codeFrameColumns } = require("@babel/code-frame");
|
|
|
34
34
|
const MultipartResponse = require("./Server/MultipartResponse");
|
|
35
35
|
const debug = require("debug")("Metro:Server");
|
|
36
36
|
const fs = require("graceful-fs");
|
|
37
|
+
const invariant = require("invariant");
|
|
38
|
+
const jscSafeUrl = require("jsc-safe-url");
|
|
37
39
|
const {
|
|
38
40
|
Logger,
|
|
39
41
|
Logger: { createActionStartEntry, createActionEndEntry, log },
|
|
@@ -355,9 +357,14 @@ class Server {
|
|
|
355
357
|
_parseOptions(url) {
|
|
356
358
|
return parseOptionsFromUrl(url, new Set(this._config.resolver.platforms));
|
|
357
359
|
}
|
|
360
|
+
_rewriteAndNormalizeUrl(requestUrl) {
|
|
361
|
+
return jscSafeUrl.toNormalUrl(
|
|
362
|
+
this._config.server.rewriteRequestUrl(jscSafeUrl.toNormalUrl(requestUrl))
|
|
363
|
+
);
|
|
364
|
+
}
|
|
358
365
|
async _processRequest(req, res, next) {
|
|
359
366
|
const originalUrl = req.url;
|
|
360
|
-
req.url = this.
|
|
367
|
+
req.url = this._rewriteAndNormalizeUrl(req.url);
|
|
361
368
|
const urlObj = url.parse(req.url, true);
|
|
362
369
|
const { host } = req.headers;
|
|
363
370
|
debug(
|
|
@@ -704,7 +711,7 @@ class Server {
|
|
|
704
711
|
bundle: bundleCode,
|
|
705
712
|
};
|
|
706
713
|
},
|
|
707
|
-
finish({ req, mres, result }) {
|
|
714
|
+
finish({ req, mres, serializerOptions, result }) {
|
|
708
715
|
if (
|
|
709
716
|
// We avoid parsing the dates since the client should never send a more
|
|
710
717
|
// recent date than the one returned by the Delta Bundler (if that's the
|
|
@@ -721,6 +728,9 @@ class Server {
|
|
|
721
728
|
String(result.numModifiedFiles)
|
|
722
729
|
);
|
|
723
730
|
mres.setHeader(DELTA_ID_HEADER, String(result.nextRevId));
|
|
731
|
+
if (serializerOptions?.sourceUrl != null) {
|
|
732
|
+
mres.setHeader("Content-Location", serializerOptions.sourceUrl);
|
|
733
|
+
}
|
|
724
734
|
mres.setHeader("Content-Type", "application/javascript; charset=UTF-8");
|
|
725
735
|
mres.setHeader("Last-Modified", result.lastModifiedDate.toUTCString());
|
|
726
736
|
mres.setHeader(
|
|
@@ -899,18 +909,27 @@ class Server {
|
|
|
899
909
|
/* $FlowFixMe: where is `rawBody` defined? Is it added by the `connect` framework? */
|
|
900
910
|
const body = await req.rawBody;
|
|
901
911
|
const parsedBody = JSON.parse(body);
|
|
902
|
-
const
|
|
903
|
-
|
|
912
|
+
const rewriteAndNormalizeStackFrame = (frame, lineNumber) => {
|
|
913
|
+
invariant(
|
|
914
|
+
frame != null && typeof frame === "object",
|
|
915
|
+
"Bad stack frame at line %d, expected object, received: %s",
|
|
916
|
+
lineNumber,
|
|
917
|
+
typeof frame
|
|
918
|
+
);
|
|
919
|
+
const frameFile = frame.file;
|
|
920
|
+
if (typeof frameFile === "string" && frameFile.includes("://")) {
|
|
904
921
|
return {
|
|
905
922
|
...frame,
|
|
906
|
-
file: this.
|
|
923
|
+
file: this._rewriteAndNormalizeUrl(frameFile),
|
|
907
924
|
};
|
|
908
925
|
}
|
|
909
926
|
return frame;
|
|
910
|
-
}
|
|
927
|
+
};
|
|
928
|
+
const stack = parsedBody.stack.map(rewriteAndNormalizeStackFrame);
|
|
911
929
|
// In case of multiple bundles / HMR, some stack frames can have different URLs from others
|
|
912
930
|
const urls = new Set();
|
|
913
931
|
stack.forEach((frame) => {
|
|
932
|
+
// These urls have been rewritten and normalized above.
|
|
914
933
|
const sourceUrl = frame.file;
|
|
915
934
|
// Skip `/debuggerWorker.js` which does not need symbolication.
|
|
916
935
|
if (
|
|
@@ -924,8 +943,11 @@ class Server {
|
|
|
924
943
|
});
|
|
925
944
|
debug("Getting source maps for symbolication");
|
|
926
945
|
const sourceMaps = await Promise.all(
|
|
927
|
-
|
|
928
|
-
|
|
946
|
+
Array.from(urls.values()).map((normalizedUrl) =>
|
|
947
|
+
this._explodedSourceMapForBundleOptions(
|
|
948
|
+
this._parseOptions(normalizedUrl)
|
|
949
|
+
)
|
|
950
|
+
)
|
|
929
951
|
);
|
|
930
952
|
debug("Performing fast symbolication");
|
|
931
953
|
const symbolicatedStack = await symbolicate(
|
|
@@ -954,11 +976,7 @@ class Server {
|
|
|
954
976
|
);
|
|
955
977
|
}
|
|
956
978
|
}
|
|
957
|
-
async
|
|
958
|
-
const options = parseOptionsFromUrl(
|
|
959
|
-
reqUrl,
|
|
960
|
-
new Set(this._config.resolver.platforms)
|
|
961
|
-
);
|
|
979
|
+
async _explodedSourceMapForBundleOptions(bundleOptions) {
|
|
962
980
|
const {
|
|
963
981
|
entryFile,
|
|
964
982
|
graphOptions,
|
|
@@ -966,7 +984,7 @@ class Server {
|
|
|
966
984
|
resolverOptions,
|
|
967
985
|
serializerOptions,
|
|
968
986
|
transformOptions,
|
|
969
|
-
} = splitBundleOptions(
|
|
987
|
+
} = splitBundleOptions(bundleOptions);
|
|
970
988
|
|
|
971
989
|
/**
|
|
972
990
|
* `entryFile` is relative to projectRoot, we need to use resolution function
|