metro 0.76.3 → 0.76.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/package.json +24 -23
  2. package/src/Assets.js +35 -3
  3. package/src/Assets.js.flow +38 -3
  4. package/src/Bundler/util.js +0 -72
  5. package/src/Bundler/util.js.flow +0 -98
  6. package/src/DeltaBundler/DeltaCalculator.js +7 -0
  7. package/src/DeltaBundler/DeltaCalculator.js.flow +7 -0
  8. package/src/DeltaBundler/Graph.js +108 -26
  9. package/src/DeltaBundler/Graph.js.flow +119 -26
  10. package/src/DeltaBundler/Serializers/baseJSBundle.js +1 -0
  11. package/src/DeltaBundler/Serializers/baseJSBundle.js.flow +1 -0
  12. package/src/DeltaBundler/Serializers/getRamBundleInfo.js +1 -0
  13. package/src/DeltaBundler/Serializers/getRamBundleInfo.js.flow +3 -1
  14. package/src/DeltaBundler/Serializers/helpers/getSourceMapInfo.js +1 -0
  15. package/src/DeltaBundler/Serializers/helpers/getSourceMapInfo.js.flow +3 -0
  16. package/src/DeltaBundler/Serializers/helpers/js.js +4 -1
  17. package/src/DeltaBundler/Serializers/helpers/js.js.flow +4 -1
  18. package/src/DeltaBundler/Serializers/hmrJSBundle.js +2 -1
  19. package/src/DeltaBundler/Serializers/hmrJSBundle.js.flow +2 -1
  20. package/src/DeltaBundler/Serializers/sourceMapGenerator.js +1 -0
  21. package/src/DeltaBundler/Serializers/sourceMapGenerator.js.flow +10 -12
  22. package/src/DeltaBundler/Serializers/sourceMapObject.js.flow +3 -8
  23. package/src/DeltaBundler/Serializers/sourceMapString.js.flow +2 -4
  24. package/src/DeltaBundler/Transformer.js +1 -0
  25. package/src/DeltaBundler/Transformer.js.flow +1 -0
  26. package/src/DeltaBundler/types.d.ts +1 -0
  27. package/src/DeltaBundler/types.flow.js.flow +10 -7
  28. package/src/Server.js +52 -14
  29. package/src/Server.js.flow +61 -16
  30. package/src/lib/getAppendScripts.js +1 -0
  31. package/src/lib/getAppendScripts.js.flow +12 -10
  32. package/src/lib/parseOptionsFromUrl.js +4 -3
  33. package/src/lib/parseOptionsFromUrl.js.flow +4 -3
@@ -174,11 +174,26 @@ export class Graph<T = MixedOutput> {
174
174
 
175
175
  const internalOptions = getInternalOptions(options);
176
176
 
177
+ // Record the paths that are part of the dependency graph before we start
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, 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>>();
177
182
  for (const path of paths) {
178
- // Start traversing from modules that are already part of the dependency graph.
179
- if (this.dependencies.get(path)) {
180
- delta.modified.add(path);
183
+ const originalModule = this.dependencies.get(path);
184
+ if (originalModule) {
185
+ originalModules.set(path, originalModule);
186
+ }
187
+ }
181
188
 
189
+ for (const [path] of originalModules) {
190
+ // Traverse over modules that are part of the dependency graph.
191
+ //
192
+ // Note: A given path may not be part of the graph *at this time*, in
193
+ // particular it may have been removed since we started traversing, but
194
+ // in that case the path will be visited if and when we add it back to
195
+ // the graph in a subsequent iteration.
196
+ if (this.dependencies.has(path)) {
182
197
  await this._traverseDependenciesForSingleFile(
183
198
  path,
184
199
  delta,
@@ -195,10 +210,41 @@ export class Graph<T = MixedOutput> {
195
210
  }
196
211
 
197
212
  const modified = new Map<string, Module<T>>();
198
- for (const path of delta.modified) {
199
- // Only report a module as modified if we're not already reporting it as added or deleted.
200
- if (!delta.added.has(path) && !delta.deleted.has(path)) {
201
- modified.set(path, nullthrows(this.dependencies.get(path)));
213
+
214
+ // A path in delta.modified has been processed during this traversal,
215
+ // but may not actually differ, may be new, or may have been deleted after
216
+ // processing. The actually-modified modules are the intersection of
217
+ // delta.modified with the pre-existing paths, minus modules deleted.
218
+ for (const [path, originalModule] of originalModules) {
219
+ invariant(
220
+ !delta.added.has(path),
221
+ 'delta.added has %s, but this path was already in the graph.',
222
+ path,
223
+ );
224
+ if (delta.modified.has(path)) {
225
+ // It's expected that a module may be both modified and subsequently
226
+ // deleted - we'll only return it as deleted.
227
+ if (!delta.deleted.has(path)) {
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
+ }
247
+ }
202
248
  }
203
249
  }
204
250
 
@@ -268,6 +314,7 @@ export class Graph<T = MixedOutput> {
268
314
  options: InternalOptions<T>,
269
315
  ): Promise<Module<T>> {
270
316
  const resolvedContext = this.#resolvedContexts.get(path);
317
+
271
318
  // Transform the file via the given option.
272
319
  // TODO: Unbind the transform method from options
273
320
  const result = await options.transform(path, resolvedContext);
@@ -280,48 +327,67 @@ export class Graph<T = MixedOutput> {
280
327
  options,
281
328
  );
282
329
 
283
- const previousModule = this.dependencies.get(path) || {
284
- inverseDependencies:
285
- delta.earlyInverseDependencies.get(path) || new CountingSet(),
286
- path,
287
- };
288
- const previousDependencies = previousModule.dependencies || new Map();
330
+ const previousModule = this.dependencies.get(path);
289
331
 
290
- // Update the module information.
291
- const module = {
292
- ...previousModule,
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
+ }),
293
340
  dependencies: new Map(previousDependencies),
294
341
  getSource: result.getSource,
295
342
  output: result.output,
343
+ unstable_transformResultKey: result.unstable_transformResultKey,
296
344
  };
297
- this.dependencies.set(module.path, module);
345
+
346
+ // Update the module information.
347
+ this.dependencies.set(nextModule.path, nextModule);
298
348
 
299
349
  // Diff dependencies (1/2): remove dependencies that have changed or been removed.
350
+ let dependenciesRemoved = false;
300
351
  for (const [key, prevDependency] of previousDependencies) {
301
352
  const curDependency = currentDependencies.get(key);
302
353
  if (
303
354
  !curDependency ||
304
355
  !dependenciesEqual(prevDependency, curDependency, options)
305
356
  ) {
306
- this._removeDependency(module, key, prevDependency, delta, options);
357
+ dependenciesRemoved = true;
358
+ this._removeDependency(nextModule, key, prevDependency, delta, options);
307
359
  }
308
360
  }
309
361
 
310
362
  // Diff dependencies (2/2): add dependencies that have changed or been added.
311
- const promises = [];
363
+ const addDependencyPromises = [];
312
364
  for (const [key, curDependency] of currentDependencies) {
313
365
  const prevDependency = previousDependencies.get(key);
314
366
  if (
315
367
  !prevDependency ||
316
368
  !dependenciesEqual(prevDependency, curDependency, options)
317
369
  ) {
318
- promises.push(
319
- this._addDependency(module, key, curDependency, delta, options),
370
+ addDependencyPromises.push(
371
+ this._addDependency(nextModule, key, curDependency, delta, options),
320
372
  );
321
373
  }
322
374
  }
323
375
 
324
- await Promise.all(promises);
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);
325
391
 
326
392
  // Replace dependencies with the correctly-ordered version. As long as all
327
393
  // the above promises have resolved, this will be the same map but without
@@ -331,13 +397,13 @@ export class Graph<T = MixedOutput> {
331
397
 
332
398
  // Catch obvious errors with a cheap assertion.
333
399
  invariant(
334
- module.dependencies.size === currentDependencies.size,
400
+ nextModule.dependencies.size === currentDependencies.size,
335
401
  'Failed to add the correct dependencies',
336
402
  );
337
403
 
338
- module.dependencies = currentDependencies;
404
+ nextModule.dependencies = currentDependencies;
339
405
 
340
- return module;
406
+ return nextModule;
341
407
  }
342
408
 
343
409
  async _addDependency(
@@ -444,7 +510,10 @@ export class Graph<T = MixedOutput> {
444
510
  /**
445
511
  * Collect a list of context modules which include a given file.
446
512
  */
447
- markModifiedContextModules(filePath: string, modifiedPaths: Set<string>) {
513
+ markModifiedContextModules(
514
+ filePath: string,
515
+ modifiedPaths: Set<string> | CountingSet<string>,
516
+ ) {
448
517
  for (const [absolutePath, context] of this.#resolvedContexts) {
449
518
  if (
450
519
  !modifiedPaths.has(absolutePath) &&
@@ -788,6 +857,23 @@ function dependenciesEqual(
788
857
  );
789
858
  }
790
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
+
791
877
  function contextParamsEqual(
792
878
  a: ?RequireContextParams,
793
879
  b: ?RequireContextParams,
@@ -803,3 +889,10 @@ function contextParamsEqual(
803
889
  a.mode === b.mode)
804
890
  );
805
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
+ }
@@ -45,6 +45,7 @@ function baseJSBundle(entryPoint, preModules, graph, options) {
45
45
  inlineSourceMap: options.inlineSourceMap,
46
46
  runBeforeMainModule: options.runBeforeMainModule,
47
47
  runModule: options.runModule,
48
+ shouldAddToIgnoreList: options.shouldAddToIgnoreList,
48
49
  sourceMapUrl: options.sourceMapUrl,
49
50
  sourceUrl: options.sourceUrl,
50
51
  }),
@@ -64,6 +64,7 @@ function baseJSBundle(
64
64
  inlineSourceMap: options.inlineSourceMap,
65
65
  runBeforeMainModule: options.runBeforeMainModule,
66
66
  runModule: options.runModule,
67
+ shouldAddToIgnoreList: options.shouldAddToIgnoreList,
67
68
  sourceMapUrl: options.sourceMapUrl,
68
69
  sourceUrl: options.sourceUrl,
69
70
  }),
@@ -31,6 +31,7 @@ async function getRamBundleInfo(entryPoint, pre, graph, options) {
31
31
  map: sourceMapObject([module], {
32
32
  excludeSource: options.excludeSource,
33
33
  processModuleFilter: options.processModuleFilter,
34
+ shouldAddToIgnoreList: options.shouldAddToIgnoreList,
34
35
  }),
35
36
  name: path.basename(module.path),
36
37
  sourcePath: module.path,
@@ -16,6 +16,7 @@ import type {
16
16
  RamModuleTransport,
17
17
  } from '../../shared/types.flow';
18
18
  import type {Module, ReadOnlyGraph, SerializerOptions} from '../types.flow';
19
+ import type {SourceMapGeneratorOptions} from './sourceMapGenerator';
19
20
  import type {GetTransformOptions} from 'metro-config/src/configTypes.flow.js';
20
21
 
21
22
  const {createRamBundleGroups} = require('../../Bundler/util');
@@ -28,7 +29,7 @@ const path = require('path');
28
29
 
29
30
  type Options = $ReadOnly<{
30
31
  ...SerializerOptions,
31
- excludeSource: boolean,
32
+ ...SourceMapGeneratorOptions,
32
33
  getTransformOptions: ?GetTransformOptions,
33
34
  platform: ?string,
34
35
  }>;
@@ -63,6 +64,7 @@ async function getRamBundleInfo(
63
64
  map: sourceMapObject([module], {
64
65
  excludeSource: options.excludeSource,
65
66
  processModuleFilter: options.processModuleFilter,
67
+ shouldAddToIgnoreList: options.shouldAddToIgnoreList,
66
68
  }),
67
69
  name: path.basename(module.path),
68
70
  sourcePath: module.path,
@@ -15,6 +15,7 @@ const { getJsOutput } = require("./js");
15
15
  function getSourceMapInfo(module, options) {
16
16
  return {
17
17
  ...getJsOutput(module).data,
18
+ isIgnored: options.shouldAddToIgnoreList(module),
18
19
  path: module.path,
19
20
  source: options.excludeSource ? "" : getModuleSource(module),
20
21
  };
@@ -23,6 +23,7 @@ function getSourceMapInfo(
23
23
  module: Module<>,
24
24
  options: {
25
25
  +excludeSource: boolean,
26
+ +shouldAddToIgnoreList: (Module<>) => boolean,
26
27
  },
27
28
  ): {
28
29
  +map: Array<MetroSourceMapSegmentTuple>,
@@ -31,9 +32,11 @@ function getSourceMapInfo(
31
32
  +path: string,
32
33
  +source: string,
33
34
  +lineCount: number,
35
+ +isIgnored: boolean,
34
36
  } {
35
37
  return {
36
38
  ...getJsOutput(module).data,
39
+ isIgnored: options.shouldAddToIgnoreList(module),
37
40
  path: module.path,
38
41
  source: options.excludeSource ? '' : getModuleSource(module),
39
42
  };
@@ -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(options.sourceUrl);
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(options.sourceUrl);
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` +
@@ -29,6 +29,7 @@ function getSourceMapInfosImpl(isBlocking, onDone, modules, options) {
29
29
  const mod = modulesToProcess.shift();
30
30
  const info = getSourceMapInfo(mod, {
31
31
  excludeSource: options.excludeSource,
32
+ shouldAddToIgnoreList: options.shouldAddToIgnoreList,
32
33
  });
33
34
  sourceMapInfos.push(info);
34
35
  return false;
@@ -13,6 +13,12 @@
13
13
 
14
14
  import type {Module} from '../types.flow';
15
15
 
16
+ export type SourceMapGeneratorOptions = $ReadOnly<{
17
+ excludeSource: boolean,
18
+ processModuleFilter: (module: Module<>) => boolean,
19
+ shouldAddToIgnoreList: (module: Module<>) => boolean,
20
+ }>;
21
+
16
22
  const getSourceMapInfo = require('./helpers/getSourceMapInfo');
17
23
  const {isJsModule} = require('./helpers/js');
18
24
  const {
@@ -26,10 +32,7 @@ function getSourceMapInfosImpl(
26
32
  isBlocking: boolean,
27
33
  onDone: ($ReadOnlyArray<ReturnType<typeof getSourceMapInfo>>) => void,
28
34
  modules: $ReadOnlyArray<Module<>>,
29
- options: {
30
- +excludeSource: boolean,
31
- +processModuleFilter: (module: Module<>) => boolean,
32
- },
35
+ options: SourceMapGeneratorOptions,
33
36
  ): void {
34
37
  const sourceMapInfos = [];
35
38
  const modulesToProcess = modules
@@ -44,6 +47,7 @@ function getSourceMapInfosImpl(
44
47
  const mod = modulesToProcess.shift();
45
48
  const info = getSourceMapInfo(mod, {
46
49
  excludeSource: options.excludeSource,
50
+ shouldAddToIgnoreList: options.shouldAddToIgnoreList,
47
51
  });
48
52
  sourceMapInfos.push(info);
49
53
  return false;
@@ -77,10 +81,7 @@ function getSourceMapInfosImpl(
77
81
 
78
82
  function sourceMapGenerator(
79
83
  modules: $ReadOnlyArray<Module<>>,
80
- options: {
81
- +excludeSource: boolean,
82
- +processModuleFilter: (module: Module<>) => boolean,
83
- },
84
+ options: SourceMapGeneratorOptions,
84
85
  ): ReturnType<typeof fromRawMappings> {
85
86
  let sourceMapInfos;
86
87
  getSourceMapInfosImpl(
@@ -101,10 +102,7 @@ function sourceMapGenerator(
101
102
 
102
103
  async function sourceMapGeneratorNonBlocking(
103
104
  modules: $ReadOnlyArray<Module<>>,
104
- options: {
105
- +excludeSource: boolean,
106
- +processModuleFilter: (module: Module<>) => boolean,
107
- },
105
+ options: SourceMapGeneratorOptions,
108
106
  ): ReturnType<typeof fromRawMappingsNonBlocking> {
109
107
  const sourceMapInfos = await new Promise<
110
108
  $ReadOnlyArray<ReturnType<typeof getSourceMapInfo>>,
@@ -12,6 +12,7 @@
12
12
  'use strict';
13
13
 
14
14
  import type {Module} from '../types.flow';
15
+ import type {SourceMapGeneratorOptions} from './sourceMapGenerator';
15
16
  import type {MixedSourceMap} from 'metro-source-map';
16
17
 
17
18
  const {
@@ -21,10 +22,7 @@ const {
21
22
 
22
23
  function sourceMapObject(
23
24
  modules: $ReadOnlyArray<Module<>>,
24
- options: {
25
- +excludeSource: boolean,
26
- +processModuleFilter: (module: Module<>) => boolean,
27
- },
25
+ options: SourceMapGeneratorOptions,
28
26
  ): MixedSourceMap {
29
27
  const generator = sourceMapGenerator(modules, options);
30
28
  return generator.toMap(undefined, {
@@ -34,10 +32,7 @@ function sourceMapObject(
34
32
 
35
33
  async function sourceMapObjectNonBlocking(
36
34
  modules: $ReadOnlyArray<Module<>>,
37
- options: {
38
- +excludeSource: boolean,
39
- +processModuleFilter: (module: Module<>) => boolean,
40
- },
35
+ options: SourceMapGeneratorOptions,
41
36
  ): Promise<MixedSourceMap> {
42
37
  const generator = await sourceMapGeneratorNonBlocking(modules, options);
43
38
  return generator.toMap(undefined, {
@@ -12,15 +12,13 @@
12
12
  'use strict';
13
13
 
14
14
  import type {Module} from '../types.flow';
15
+ import type {SourceMapGeneratorOptions} from './sourceMapGenerator';
15
16
 
16
17
  const {sourceMapGenerator} = require('./sourceMapGenerator');
17
18
 
18
19
  function sourceMapString(
19
20
  modules: $ReadOnlyArray<Module<>>,
20
- options: {
21
- +excludeSource: boolean,
22
- +processModuleFilter: (module: Module<>) => boolean,
23
- },
21
+ options: SourceMapGeneratorOptions,
24
22
  ): string {
25
23
  return sourceMapGenerator(modules, options).toString(undefined, {
26
24
  excludeSource: options.excludeSource,
@@ -130,6 +130,7 @@ class Transformer {
130
130
  cache.set(fullKey, data.result);
131
131
  return {
132
132
  ...data.result,
133
+ unstable_transformResultKey: fullKey.toString(),
133
134
  getSource() {
134
135
  if (fileBuffer) {
135
136
  return fileBuffer;
@@ -155,6 +155,7 @@ class Transformer {
155
155
 
156
156
  return {
157
157
  ...data.result,
158
+ unstable_transformResultKey: fullKey.toString(),
158
159
  getSource(): Buffer {
159
160
  if (fileBuffer) {
160
161
  return fileBuffer;
@@ -164,6 +164,7 @@ export interface SerializerOptions<T = MixedOutput> {
164
164
  readonly runBeforeMainModule: ReadonlyArray<string>;
165
165
  readonly runModule: boolean;
166
166
  readonly serverRoot: string;
167
+ readonly shouldAddToIgnoreList: (module: Module<T>) => boolean;
167
168
  readonly sourceMapUrl?: string;
168
169
  readonly sourceUrl?: string;
169
170
  }
@@ -61,13 +61,14 @@ export type Dependency = {
61
61
  +data: TransformResultDependency,
62
62
  };
63
63
 
64
- export type Module<T = MixedOutput> = {
65
- +dependencies: Map<string, Dependency>,
66
- +inverseDependencies: CountingSet<string>,
67
- +output: $ReadOnlyArray<T>,
68
- +path: string,
69
- +getSource: () => Buffer,
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<{
@@ -156,6 +158,7 @@ export type SerializerOptions = $ReadOnly<{
156
158
  runBeforeMainModule: $ReadOnlyArray<string>,
157
159
  runModule: boolean,
158
160
  serverRoot: string,
161
+ shouldAddToIgnoreList: (Module<>) => boolean,
159
162
  sourceMapUrl: ?string,
160
163
  sourceUrl: ?string,
161
164
  }>;