webpack 5.110.0 → 5.110.2

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 (52) hide show
  1. package/README.md +10 -17
  2. package/lib/ExportsInfo.js +10 -0
  3. package/lib/FileSystemInfo.js +17 -2
  4. package/lib/Generator.js +17 -0
  5. package/lib/NormalModule.js +3 -3
  6. package/lib/RuntimeTemplate.js +423 -430
  7. package/lib/asset/AssetBytesGenerator.js +1 -3
  8. package/lib/asset/AssetGenerator.js +1 -3
  9. package/lib/asset/AssetSourceGenerator.js +1 -3
  10. package/lib/config/defaults.js +6 -0
  11. package/lib/css/CssGenerator.js +1 -3
  12. package/lib/css/CssLoadingRuntimeModule.js +32 -13
  13. package/lib/css/CssModulesPlugin.js +9 -17
  14. package/lib/css/data.js +13 -3
  15. package/lib/css/syntax.js +78 -10
  16. package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +10 -4
  17. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +2 -0
  18. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +48 -3
  19. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +83 -14
  20. package/lib/dependencies/HarmonyImportSideEffectDependency.js +114 -0
  21. package/lib/dependencies/WorkerAndWorkletPlugin.js +1 -0
  22. package/lib/dependencies/WorkerDependency.js +6 -12
  23. package/lib/dependencies/WorkletDependency.js +7 -11
  24. package/lib/esm/ModuleChunkLoadingPlugin.js +6 -9
  25. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +7 -1
  26. package/lib/html/HtmlGenerator.js +1 -1
  27. package/lib/html/HtmlModulesPlugin.js +55 -16
  28. package/lib/html/syntax.js +113 -26
  29. package/lib/javascript/JavascriptGenerator.js +1 -1
  30. package/lib/javascript/JavascriptModulesPlugin.js +3 -10
  31. package/lib/json/JsonGenerator.js +1 -1
  32. package/lib/loaders/LoaderRunner.js +33 -5
  33. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +8 -15
  34. package/lib/optimize/ConcatenatedModule.js +8 -0
  35. package/lib/optimize/ConstExportsPlugin.js +5 -0
  36. package/lib/optimize/InlineExports.js +121 -0
  37. package/lib/optimize/RealContentHashPlugin.js +111 -35
  38. package/lib/prefetch/ResourceHintPlugin.js +6 -12
  39. package/lib/serialization/ObjectMiddleware.js +128 -108
  40. package/lib/typescript/TypeScriptPlugin.js +8 -10
  41. package/lib/util/SourceProcessor.js +24 -8
  42. package/lib/util/createMappings.js +25 -3
  43. package/lib/util/removeBOM.js +82 -1
  44. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +5 -3
  45. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +5 -3
  46. package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +3 -1
  47. package/lib/wasm-async/UniversalCompileAsyncWasmPlugin.js +7 -12
  48. package/lib/wasm-sync/WebAssemblyJavascriptGenerator.js +3 -1
  49. package/lib/web/FetchCompileAsyncWasmPlugin.js +10 -6
  50. package/lib/web/FetchCompileWasmPlugin.js +4 -2
  51. package/package.json +1 -1
  52. package/types.d.ts +77 -10
@@ -345,47 +345,68 @@ ${referencingAssets
345
345
  }
346
346
  return hashes;
347
347
  };
348
+ // Tarjan: hashes naming each other come out as one group where a chain
349
+ // walk would never end, and groups arrive dependencies-first as needed.
350
+ /** @type {string[][]} */
351
+ const groupsInOrder = [];
352
+ /** @type {Map<string, number>} */
353
+ const indexOf = new Map();
354
+ /** @type {Map<string, number>} */
355
+ const lowOf = new Map();
356
+ /** @type {Set<string>} */
357
+ const onStack = new Set();
358
+ /** @type {string[]} */
359
+ const stack = [];
348
360
  /**
349
- * Returns the hash info.
361
+ * Visits one hash and everything its assets reference.
350
362
  * @param {string} hash the hash
351
- * @returns {string} the hash info
363
+ * @returns {void}
352
364
  */
353
- const hashInfo = (hash) => {
354
- const assets = hashToAssets.get(hash);
355
- return `${hash} (${Array.from(
356
- /** @type {AssetInfoForRealContentHash[]} */ (assets),
357
- (a) => a.name
358
- )})`;
359
- };
360
- /** @type {Hashes} */
361
- const hashesInOrder = new Set();
362
- for (const hash of hashToAssets.keys()) {
363
- /**
364
- * Processes the provided hash.
365
- * @param {string} hash the hash
366
- * @param {Set<string>} stack stack of hashes
367
- */
368
- const add = (hash, stack) => {
369
- const deps = getDependencies(hash);
370
- if (!deps) return;
371
- stack.add(hash);
365
+ const strongConnect = (hash) => {
366
+ const index = indexOf.size;
367
+ indexOf.set(hash, index);
368
+ lowOf.set(hash, index);
369
+ stack.push(hash);
370
+ onStack.add(hash);
371
+ // An unowned reference reports the caching problem and stays a leaf.
372
+ const deps = getDependencies(hash);
373
+ if (deps) {
372
374
  for (const dep of deps) {
373
- if (hashesInOrder.has(dep)) continue;
374
- if (stack.has(dep)) {
375
- throw new Error(
376
- `Circular hash dependency ${Array.from(
377
- stack,
378
- hashInfo
379
- ).join(" -> ")} -> ${hashInfo(dep)}`
375
+ if (!indexOf.has(dep)) {
376
+ strongConnect(dep);
377
+ lowOf.set(
378
+ hash,
379
+ Math.min(
380
+ /** @type {number} */ (lowOf.get(hash)),
381
+ /** @type {number} */ (lowOf.get(dep))
382
+ )
383
+ );
384
+ } else if (onStack.has(dep)) {
385
+ lowOf.set(
386
+ hash,
387
+ Math.min(
388
+ /** @type {number} */ (lowOf.get(hash)),
389
+ /** @type {number} */ (indexOf.get(dep))
390
+ )
380
391
  );
381
392
  }
382
- add(dep, stack);
383
393
  }
384
- hashesInOrder.add(hash);
385
- stack.delete(hash);
386
- };
387
- if (hashesInOrder.has(hash)) continue;
388
- add(hash, new Set());
394
+ }
395
+ if (lowOf.get(hash) === indexOf.get(hash)) {
396
+ /** @type {string[]} */
397
+ const group = [];
398
+ /** @type {string} */
399
+ let member;
400
+ do {
401
+ member = /** @type {string} */ (stack.pop());
402
+ onStack.delete(member);
403
+ if (hashToAssets.has(member)) group.push(member);
404
+ } while (member !== hash);
405
+ if (group.length > 0) groupsInOrder.push(group);
406
+ }
407
+ };
408
+ for (const hash of hashToAssets.keys()) {
409
+ if (!indexOf.has(hash)) strongConnect(hash);
389
410
  }
390
411
  /** @type {Map<string, string>} */
391
412
  const hashToNewHash = new Map();
@@ -474,7 +495,62 @@ ${referencingAssets
474
495
  };
475
496
  /** @type {Comparator<AssetInfoForRealContentHash>} */
476
497
  const comparator = compareSelect((a) => a.name, compareStrings);
477
- for (const oldHash of hashesInOrder) {
498
+ for (const group of groupsInOrder) {
499
+ // A cycle is assigned as one group: members are told apart by stable
500
+ // markers, so the group hashes to a fixed point instead of chasing one.
501
+ if (group.length > 1) {
502
+ const members = group.sort();
503
+ /** @type {Map<string, string>} */
504
+ const markerOf = new Map(
505
+ members.map((hash, i) => [hash, `|webpack/scc/${i}|`])
506
+ );
507
+ for (let i = 0; i < members.length; i++) {
508
+ const oldHash = members[i];
509
+ const memberAssets =
510
+ /** @type {AssetInfoForRealContentHash[]} */
511
+ (hashToAssets.get(oldHash));
512
+ memberAssets.sort(comparator);
513
+ const hash = createHash(this._hashFunction);
514
+ if (compilation.outputOptions.hashSalt) {
515
+ hash.update(compilation.outputOptions.hashSalt);
516
+ }
517
+ // Two members can normalize to the same text; the position keeps
518
+ // their hashes apart, and sorting keeps the position stable.
519
+ hash.update(`${i}`);
520
+ for (const asset of memberAssets) {
521
+ if (Buffer.isBuffer(asset.content)) {
522
+ hash.update(asset.content);
523
+ continue;
524
+ }
525
+ const withoutOwn =
526
+ /** @type {Hashes} */
527
+ (asset.ownHashes).has(oldHash);
528
+ hash.update(
529
+ asset.content.replace(hashRegExp, (matched) => {
530
+ // Its own occurrences vanish, exactly as a lone hash's do.
531
+ if (
532
+ withoutOwn &&
533
+ /** @type {Hashes} */ (asset.ownHashes).has(matched)
534
+ ) {
535
+ return "";
536
+ }
537
+ const marker = markerOf.get(matched);
538
+ if (marker !== undefined) return marker;
539
+ const mapped = hashToNewHash.get(matched);
540
+ return mapped === undefined ? matched : mapped;
541
+ })
542
+ );
543
+ }
544
+ const digest = hash.digest(
545
+ /** @type {HashDigest} */ (
546
+ hashToDigest.get(oldHash) || this._hashDigest
547
+ )
548
+ );
549
+ hashToNewHash.set(oldHash, digest.slice(0, oldHash.length));
550
+ }
551
+ continue;
552
+ }
553
+ const oldHash = group[0];
478
554
  const assets =
479
555
  /** @type {AssetInfoForRealContentHash[]} */
480
556
  (hashToAssets.get(oldHash));
@@ -481,19 +481,13 @@ const collectStartupAssetHintLines = (
481
481
  seenWorkerChunks.add(workerChunk.id);
482
482
  // The same specifier the `new Worker(...)` call site bakes, asked of the
483
483
  // same module, so the two agree on whether a literal is spellable.
484
- const specifier = runtimeTemplate.supportsAnalyzable(
485
- "url",
484
+ const specifier = runtimeTemplate.getAnalyzableWorkerUrl(
485
+ dep.options.publicPath,
486
+ workerChunk,
487
+ module,
486
488
  chunkGraph,
487
- module
488
- )
489
- ? runtimeTemplate._getAnalyzableChunkSpecifier(
490
- dep.options.publicPath,
491
- workerChunk,
492
- module,
493
- chunkGraph,
494
- set
495
- )
496
- : null;
489
+ set
490
+ );
497
491
  // Without one the call site keeps the runtime form, where it wraps its own
498
492
  // href — firing the `<link>` here too would emit the call twice.
499
493
  if (specifier === null) continue;
@@ -461,7 +461,7 @@ class ObjectMiddleware extends SerializerMiddleware {
461
461
  return buf;
462
462
  };
463
463
  let currentPosTypeLookup = 0;
464
- /** @type {Map<ComplexSerializableType, number>} */
464
+ /** @type {Map<SerializerConfigWithSerializer, number>} */
465
465
  let objectTypeLookup = new Map();
466
466
  /** @type {Set<ComplexSerializableType>} */
467
467
  const cycleStack = new Set();
@@ -573,140 +573,160 @@ class ObjectMiddleware extends SerializerMiddleware {
573
573
  ...context
574
574
  };
575
575
  this.extendContext(ctx);
576
+ // Dispatched on `typeof` first: two thirds of the items are numbers,
577
+ // booleans and `undefined`, and only 3% are buffers.
576
578
  /**
577
579
  * Processes the provided item.
578
580
  * @param {ComplexSerializableType} item item to serialize
579
581
  */
580
582
  const process = (item) => {
581
- if (Buffer.isBuffer(item)) {
582
- // check if we can emit a reference
583
- const ref = referenceable.get(item);
584
- if (ref !== undefined) {
585
- result.push(ESCAPE, ref - currentPos);
583
+ switch (typeof item) {
584
+ case "number":
585
+ case "boolean":
586
+ result.push(item);
586
587
  return;
587
- }
588
- const alreadyUsedBuffer = dedupeBuffer(item);
589
- if (alreadyUsedBuffer !== item) {
590
- const ref = referenceable.get(alreadyUsedBuffer);
591
- if (ref !== undefined) {
592
- referenceable.set(item, ref);
593
- result.push(ESCAPE, ref - currentPos);
594
- return;
588
+ case "string": {
589
+ if (item.length > 1) {
590
+ // short strings are shorter when not emitting a reference (this saves 1 byte per empty string)
591
+ // check if we can emit a reference
592
+ const ref = referenceable.get(item);
593
+ if (ref !== undefined) {
594
+ const offset = ref - currentPos;
595
+ if (
596
+ // One far ref → 5 bytes (`null` + offset), one near ref -> 2/3 bytes
597
+ offset >= -32768 ||
598
+ // long enough that the ref wins, keep it
599
+ item.length >= 4 ||
600
+ // multibyte → inline would be even bigger, keep it
601
+ Buffer.byteLength(item) !== item.length
602
+ ) {
603
+ // A back-reference is `null` + the offset.
604
+ result.push(ESCAPE, offset);
605
+ return;
606
+ }
607
+ }
608
+ addReferenceable(item);
595
609
  }
596
- item = alreadyUsedBuffer;
597
- }
598
- addReferenceable(item);
599
610
 
600
- result.push(/** @type {Buffer} */ (item));
601
- } else if (item === ESCAPE) {
602
- result.push(ESCAPE, ESCAPE_ESCAPE_VALUE);
603
- } else if (
604
- typeof item === "object"
605
- // We don't have to check for null as ESCAPE is null and this has been checked before
606
- ) {
607
- // check if we can emit a reference
608
- const ref = referenceable.get(item);
609
- if (ref !== undefined) {
610
- result.push(ESCAPE, ref - currentPos);
611
+ if (item.length > 102400 && context.logger) {
612
+ context.logger.warn(
613
+ `Serializing big strings (${Math.round(
614
+ item.length / 1024
615
+ )}kiB) impacts deserialization performance (consider using Buffer instead and decode when needed)`
616
+ );
617
+ }
618
+
619
+ result.push(item);
611
620
  return;
612
621
  }
622
+ case "undefined":
623
+ result.push(ESCAPE, ESCAPE_UNDEFINED);
624
+ return;
625
+ case "object": {
626
+ // ESCAPE is null, so this covers null too
627
+ if (item === ESCAPE) {
628
+ result.push(ESCAPE, ESCAPE_ESCAPE_VALUE);
629
+ return;
630
+ }
613
631
 
614
- if (cycleStack.has(item)) {
615
- throw new Error(
616
- "This is a circular references. To serialize circular references use 'setCircularReference' somewhere in the circle during serialize and deserialize."
617
- );
618
- }
632
+ if (Buffer.isBuffer(item)) {
633
+ // check if we can emit a reference
634
+ const ref = referenceable.get(item);
635
+ if (ref !== undefined) {
636
+ result.push(ESCAPE, ref - currentPos);
637
+ return;
638
+ }
639
+ const alreadyUsedBuffer = dedupeBuffer(item);
640
+ if (alreadyUsedBuffer !== item) {
641
+ const ref = referenceable.get(alreadyUsedBuffer);
642
+ if (ref !== undefined) {
643
+ referenceable.set(item, ref);
644
+ result.push(ESCAPE, ref - currentPos);
645
+ return;
646
+ }
647
+ item = alreadyUsedBuffer;
648
+ }
649
+ addReferenceable(item);
619
650
 
620
- const { request, name, serializer } = ObjectMiddleware.getSerializerFor(
621
- /** @type {Constructor} */
622
- (item)
623
- );
624
- const key = `${request}/${name}`;
625
- const lastIndex = objectTypeLookup.get(key);
651
+ result.push(/** @type {Buffer} */ (item));
652
+ return;
653
+ }
626
654
 
627
- if (lastIndex === undefined) {
628
- objectTypeLookup.set(key, currentPosTypeLookup++);
655
+ // check if we can emit a reference
656
+ const ref = referenceable.get(item);
657
+ if (ref !== undefined) {
658
+ result.push(ESCAPE, ref - currentPos);
659
+ return;
660
+ }
629
661
 
630
- result.push(ESCAPE, request, name);
631
- } else {
632
- result.push(ESCAPE, currentPosTypeLookup - lastIndex);
633
- }
662
+ if (cycleStack.has(item)) {
663
+ throw new Error(
664
+ "This is a circular references. To serialize circular references use 'setCircularReference' somewhere in the circle during serialize and deserialize."
665
+ );
666
+ }
634
667
 
635
- cycleStack.add(item);
668
+ // Keyed by the registration itself: one per class, so it stands in for
669
+ // `${request}/${name}` without building that string per object.
670
+ const config = ObjectMiddleware.getSerializerFor(
671
+ /** @type {Constructor} */
672
+ (item)
673
+ );
674
+ const { request, name, serializer } = config;
675
+ const lastIndex = objectTypeLookup.get(config);
636
676
 
637
- try {
638
- serializer.serialize(item, ctx);
639
- } finally {
640
- cycleStack.delete(item);
641
- }
677
+ if (lastIndex === undefined) {
678
+ objectTypeLookup.set(config, currentPosTypeLookup++);
642
679
 
643
- result.push(ESCAPE, ESCAPE_END_OBJECT);
680
+ result.push(ESCAPE, request, name);
681
+ } else {
682
+ result.push(ESCAPE, currentPosTypeLookup - lastIndex);
683
+ }
644
684
 
645
- addReferenceable(item);
646
- } else if (typeof item === "string") {
647
- if (item.length > 1) {
648
- // short strings are shorter when not emitting a reference (this saves 1 byte per empty string)
649
- // check if we can emit a reference
650
- const ref = referenceable.get(item);
651
- if (ref !== undefined) {
652
- const offset = ref - currentPos;
653
- if (
654
- // One far ref → 5 bytes (`null` + offset), one near ref -> 2/3 bytes
655
- offset >= -32768 ||
656
- // long enough that the ref wins, keep it
657
- item.length >= 4 ||
658
- // multibyte → inline would be even bigger, keep it
659
- Buffer.byteLength(item) !== item.length
660
- ) {
661
- // A back-reference is `null` + the offset.
662
- result.push(ESCAPE, offset);
663
- return;
664
- }
685
+ cycleStack.add(item);
686
+
687
+ try {
688
+ serializer.serialize(item, ctx);
689
+ } finally {
690
+ cycleStack.delete(item);
665
691
  }
666
- addReferenceable(item);
667
- }
668
692
 
669
- if (item.length > 102400 && context.logger) {
670
- context.logger.warn(
671
- `Serializing big strings (${Math.round(
672
- item.length / 1024
673
- )}kiB) impacts deserialization performance (consider using Buffer instead and decode when needed)`
674
- );
675
- }
693
+ result.push(ESCAPE, ESCAPE_END_OBJECT);
676
694
 
677
- result.push(item);
678
- } else if (typeof item === "function") {
679
- if (!SerializerMiddleware.isLazy(item)) {
680
- throw new Error(`Unexpected function ${item}`);
695
+ addReferenceable(item);
696
+ return;
681
697
  }
698
+ case "function": {
699
+ if (!SerializerMiddleware.isLazy(item)) {
700
+ throw new Error(`Unexpected function ${item}`);
701
+ }
682
702
 
683
- /** @type {SerializedType | undefined} */
684
- const serializedData =
685
- SerializerMiddleware.getLazySerializedValue(item);
703
+ /** @type {SerializedType | undefined} */
704
+ const serializedData =
705
+ SerializerMiddleware.getLazySerializedValue(item);
686
706
 
687
- if (serializedData !== undefined) {
688
- if (typeof serializedData === "function") {
689
- result.push(serializedData);
690
- } else {
707
+ if (serializedData !== undefined) {
708
+ if (typeof serializedData === "function") {
709
+ result.push(serializedData);
710
+ } else {
711
+ throw new Error("Not implemented");
712
+ }
713
+ } else if (SerializerMiddleware.isLazy(item, this)) {
691
714
  throw new Error("Not implemented");
715
+ } else {
716
+ const data =
717
+ /** @type {() => PrimitiveSerializableType[] | Promise<PrimitiveSerializableType[]>} */
718
+ (
719
+ SerializerMiddleware.serializeLazy(item, (data) =>
720
+ this.serialize([data], context)
721
+ )
722
+ );
723
+ SerializerMiddleware.setLazySerializedValue(item, data);
724
+ result.push(data);
692
725
  }
693
- } else if (SerializerMiddleware.isLazy(item, this)) {
694
- throw new Error("Not implemented");
695
- } else {
696
- const data =
697
- /** @type {() => PrimitiveSerializableType[] | Promise<PrimitiveSerializableType[]>} */
698
- (
699
- SerializerMiddleware.serializeLazy(item, (data) =>
700
- this.serialize([data], context)
701
- )
702
- );
703
- SerializerMiddleware.setLazySerializedValue(item, data);
704
- result.push(data);
726
+ return;
705
727
  }
706
- } else if (item === undefined) {
707
- result.push(ESCAPE, ESCAPE_UNDEFINED);
708
- } else {
709
- result.push(item);
728
+ default:
729
+ result.push(item);
710
730
  }
711
731
  };
712
732
 
@@ -14,7 +14,7 @@ const {
14
14
  } = require("../ModuleTypeConstants");
15
15
  const NormalModule = require("../NormalModule");
16
16
  const memoize = require("../util/memoize");
17
- const removeBOM = require("../util/removeBOM");
17
+ const { removeBOMFromResult } = require("../util/removeBOM");
18
18
 
19
19
  const getModuleBuildError = memoize(() =>
20
20
  require("../errors/ModuleBuildError")
@@ -133,15 +133,11 @@ const stripTypes = (input) => {
133
133
  };
134
134
 
135
135
  /**
136
- * Coerce a Buffer-or-string source to a UTF-8 string, dropping any BOM.
137
136
  * @param {string | Buffer} source raw source from the loader pipeline
138
- * @returns {string} UTF-8 string without BOM
137
+ * @returns {string} UTF-8 string
139
138
  */
140
- const toBomFreeString = (source) => {
141
- const text = Buffer.isBuffer(source) ? source.toString("utf8") : source;
142
- const stripped = removeBOM(text);
143
- return typeof stripped === "string" ? stripped : stripped.toString("utf8");
144
- };
139
+ const asString = (source) =>
140
+ Buffer.isBuffer(source) ? source.toString("utf8") : source;
145
141
 
146
142
  class TypeScriptPlugin {
147
143
  /**
@@ -181,8 +177,10 @@ class TypeScriptPlugin {
181
177
  throw new (getModuleBuildError())(new Error(NODE_API_MISSING));
182
178
  }
183
179
 
184
- const [rawSource, loaderSourceMap, ...rest] = result;
185
- const preStripSource = toBomFreeString(rawSource);
180
+ // A BOM must be off the source before it is stripped of types, and its
181
+ // map moved with it.
182
+ const [rawSource, loaderSourceMap, ...rest] = removeBOMFromResult(result);
183
+ const preStripSource = asString(rawSource);
186
184
  const strippedSource = stripTypes(preStripSource);
187
185
 
188
186
  const needSourceMap = module.useSourceMap || module.useSimpleSourceMap;
@@ -99,6 +99,23 @@ const expandDeferredWrites = (text, resolve, infix) => {
99
99
  return out + text.slice(from);
100
100
  };
101
101
 
102
+ /**
103
+ * The text this print spells with every deferred write declined: each marker
104
+ * replaced by what would stand there had no renderer answered. What a print
105
+ * verifying itself by re-parsing has to read, since a marker otherwise parses
106
+ * as text where its node stood. `build` rather than `source` so a write inside
107
+ * an attribute comes back escaped for where it sits.
108
+ * @param {string} text the printed output, holding this print's markers
109
+ * @param {DeferredWrite[]} writes what the print deferred, indexed by id
110
+ * @returns {string} the text with every marker put back
111
+ */
112
+ const declineDeferredWrites = (text, writes) =>
113
+ expandDeferredWrites(
114
+ text,
115
+ (id) => (id < writes.length ? writes[id].build(undefined) : undefined),
116
+ deferredInfix
117
+ );
118
+
102
119
  /**
103
120
  * Babel-style visitor map keyed by a numeric node-type discriminator; a bucket
104
121
  * is a function (enter-only) or `{ enter?, exit? }`.
@@ -389,16 +406,14 @@ class PrintContext {
389
406
  _growStore(need) {
390
407
  // From empty: a context that prints one inline `style=""` must not pay for
391
408
  // a stylesheet's worth of columns.
392
- const size = Math.max(need, this._storeText.length * 2, 16);
393
- // Appended, which keeps the column packed for the read every composed child
394
- // makes, and carries what it already holds across in the same pass.
395
- const held = this._storeText;
396
- const text = [];
397
- for (let i = 0; i < held.length; i++) text.push(held[i]);
398
- for (let i = held.length; i < size; i++) text.push("");
409
+ const text = this._storeText;
410
+ const size = Math.max(need, text.length * 2, 16);
411
+ // Appended in place: `push` keeps the column packed for the read every
412
+ // composed child makes, and what it already holds never moves — where
413
+ // filling a second array would copy every slot again at each growth.
414
+ for (let i = text.length; i < size; i++) text.push("");
399
415
  const gen = new Int32Array(size);
400
416
  gen.set(this._storeGen);
401
- this._storeText = text;
402
417
  this._storeGen = gen;
403
418
  }
404
419
 
@@ -1142,4 +1157,5 @@ class SourceProcessor {
1142
1157
  SourceProcessor.PrintContext = PrintContext;
1143
1158
 
1144
1159
  module.exports = SourceProcessor;
1160
+ module.exports.declineDeferredWrites = declineDeferredWrites;
1145
1161
  module.exports.deferredWrite = deferredWrite;
@@ -13,9 +13,9 @@
13
13
  * while richer call sites can pass arrays of segments.
14
14
  *
15
15
  * TODO move this encoder into `webpack-sources` and replace the body of this
16
- * file with re-exports. The public shape (`encodeVLQ`, `encodeMappings(lines)`,
17
- * `MappingSegment`, `LineMappings`) is intended to match what would land
18
- * upstream so call sites don't have to change.
16
+ * file with re-exports. The public shape (`encodeVLQ`, `decodeVLQ`,
17
+ * `encodeMappings(lines)`, `MappingSegment`, `LineMappings`) is intended to
18
+ * match what would land upstream so call sites don't have to change.
19
19
  */
20
20
 
21
21
  const VLQ_BASE64 =
@@ -38,6 +38,27 @@ const encodeVLQ = (value) => {
38
38
  return result;
39
39
  };
40
40
 
41
+ /**
42
+ * Decode one base64 VLQ value per the source-map V3 spec.
43
+ * @param {string} mappings a VLQ encoded `mappings` string
44
+ * @param {number} start index of the value's first character
45
+ * @returns {{ value: number, end: number }} the decoded value and the index just past it; `end` equals `start` when there is no valid value at `start`
46
+ */
47
+ const decodeVLQ = (mappings, start) => {
48
+ let value = 0;
49
+ let shift = 0;
50
+ let index = start;
51
+ let digit;
52
+ do {
53
+ digit = index < mappings.length ? VLQ_BASE64.indexOf(mappings[index]) : -1;
54
+ if (digit < 0) return { value: 0, end: start };
55
+ value += (digit & 0x1f) << shift;
56
+ shift += 5;
57
+ index++;
58
+ } while (digit & 0x20);
59
+ return { value: value & 1 ? -(value >>> 1) : value >>> 1, end: index };
60
+ };
61
+
41
62
  /**
42
63
  * @typedef {object} MappingSegment
43
64
  * @property {number=} generatedColumn 0-based generated column (defaults to 0)
@@ -114,5 +135,6 @@ const encodeMappings = (lines) => {
114
135
  return encodedLines.join(";");
115
136
  };
116
137
 
138
+ module.exports.decodeVLQ = decodeVLQ;
117
139
  module.exports.encodeMappings = encodeMappings;
118
140
  module.exports.encodeVLQ = encodeVLQ;