@atlaspack/packager-js 2.14.5-canary.21 → 2.14.5-canary.211

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.
@@ -1,5 +1,3 @@
1
- // @flow
2
-
3
1
  import type {
4
2
  Asset,
5
3
  BundleGraph,
@@ -15,6 +13,7 @@ import {
15
13
  relativeBundlePath,
16
14
  countLines,
17
15
  normalizeSeparators,
16
+ debugTools,
18
17
  } from '@atlaspack/utils';
19
18
  import SourceMap from '@parcel/source-map';
20
19
  import nullthrows from 'nullthrows';
@@ -25,11 +24,18 @@ import ThrowableDiagnostic, {
25
24
  import globals from 'globals';
26
25
  import path from 'path';
27
26
  import {getFeatureFlag} from '@atlaspack/feature-flags';
27
+ import {outdent} from 'outdent';
28
28
 
29
29
  import {ESMOutputFormat} from './ESMOutputFormat';
30
30
  import {CJSOutputFormat} from './CJSOutputFormat';
31
31
  import {GlobalOutputFormat} from './GlobalOutputFormat';
32
- import {prelude, helpers, bundleQueuePrelude, fnExpr} from './helpers';
32
+ import {
33
+ preludeOld,
34
+ preludeNew,
35
+ helpers,
36
+ bundleQueuePrelude,
37
+ fnExpr,
38
+ } from './helpers';
33
39
  import {
34
40
  replaceScriptDependencies,
35
41
  getSpecifier,
@@ -39,7 +45,6 @@ import {
39
45
 
40
46
  // General regex used to replace imports with the resolved code, references with resolutions,
41
47
  // and count the number of newlines in the file for source maps.
42
- //
43
48
  // For conditional bundling the only difference in this regex is adding `importCond` where we have `importAsync` etc..
44
49
  const REPLACEMENT_RE_CONDITIONAL =
45
50
  /\n|import\s+"([0-9a-f]{16,20}:.+?)";|(?:\$[0-9a-f]{16,20}\$exports)|(?:\$[0-9a-f]{16,20}\$(?:import|importAsync|require|importCond)\$[0-9a-f]+(?:\$[0-9a-f]+)?)/g;
@@ -55,6 +60,7 @@ const GLOBALS_BY_CONTEXT = {
55
60
  ...Object.keys(globals.serviceworker),
56
61
  ]),
57
62
  worklet: new Set([...BUILTINS]),
63
+ tesseract: new Set([...BUILTINS, ...Object.keys(globals.worker)]),
58
64
  node: new Set([...BUILTINS, ...Object.keys(globals.node)]),
59
65
  'electron-main': new Set([...BUILTINS, ...Object.keys(globals.node)]),
60
66
  'electron-renderer': new Set([
@@ -62,13 +68,13 @@ const GLOBALS_BY_CONTEXT = {
62
68
  ...Object.keys(globals.node),
63
69
  ...Object.keys(globals.browser),
64
70
  ]),
65
- };
71
+ } as const;
66
72
 
67
73
  const OUTPUT_FORMATS = {
68
74
  esmodule: ESMOutputFormat,
69
75
  commonjs: CJSOutputFormat,
70
76
  global: GlobalOutputFormat,
71
- };
77
+ } as const;
72
78
 
73
79
  export interface OutputFormat {
74
80
  buildBundlePrelude(): [string, number];
@@ -83,27 +89,37 @@ export class ScopeHoistingPackager {
83
89
  useAsyncBundleRuntime: boolean;
84
90
  outputFormat: OutputFormat;
85
91
  isAsyncBundle: boolean;
86
- globalNames: $ReadOnlySet<string>;
87
- assetOutputs: Map<string, {|code: string, map: ?Buffer|}>;
92
+ globalNames: ReadonlySet<string>;
93
+ assetOutputs: Map<
94
+ Asset,
95
+ {
96
+ code: string;
97
+ map: Buffer | null | undefined;
98
+ }
99
+ > = new Map();
88
100
  exportedSymbols: Map<
89
101
  string,
90
- {|
91
- asset: Asset,
92
- exportSymbol: string,
93
- local: string,
94
- exportAs: Array<string>,
95
- |},
102
+ {
103
+ asset: Asset;
104
+ exportSymbol: string;
105
+ local: string;
106
+ exportAs: Array<string>;
107
+ }
96
108
  > = new Map();
97
109
  externals: Map<string, Map<string, string>> = new Map();
98
110
  topLevelNames: Map<string, number> = new Map();
99
- seenAssets: Set<string> = new Set();
100
- wrappedAssets: Set<string> = new Set();
101
- hoistedRequires: Map<string, Map<string, string>> = new Map();
111
+ seenAssets: Set<Asset> = new Set();
112
+ wrappedAssets: Set<Asset> = new Set();
113
+ constantAssets: Set<Asset> = new Set();
114
+ hoistedRequires: Map<Dependency, Map<Asset, string>> = new Map();
115
+ seenHoistedRequires: Set<string> = new Set();
102
116
  needsPrelude: boolean = false;
103
117
  usedHelpers: Set<string> = new Set();
104
118
  externalAssets: Set<Asset> = new Set();
105
- forceSkipWrapAssets: Array<string> = [];
106
119
  logger: PluginLogger;
120
+ useBothScopeHoistingImprovements: boolean =
121
+ getFeatureFlag('applyScopeHoistingImprovementV2') ||
122
+ getFeatureFlag('applyScopeHoistingImprovement');
107
123
 
108
124
  constructor(
109
125
  options: PluginOptions,
@@ -111,7 +127,6 @@ export class ScopeHoistingPackager {
111
127
  bundle: NamedBundle,
112
128
  parcelRequireName: string,
113
129
  useAsyncBundleRuntime: boolean,
114
- forceSkipWrapAssets: Array<string>,
115
130
  logger: PluginLogger,
116
131
  ) {
117
132
  this.options = options;
@@ -119,7 +134,6 @@ export class ScopeHoistingPackager {
119
134
  this.bundle = bundle;
120
135
  this.parcelRequireName = parcelRequireName;
121
136
  this.useAsyncBundleRuntime = useAsyncBundleRuntime;
122
- this.forceSkipWrapAssets = forceSkipWrapAssets ?? [];
123
137
  this.logger = logger;
124
138
 
125
139
  let OutputFormat = OUTPUT_FORMATS[this.bundle.env.outputFormat];
@@ -128,13 +142,17 @@ export class ScopeHoistingPackager {
128
142
  this.isAsyncBundle =
129
143
  this.bundleGraph.hasParentBundleOfType(this.bundle, 'js') &&
130
144
  !this.bundle.env.isIsolated() &&
131
- this.bundle.bundleBehavior !== 'isolated';
145
+ this.bundle.bundleBehavior !== 'isolated' &&
146
+ this.bundle.bundleBehavior !== 'inlineIsolated';
132
147
 
133
148
  this.globalNames = GLOBALS_BY_CONTEXT[bundle.env.context];
134
149
  }
135
150
 
136
- async package(): Promise<{|contents: string, map: ?SourceMap|}> {
137
- let wrappedAssets = await this.loadAssets();
151
+ async package(): Promise<{
152
+ contents: string;
153
+ map: SourceMap | null | undefined;
154
+ }> {
155
+ await this.loadAssets();
138
156
  this.buildExportedSymbols();
139
157
 
140
158
  // If building a library, the target is actually another bundler rather
@@ -155,10 +173,13 @@ export class ScopeHoistingPackager {
155
173
 
156
174
  let res = '';
157
175
  let lineCount = 0;
158
- let sourceMap = null;
159
- let processAsset = (asset) => {
176
+ let sourceMap: SourceMap | null | undefined = null;
177
+ let processAsset = (asset: Asset) => {
178
+ this.seenHoistedRequires.clear();
160
179
  let [content, map, lines] = this.visitAsset(asset);
180
+
161
181
  if (sourceMap && map) {
182
+ // @ts-expect-error TS2551 - addSourceMap method exists but missing from @parcel/source-map type definitions
162
183
  sourceMap.addSourceMap(map, lineCount);
163
184
  } else if (this.bundle.env.sourceMap) {
164
185
  sourceMap = map;
@@ -168,10 +189,22 @@ export class ScopeHoistingPackager {
168
189
  lineCount += lines + 1;
169
190
  };
170
191
 
192
+ if (
193
+ getFeatureFlag('inlineConstOptimisationFix') ||
194
+ this.useBothScopeHoistingImprovements
195
+ ) {
196
+ // Write out all constant modules used by this bundle
197
+ for (let asset of this.constantAssets) {
198
+ if (!this.seenAssets.has(asset)) {
199
+ processAsset(asset);
200
+ }
201
+ }
202
+ }
203
+
171
204
  // Hoist wrapped asset to the top of the bundle to ensure that they are registered
172
205
  // before they are used.
173
- for (let asset of wrappedAssets) {
174
- if (!this.seenAssets.has(asset.id)) {
206
+ for (let asset of this.wrappedAssets) {
207
+ if (!this.seenAssets.has(asset)) {
175
208
  processAsset(asset);
176
209
  }
177
210
  }
@@ -179,7 +212,7 @@ export class ScopeHoistingPackager {
179
212
  // Add each asset that is directly connected to the bundle. Dependencies will be handled
180
213
  // by replacing `import` statements in the code.
181
214
  this.bundle.traverseAssets((asset, _, actions) => {
182
- if (this.seenAssets.has(asset.id)) {
215
+ if (this.seenAssets.has(asset)) {
183
216
  actions.skipChildren();
184
217
  return;
185
218
  }
@@ -191,14 +224,28 @@ export class ScopeHoistingPackager {
191
224
  let [prelude, preludeLines] = this.buildBundlePrelude();
192
225
  res = prelude + res;
193
226
  lineCount += preludeLines;
227
+ // @ts-expect-error TS2339 - offsetLines method exists but missing from @parcel/source-map type definitions
194
228
  sourceMap?.offsetLines(1, preludeLines);
195
229
 
196
230
  let entries = this.bundle.getEntryAssets();
197
231
  let mainEntry = this.bundle.getMainEntry();
198
232
  if (this.isAsyncBundle) {
199
- // In async bundles we don't want the main entry to execute until we require it
200
- // as there might be dependencies in a sibling bundle that hasn't loaded yet.
201
- entries = entries.filter((a) => a.id !== mainEntry?.id);
233
+ if (
234
+ this.useBothScopeHoistingImprovements ||
235
+ getFeatureFlag('supportWebpackChunkName')
236
+ ) {
237
+ // Generally speaking, async bundles should not be executed on load, as
238
+ // they're just collections of assets that other assets require.
239
+ // However, there are some special cases where a runtime asset needs to be
240
+ // injected, but no other asset will require it (mostly the bundle
241
+ // manifest).
242
+ // In this case, those assets need to be required on load.
243
+ entries = entries.filter(
244
+ (a) => a.meta?.runtimeAssetRequiringExecutionOnLoad,
245
+ );
246
+ } else {
247
+ entries = entries.filter((a) => a.id !== mainEntry?.id);
248
+ }
202
249
  mainEntry = null;
203
250
  }
204
251
 
@@ -206,7 +253,7 @@ export class ScopeHoistingPackager {
206
253
 
207
254
  // If any of the entry assets are wrapped, call parcelRequire so they are executed.
208
255
  for (let entry of entries) {
209
- if (this.wrappedAssets.has(entry.id) && !this.isScriptEntry(entry)) {
256
+ if (this.wrappedAssets.has(entry) && !this.isScriptEntry(entry)) {
210
257
  let parcelRequire = `parcelRequire(${JSON.stringify(
211
258
  this.bundleGraph.getAssetPublicId(entry),
212
259
  )});\n`;
@@ -250,9 +297,7 @@ export class ScopeHoistingPackager {
250
297
  lineCount++;
251
298
 
252
299
  let mainEntry = nullthrows(this.bundle.getMainEntry());
253
- let {code, map: mapBuffer} = nullthrows(
254
- this.assetOutputs.get(mainEntry.id),
255
- );
300
+ let {code, map: mapBuffer} = nullthrows(this.assetOutputs.get(mainEntry));
256
301
  let map;
257
302
  if (mapBuffer) {
258
303
  map = new SourceMap(this.options.projectRoot, mapBuffer);
@@ -265,6 +310,7 @@ export class ScopeHoistingPackager {
265
310
  this.parcelRequireName,
266
311
  );
267
312
  if (sourceMap && map) {
313
+ // @ts-expect-error TS2339 - addSourceMap method exists but missing from @parcel/source-map type definitions
268
314
  sourceMap.addSourceMap(map, lineCount);
269
315
  }
270
316
  }
@@ -281,10 +327,7 @@ export class ScopeHoistingPackager {
281
327
 
282
328
  let hasConditionalReference = false;
283
329
  let isConditionalBundle = false;
284
- if (
285
- getFeatureFlag('conditionalBundlingApi') &&
286
- getFeatureFlag('conditionalBundlingAsyncRuntime')
287
- ) {
330
+ if (getFeatureFlag('conditionalBundlingApi')) {
288
331
  // If the bundle has a conditional bundle reference (has an importCond)
289
332
  hasConditionalReference =
290
333
  this.bundleGraph.getReferencedConditionalBundles(bundle).length > 0;
@@ -296,6 +339,7 @@ export class ScopeHoistingPackager {
296
339
  this.useAsyncBundleRuntime &&
297
340
  bundle.type === 'js' &&
298
341
  bundle.bundleBehavior !== 'inline' &&
342
+ bundle.bundleBehavior !== 'inlineIsolated' &&
299
343
  bundle.env.outputFormat === 'esmodule' &&
300
344
  !bundle.env.isIsolated() &&
301
345
  bundle.bundleBehavior !== 'isolated' &&
@@ -309,11 +353,8 @@ export class ScopeHoistingPackager {
309
353
  .filter((b) => this.shouldBundleQueue(b))
310
354
  .map((b) => b.publicId);
311
355
 
312
- const conditions = [];
313
- if (
314
- getFeatureFlag('conditionalBundlingApi') &&
315
- getFeatureFlag('conditionalBundlingAsyncRuntime')
316
- ) {
356
+ const conditions: Array<string> = [];
357
+ if (getFeatureFlag('conditionalBundlingApi')) {
317
358
  const conditionSet = this.bundleGraph
318
359
  .getConditionalBundleMapping()
319
360
  .get(bundle.id);
@@ -354,16 +395,20 @@ export class ScopeHoistingPackager {
354
395
  return `$parcel$global.rwr(${params.join(', ')});`;
355
396
  }
356
397
 
357
- async loadAssets(): Promise<Array<Asset>> {
358
- let queue = new PromiseQueue({maxConcurrent: 32});
359
- let wrapped = [];
398
+ async loadAssets() {
399
+ type QueueItem = [Asset, {code: string; map: Buffer | undefined | null}];
400
+ let queue = new PromiseQueue<QueueItem>({
401
+ maxConcurrent: 32,
402
+ });
403
+
360
404
  this.bundle.traverseAssets((asset) => {
361
405
  queue.add(async () => {
362
406
  let [code, map] = await Promise.all([
363
407
  asset.getCode(),
364
408
  this.bundle.env.sourceMap ? asset.getMapBuffer() : null,
365
409
  ]);
366
- return [asset.id, {code, map}];
410
+
411
+ return [asset, {code, map}];
367
412
  });
368
413
 
369
414
  if (
@@ -381,52 +426,113 @@ export class ScopeHoistingPackager {
381
426
  .getIncomingDependencies(asset)
382
427
  .some((dep) => dep.priority === 'lazy')
383
428
  ) {
384
- this.wrappedAssets.add(asset.id);
385
- wrapped.push(asset);
429
+ this.wrappedAssets.add(asset);
430
+ } else if (
431
+ (getFeatureFlag('inlineConstOptimisationFix') ||
432
+ this.useBothScopeHoistingImprovements) &&
433
+ asset.meta.isConstantModule
434
+ ) {
435
+ this.constantAssets.add(asset);
386
436
  }
387
437
  }
388
438
  });
389
439
 
390
- for (let wrappedAssetRoot of [...wrapped]) {
391
- this.bundle.traverseAssets((asset, _, actions) => {
392
- if (asset === wrappedAssetRoot) {
393
- return;
440
+ if (this.useBothScopeHoistingImprovements) {
441
+ // Tracks which assets have been assigned to a wrap group
442
+ let assignedAssets = new Set<Asset>();
443
+
444
+ // In V2 scope hoisting, we iterate from the main entry, rather than
445
+ // wrapping the entry assets
446
+ if (!getFeatureFlag('applyScopeHoistingImprovementV2')) {
447
+ // Make all entry assets wrapped, to avoid any top level hoisting
448
+ for (let entryAsset of this.bundle.getEntryAssets()) {
449
+ if (!this.wrappedAssets.has(entryAsset)) {
450
+ this.wrappedAssets.add(entryAsset);
451
+ }
394
452
  }
453
+ }
395
454
 
396
- if (this.wrappedAssets.has(asset.id)) {
397
- actions.skipChildren();
398
- return;
399
- }
400
- // This prevents children of a wrapped asset also being wrapped - it's an "unsafe" optimisation
401
- // that should only be used when you know (or think you know) what you're doing.
402
- //
403
- // In particular this can force an async bundle to be scope hoisted where it previously would not be
404
- // due to the entry asset being wrapped.
405
- if (
406
- this.forceSkipWrapAssets.length > 0 &&
407
- this.forceSkipWrapAssets.some(
408
- (p) =>
409
- p === path.relative(this.options.projectRoot, asset.filePath),
410
- )
411
- ) {
412
- this.logger.verbose({
413
- message: `Force skipping wrapping of ${path.relative(
414
- this.options.projectRoot,
415
- asset.filePath,
416
- )}`,
417
- });
418
- actions.skipChildren();
419
- return;
420
- }
421
- if (!asset.meta.isConstantModule) {
422
- this.wrappedAssets.add(asset.id);
423
- wrapped.push(asset);
455
+ // We need to make a new copy here so that we can add to the list and
456
+ // iterate the newly added items, without mutating the wrappedAssets set
457
+ let moduleGroupParents = [...this.wrappedAssets.values()];
458
+
459
+ if (getFeatureFlag('applyScopeHoistingImprovementV2')) {
460
+ // The main entry needs to be check to find assets that would have gone in
461
+ // the top level scope
462
+ let mainEntry = this.bundle.getMainEntry();
463
+ if (mainEntry && !this.wrappedAssets.has(mainEntry)) {
464
+ moduleGroupParents.unshift(mainEntry);
424
465
  }
425
- }, wrappedAssetRoot);
466
+ }
467
+
468
+ for (let moduleGroupParentAsset of moduleGroupParents) {
469
+ this.bundle.traverseAssets((asset, _, actions) => {
470
+ if (asset === moduleGroupParentAsset) {
471
+ return;
472
+ }
473
+
474
+ if (this.wrappedAssets.has(asset)) {
475
+ actions.skipChildren();
476
+ return;
477
+ }
478
+
479
+ if (
480
+ !asset.meta.isConstantModule &&
481
+ (assignedAssets.has(asset) || this.isReExported(asset))
482
+ ) {
483
+ this.wrappedAssets.add(asset);
484
+
485
+ // This also needs to be added to the traversal so that we iterate
486
+ // it during this check.
487
+ moduleGroupParents.push(asset);
488
+
489
+ actions.skipChildren();
490
+ return;
491
+ }
492
+
493
+ assignedAssets.add(asset);
494
+ }, moduleGroupParentAsset);
495
+ }
496
+ } else {
497
+ for (let wrappedAssetRoot of this.wrappedAssets) {
498
+ this.bundle.traverseAssets((asset, _, actions) => {
499
+ if (asset === wrappedAssetRoot) {
500
+ return;
501
+ }
502
+
503
+ if (this.wrappedAssets.has(asset)) {
504
+ actions.skipChildren();
505
+ return;
506
+ }
507
+
508
+ if (!asset.meta.isConstantModule) {
509
+ this.wrappedAssets.add(asset);
510
+ }
511
+ }, wrappedAssetRoot);
512
+ }
426
513
  }
427
514
 
428
515
  this.assetOutputs = new Map(await queue.run());
429
- return wrapped;
516
+ }
517
+
518
+ isReExported(asset: Asset): boolean {
519
+ let parentSymbols = this.bundleGraph
520
+ .getIncomingDependencies(asset)
521
+ .map((dep) => this.bundleGraph.getAssetWithDependency(dep))
522
+ .flatMap((parent) => {
523
+ if (parent == null) {
524
+ return [];
525
+ }
526
+ return this.bundleGraph.getExportedSymbols(parent, this.bundle);
527
+ });
528
+
529
+ let assetSymbols = this.bundleGraph.getExportedSymbols(asset, this.bundle);
530
+
531
+ return assetSymbols.some((assetSymbol) =>
532
+ parentSymbols.some(
533
+ (parentSymbol) => parentSymbol.symbol === assetSymbol.symbol,
534
+ ),
535
+ );
430
536
  }
431
537
 
432
538
  buildExportedSymbols() {
@@ -439,7 +545,7 @@ export class ScopeHoistingPackager {
439
545
 
440
546
  // TODO: handle ESM exports of wrapped entry assets...
441
547
  let entry = this.bundle.getMainEntry();
442
- if (entry && !this.wrappedAssets.has(entry.id)) {
548
+ if (entry && !this.wrappedAssets.has(entry)) {
443
549
  let hasNamespace = entry.symbols.hasExportSymbol('*');
444
550
 
445
551
  for (let {
@@ -464,6 +570,7 @@ export class ScopeHoistingPackager {
464
570
  symbols = [];
465
571
  this.exportedSymbols.set(symbol, {
466
572
  asset,
573
+
467
574
  exportSymbol,
468
575
  local: symbol,
469
576
  exportAs: symbols,
@@ -519,20 +626,24 @@ export class ScopeHoistingPackager {
519
626
  return `${obj}[${JSON.stringify(property)}]`;
520
627
  }
521
628
 
522
- visitAsset(asset: Asset): [string, ?SourceMap, number] {
523
- invariant(!this.seenAssets.has(asset.id), 'Already visited asset');
524
- this.seenAssets.add(asset.id);
629
+ visitAsset(asset: Asset): [string, SourceMap | null | undefined, number] {
630
+ invariant(!this.seenAssets.has(asset), 'Already visited asset');
631
+ this.seenAssets.add(asset);
525
632
 
526
- let {code, map} = nullthrows(this.assetOutputs.get(asset.id));
633
+ let {code, map} = nullthrows(this.assetOutputs.get(asset));
527
634
  return this.buildAsset(asset, code, map);
528
635
  }
529
636
 
637
+ getAssetFilePath(asset: Asset): string {
638
+ return path.relative(this.options.projectRoot, asset.filePath);
639
+ }
640
+
530
641
  buildAsset(
531
642
  asset: Asset,
532
643
  code: string,
533
- map: ?Buffer,
534
- ): [string, ?SourceMap, number] {
535
- let shouldWrap = this.wrappedAssets.has(asset.id);
644
+ map?: Buffer | null,
645
+ ): [string, SourceMap | null | undefined, number] {
646
+ let shouldWrap = this.wrappedAssets.has(asset);
536
647
  let deps = this.bundleGraph.getDependencies(asset);
537
648
 
538
649
  let sourceMap =
@@ -559,16 +670,24 @@ export class ScopeHoistingPackager {
559
670
  continue;
560
671
  }
561
672
 
562
- if (
563
- this.bundle.hasAsset(resolved) &&
564
- !this.seenAssets.has(resolved.id)
565
- ) {
566
- let [code, map, lines] = this.visitAsset(resolved);
567
- depCode += code + '\n';
568
- if (sourceMap && map) {
569
- sourceMap.addSourceMap(map, lineCount);
673
+ if (this.bundle.hasAsset(resolved) && !this.seenAssets.has(resolved)) {
674
+ if (
675
+ this.useBothScopeHoistingImprovements &&
676
+ this.wrappedAssets.has(resolved)
677
+ ) {
678
+ // When the dep is wrapped then we just need to drop a side effect
679
+ // require instead of inlining
680
+ depCode += `parcelRequire("${this.bundleGraph.getAssetPublicId(resolved)}");\n`;
681
+ lineCount += 1;
682
+ } else {
683
+ let [code, map, lines] = this.visitAsset(resolved);
684
+ depCode += code + '\n';
685
+ if (sourceMap && map) {
686
+ // @ts-expect-error TS2551 - addSourceMap method exists but missing from @parcel/source-map type definitions
687
+ sourceMap.addSourceMap(map, lineCount);
688
+ }
689
+ lineCount += lines + 1;
570
690
  }
571
- lineCount += lines + 1;
572
691
  }
573
692
  }
574
693
 
@@ -602,7 +721,7 @@ export class ScopeHoistingPackager {
602
721
  code += append;
603
722
 
604
723
  let lineCount = 0;
605
- let depContent = [];
724
+ let depContent: Array<[string, SourceMap | null | undefined, number]> = [];
606
725
  if (depMap.size === 0 && replacements.size === 0) {
607
726
  // If there are no dependencies or replacements, use a simple function to count the number of lines.
608
727
  lineCount = countLines(code) - 1;
@@ -647,27 +766,68 @@ export class ScopeHoistingPackager {
647
766
  // after the dependency is declared. This handles the case where the resulting asset
648
767
  // is wrapped, but the dependency in this asset is not marked as wrapped. This means
649
768
  // that it was imported/required at the top-level, so its side effects should run immediately.
650
- let [res, lines] = this.getHoistedParcelRequires(
651
- asset,
652
- dep,
653
- resolved,
654
- );
769
+ let res = '';
770
+ let lines = 0;
655
771
  let map;
772
+
773
+ if (!getFeatureFlag('applyScopeHoistingImprovementV2')) {
774
+ [res, lines] = this.getHoistedParcelRequires(
775
+ asset,
776
+ dep,
777
+ resolved,
778
+ );
779
+ }
780
+
656
781
  if (
657
782
  this.bundle.hasAsset(resolved) &&
658
- !this.seenAssets.has(resolved.id)
783
+ !this.seenAssets.has(resolved)
659
784
  ) {
660
785
  // If this asset is wrapped, we need to hoist the code for the dependency
661
786
  // outside our parcelRequire.register wrapper. This is safe because all
662
787
  // assets referenced by this asset will also be wrapped. Otherwise, inline the
663
788
  // asset content where the import statement was.
664
- if (shouldWrap) {
665
- depContent.push(this.visitAsset(resolved));
789
+ if (this.useBothScopeHoistingImprovements) {
790
+ if (
791
+ !resolved.meta.isConstantModule &&
792
+ !this.wrappedAssets.has(resolved)
793
+ ) {
794
+ let [depCode, depMap, depLines] =
795
+ this.visitAsset(resolved);
796
+ if (debugTools['asset-file-names-in-output']) {
797
+ let resolvedPath = this.getAssetFilePath(resolved);
798
+ res = outdent`
799
+ /* Scope hoisted asset: ${resolvedPath} */
800
+ ${depCode}
801
+ /* End: ${resolvedPath} */
802
+ ${res}
803
+ `;
804
+ lines += 3 + depLines;
805
+ } else {
806
+ res = depCode + '\n' + res;
807
+ lines += 1 + depLines;
808
+ }
809
+ map = depMap;
810
+ }
666
811
  } else {
667
- let [depCode, depMap, depLines] = this.visitAsset(resolved);
668
- res = depCode + '\n' + res;
669
- lines += 1 + depLines;
670
- map = depMap;
812
+ if (shouldWrap) {
813
+ depContent.push(this.visitAsset(resolved));
814
+ } else {
815
+ let [depCode, depMap, depLines] =
816
+ this.visitAsset(resolved);
817
+ res = depCode + '\n' + res;
818
+ lines += 1 + depLines;
819
+ map = depMap;
820
+ }
821
+ }
822
+ }
823
+
824
+ if (getFeatureFlag('applyScopeHoistingImprovementV2')) {
825
+ let [requiresCode, requiresLines] =
826
+ this.getHoistedParcelRequires(asset, dep, resolved);
827
+
828
+ if (requiresCode) {
829
+ res = requiresCode + '\n' + res;
830
+ lines += requiresLines + 1;
671
831
  }
672
832
  }
673
833
 
@@ -679,6 +839,7 @@ export class ScopeHoistingPackager {
679
839
  }
680
840
 
681
841
  if (map) {
842
+ // @ts-expect-error TS2551 - addSourceMap method exists but missing from @parcel/source-map type definitions
682
843
  sourceMap.addSourceMap(map, lineCount);
683
844
  }
684
845
  }
@@ -726,10 +887,16 @@ ${code}
726
887
 
727
888
  lineCount += 2;
728
889
 
890
+ if (debugTools['asset-file-names-in-output']) {
891
+ code = `/* ${this.getAssetFilePath(asset)} */\n` + code;
892
+ lineCount += 1;
893
+ }
894
+
729
895
  for (let [depCode, map, lines] of depContent) {
730
896
  if (!depCode) continue;
731
897
  code += depCode + '\n';
732
898
  if (sourceMap && map) {
899
+ // @ts-expect-error TS2551 - addSourceMap method exists but missing from @parcel/source-map type definitions
733
900
  sourceMap.addSourceMap(map, lineCount);
734
901
  }
735
902
  lineCount += lines + 1;
@@ -848,7 +1015,7 @@ ${code}
848
1015
  // If this asset is wrapped, we need to replace the exports namespace with `module.exports`,
849
1016
  // which will be provided to us by the wrapper.
850
1017
  if (
851
- this.wrappedAssets.has(asset.id) ||
1018
+ this.wrappedAssets.has(asset) ||
852
1019
  (this.bundle.env.outputFormat === 'commonjs' &&
853
1020
  asset === this.bundle.getMainEntry())
854
1021
  ) {
@@ -895,7 +1062,9 @@ ${code}
895
1062
 
896
1063
  for (let [imported, {local}] of dep.symbols) {
897
1064
  // If already imported, just add the already renamed variable to the mapping.
1065
+
898
1066
  let renamed = external.get(imported);
1067
+
899
1068
  if (renamed && local !== '*' && replacements) {
900
1069
  replacements.set(local, renamed);
901
1070
  continue;
@@ -908,6 +1077,7 @@ ${code}
908
1077
  if (!renamed) {
909
1078
  if (referencedBundle) {
910
1079
  let entry = nullthrows(referencedBundle.getMainEntry());
1080
+
911
1081
  renamed =
912
1082
  entry.symbols.get('*')?.local ??
913
1083
  `$${String(entry.meta.id)}$exports`;
@@ -922,6 +1092,7 @@ ${code}
922
1092
 
923
1093
  if (local !== '*' && replacements) {
924
1094
  let replacement;
1095
+
925
1096
  if (imported === '*') {
926
1097
  replacement = renamed;
927
1098
  } else if (imported === 'default') {
@@ -946,10 +1117,12 @@ ${code}
946
1117
  let property;
947
1118
  if (referencedBundle) {
948
1119
  let entry = nullthrows(referencedBundle.getMainEntry());
1120
+
949
1121
  if (entry.symbols.hasExportSymbol('*')) {
950
1122
  // If importing * and the referenced module has a * export (e.g. CJS), use default instead.
951
1123
  // This mirrors the logic in buildExportedSymbols.
952
1124
  property = imported;
1125
+
953
1126
  imported =
954
1127
  referencedBundle?.env.outputFormat === 'esmodule'
955
1128
  ? 'default'
@@ -957,6 +1130,7 @@ ${code}
957
1130
  } else {
958
1131
  if (imported === '*') {
959
1132
  let exportedSymbols = this.bundleGraph.getExportedSymbols(entry);
1133
+
960
1134
  if (local === '*') {
961
1135
  // Re-export all symbols.
962
1136
  for (let exported of exportedSymbols) {
@@ -967,11 +1141,10 @@ ${code}
967
1141
  continue;
968
1142
  }
969
1143
  }
970
- renamed = this.bundleGraph.getSymbolResolution(
971
- entry,
972
- imported,
973
- this.bundle,
974
- ).symbol;
1144
+
1145
+ renamed =
1146
+ this.bundleGraph.getSymbolResolution(entry, imported, this.bundle)
1147
+ .symbol || undefined;
975
1148
  }
976
1149
  }
977
1150
 
@@ -993,8 +1166,10 @@ ${code}
993
1166
  }
994
1167
 
995
1168
  external.set(imported, renamed);
1169
+
996
1170
  if (local !== '*' && replacements) {
997
1171
  let replacement = renamed;
1172
+
998
1173
  if (property === '*') {
999
1174
  replacement = renamed;
1000
1175
  } else if (property === 'default') {
@@ -1003,6 +1178,7 @@ ${code}
1003
1178
  } else if (property) {
1004
1179
  replacement = this.getPropertyAccess(renamed, property);
1005
1180
  }
1181
+
1006
1182
  replacements.set(local, replacement);
1007
1183
  }
1008
1184
  }
@@ -1026,7 +1202,7 @@ ${code}
1026
1202
  }
1027
1203
  return (
1028
1204
  (!this.bundle.hasAsset(resolved) && !this.externalAssets.has(resolved)) ||
1029
- (this.wrappedAssets.has(resolved.id) && resolved !== parentAsset)
1205
+ (this.wrappedAssets.has(resolved) && resolved !== parentAsset)
1030
1206
  );
1031
1207
  }
1032
1208
 
@@ -1076,14 +1252,14 @@ ${code}
1076
1252
  (!this.bundle.hasAsset(resolvedAsset) ||
1077
1253
  !this.shouldSkipAsset(resolvedAsset))
1078
1254
  ) {
1079
- let hoisted = this.hoistedRequires.get(dep.id);
1255
+ let hoisted = this.hoistedRequires.get(dep);
1080
1256
  if (!hoisted) {
1081
1257
  hoisted = new Map();
1082
- this.hoistedRequires.set(dep.id, hoisted);
1258
+ this.hoistedRequires.set(dep, hoisted);
1083
1259
  }
1084
1260
 
1085
1261
  hoisted.set(
1086
- resolvedAsset.id,
1262
+ resolvedAsset,
1087
1263
  `var $${publicId} = parcelRequire(${JSON.stringify(publicId)});`,
1088
1264
  );
1089
1265
  }
@@ -1117,6 +1293,7 @@ ${code}
1117
1293
  obj = `$${publicId}`;
1118
1294
  } else {
1119
1295
  obj = resolvedAsset.symbols.get('*')?.local || `$${assetId}$exports`;
1296
+
1120
1297
  obj = replacements?.get(obj) || obj;
1121
1298
  }
1122
1299
 
@@ -1124,7 +1301,7 @@ ${code}
1124
1301
  // Resolve to the namespace object if requested or this is a CJS default interop reqiure.
1125
1302
  if (
1126
1303
  parentAsset === resolvedAsset &&
1127
- this.wrappedAssets.has(resolvedAsset.id)
1304
+ this.wrappedAssets.has(resolvedAsset)
1128
1305
  ) {
1129
1306
  // Directly use module.exports for wrapped assets importing themselves.
1130
1307
  return 'module.exports';
@@ -1167,7 +1344,7 @@ ${code}
1167
1344
  return ['', 0];
1168
1345
  }
1169
1346
 
1170
- let hoisted = this.hoistedRequires.get(dep.id);
1347
+ let hoisted = this.hoistedRequires.get(dep);
1171
1348
  let res = '';
1172
1349
  let lineCount = 0;
1173
1350
  let isWrapped = this.isWrapped(resolved, parentAsset);
@@ -1179,7 +1356,7 @@ ${code}
1179
1356
  if (
1180
1357
  isWrapped &&
1181
1358
  !dep.meta.shouldWrap &&
1182
- (!hoisted || hoisted.keys().next().value !== resolved.id) &&
1359
+ (!hoisted || hoisted.keys().next().value !== resolved) &&
1183
1360
  !this.bundleGraph.isDependencySkipped(dep) &&
1184
1361
  !this.shouldSkipAsset(resolved)
1185
1362
  ) {
@@ -1191,8 +1368,22 @@ ${code}
1191
1368
 
1192
1369
  if (hoisted) {
1193
1370
  this.needsPrelude = true;
1194
- res += '\n' + [...hoisted.values()].join('\n');
1195
- lineCount += hoisted.size;
1371
+
1372
+ if (getFeatureFlag('applyScopeHoistingImprovementV2')) {
1373
+ let hoistedValues = [...hoisted.values()].filter(
1374
+ (val) => !this.seenHoistedRequires.has(val),
1375
+ );
1376
+
1377
+ for (let val of hoistedValues) {
1378
+ this.seenHoistedRequires.add(val);
1379
+ }
1380
+
1381
+ res += '\n' + hoistedValues.join('\n');
1382
+ lineCount += hoisted.size;
1383
+ } else {
1384
+ res += '\n' + [...hoisted.values()].join('\n');
1385
+ lineCount += hoisted.size;
1386
+ }
1196
1387
  }
1197
1388
 
1198
1389
  return [res, lineCount];
@@ -1207,7 +1398,7 @@ ${code}
1207
1398
  let prependLineCount = 0;
1208
1399
  let append = '';
1209
1400
 
1210
- let shouldWrap = this.wrappedAssets.has(asset.id);
1401
+ let shouldWrap = this.wrappedAssets.has(asset);
1211
1402
  let usedSymbols = nullthrows(this.bundleGraph.getUsedSymbols(asset));
1212
1403
  let assetId = asset.meta.id;
1213
1404
  invariant(typeof assetId === 'string');
@@ -1220,34 +1411,51 @@ ${code}
1220
1411
  usedSymbols.has('default') &&
1221
1412
  !asset.symbols.hasExportSymbol('__esModule');
1222
1413
 
1223
- let usedNamespace =
1224
- // If the asset has * in its used symbols, we might need the exports namespace.
1225
- // The one case where this isn't true is in ESM library entries, where the only
1226
- // dependency on * is the entry dependency. In this case, we will use ESM exports
1227
- // instead of the namespace object.
1228
- (usedSymbols.has('*') &&
1229
- (this.bundle.env.outputFormat !== 'esmodule' ||
1230
- !this.bundle.env.isLibrary ||
1231
- asset !== this.bundle.getMainEntry() ||
1232
- this.bundleGraph
1233
- .getIncomingDependencies(asset)
1234
- .some(
1235
- (dep) =>
1236
- !dep.isEntry &&
1237
- this.bundle.hasDependency(dep) &&
1238
- nullthrows(this.bundleGraph.getUsedSymbols(dep)).has('*'),
1239
- ))) ||
1240
- // If a symbol is imported (used) from a CJS asset but isn't listed in the symbols,
1241
- // we fallback on the namespace object.
1242
- (asset.symbols.hasExportSymbol('*') &&
1243
- [...usedSymbols].some((s) => !asset.symbols.hasExportSymbol(s))) ||
1244
- // If the exports has this asset's namespace (e.g. ESM output from CJS input),
1245
- // include the namespace object for the default export.
1246
- this.exportedSymbols.has(`$${assetId}$exports`) ||
1247
- // CommonJS library bundle entries always need a namespace.
1248
- (this.bundle.env.isLibrary &&
1249
- this.bundle.env.outputFormat === 'commonjs' &&
1250
- asset === this.bundle.getMainEntry());
1414
+ let usedNamespace;
1415
+ if (
1416
+ getFeatureFlag('inlineConstOptimisationFix') &&
1417
+ asset.meta.isConstantModule
1418
+ ) {
1419
+ // Only set usedNamespace if there is an incoming dependency in the current bundle that uses '*'
1420
+ usedNamespace = this.bundleGraph
1421
+ .getIncomingDependencies(asset)
1422
+ .some(
1423
+ (dep) =>
1424
+ this.bundle.hasDependency(dep) &&
1425
+ nullthrows(this.bundleGraph.getUsedSymbols(dep)).has('*'),
1426
+ );
1427
+ } else {
1428
+ usedNamespace =
1429
+ // If the asset has * in its used symbols, we might need the exports namespace.
1430
+ // The one case where this isn't true is in ESM library entries, where the only
1431
+ // dependency on * is the entry dependency. In this case, we will use ESM exports
1432
+ // instead of the namespace object.
1433
+
1434
+ (usedSymbols.has('*') &&
1435
+ (this.bundle.env.outputFormat !== 'esmodule' ||
1436
+ !this.bundle.env.isLibrary ||
1437
+ asset !== this.bundle.getMainEntry() ||
1438
+ this.bundleGraph
1439
+ .getIncomingDependencies(asset)
1440
+ .some(
1441
+ (dep) =>
1442
+ !dep.isEntry &&
1443
+ this.bundle.hasDependency(dep) &&
1444
+ nullthrows(this.bundleGraph.getUsedSymbols(dep)).has('*'),
1445
+ ))) ||
1446
+ // If a symbol is imported (used) from a CJS asset but isn't listed in the symbols,
1447
+ // we fallback on the namespace object.
1448
+
1449
+ (asset.symbols.hasExportSymbol('*') &&
1450
+ [...usedSymbols].some((s) => !asset.symbols.hasExportSymbol(s))) ||
1451
+ // If the exports has this asset's namespace (e.g. ESM output from CJS input),
1452
+ // include the namespace object for the default export.
1453
+ this.exportedSymbols.has(`$${assetId}$exports`) ||
1454
+ // CommonJS library bundle entries always need a namespace.
1455
+ (this.bundle.env.isLibrary &&
1456
+ this.bundle.env.outputFormat === 'commonjs' &&
1457
+ asset === this.bundle.getMainEntry());
1458
+ }
1251
1459
 
1252
1460
  // If the asset doesn't have static exports, should wrap, the namespace is used,
1253
1461
  // or we need default interop, then we need to synthesize a namespace object for
@@ -1274,6 +1482,7 @@ ${code}
1274
1482
  // Insert the __esModule interop flag for this module if it has a `default` export
1275
1483
  // and the namespace symbol is used.
1276
1484
  // TODO: only if required by CJS?
1485
+
1277
1486
  if (asset.symbols.hasExportSymbol('default') && usedSymbols.has('*')) {
1278
1487
  prepend += `\n$parcel$defineInteropFlag($${assetId}$exports);\n`;
1279
1488
  prependLineCount += 2;
@@ -1337,6 +1546,7 @@ ${code}
1337
1546
  let resolvedSymbol = this.getSymbolResolution(
1338
1547
  asset,
1339
1548
  resolved,
1549
+
1340
1550
  symbol,
1341
1551
  undefined,
1342
1552
  replacements,
@@ -1388,28 +1598,36 @@ ${code}
1388
1598
  // for the symbol so that when the value changes the object property also changes. This is
1389
1599
  // required to simulate ESM live bindings. It's easier to do it this way rather than inserting
1390
1600
  // additional assignments after each mutation of the original binding.
1391
- prepend += `\n${usedExports
1392
- .map((exp) => {
1393
- let resolved = this.getSymbolResolution(
1394
- asset,
1395
- asset,
1601
+ for (let exp of usedExports) {
1602
+ let resolved = this.getSymbolResolution(
1603
+ asset,
1604
+ asset,
1605
+ exp,
1606
+ undefined,
1607
+ replacements,
1608
+ );
1609
+ const meta = asset.symbols.get(exp)?.meta;
1610
+ if (
1611
+ getFeatureFlag('exportsRebindingOptimisation') &&
1612
+ meta?.isStaticBindingSafe
1613
+ ) {
1614
+ append += `$${assetId}$exports[${JSON.stringify(
1396
1615
  exp,
1397
- undefined,
1398
- replacements,
1399
- );
1616
+ )}] = ${resolved};\n`;
1617
+ } else {
1400
1618
  let get = this.buildFunctionExpression([], resolved);
1401
1619
  let isEsmExport = !!asset.symbols.get(exp)?.meta?.isEsm;
1402
1620
  let set =
1403
1621
  !isEsmExport && asset.meta.hasCJSExports
1404
1622
  ? ', ' + this.buildFunctionExpression(['v'], `${resolved} = v`)
1405
1623
  : '';
1406
- return `$parcel$export($${assetId}$exports, ${JSON.stringify(
1624
+ prepend += `$parcel$export($${assetId}$exports, ${JSON.stringify(
1407
1625
  exp,
1408
- )}, ${get}${set});`;
1409
- })
1410
- .join('\n')}\n`;
1411
- this.usedHelpers.add('$parcel$export');
1412
- prependLineCount += 1 + usedExports.length;
1626
+ )}, ${get}${set});\n`;
1627
+ this.usedHelpers.add('$parcel$export');
1628
+ prependLineCount += 1 + usedExports.length;
1629
+ }
1630
+ }
1413
1631
  }
1414
1632
  }
1415
1633
 
@@ -1448,9 +1666,11 @@ ${code}
1448
1666
  }
1449
1667
 
1450
1668
  for (let helper of this.usedHelpers) {
1451
- let currentHelper = helpers[helper];
1669
+ let currentHelper = (helpers as Record<string, any>)[helper];
1452
1670
  if (typeof currentHelper === 'function') {
1453
- currentHelper = helpers[helper](this.bundle.env);
1671
+ currentHelper = (helpers as Record<string, any>)[helper](
1672
+ this.bundle.env,
1673
+ );
1454
1674
  }
1455
1675
  res += currentHelper;
1456
1676
  if (enableSourceMaps) {
@@ -1470,11 +1690,14 @@ ${code}
1470
1690
  .some((g) => this.bundleGraph.isEntryBundleGroup(g)) ||
1471
1691
  this.bundle.env.isIsolated() ||
1472
1692
  this.bundle.bundleBehavior === 'isolated' ||
1693
+ this.bundle.bundleBehavior === 'inlineIsolated' ||
1473
1694
  // Conditional deps may be loaded before entrypoints on the server
1474
1695
  this.hasConditionalDependency();
1475
1696
 
1476
1697
  if (mightBeFirstJS) {
1477
- let preludeCode = prelude(this.parcelRequireName);
1698
+ let preludeCode = (
1699
+ getFeatureFlag('useNewPrelude') ? preludeNew : preludeOld
1700
+ )(this.parcelRequireName);
1478
1701
  res += preludeCode;
1479
1702
  if (enableSourceMaps) {
1480
1703
  lines += countLines(preludeCode) - 1;
@@ -1498,7 +1721,11 @@ ${code}
1498
1721
  }
1499
1722
 
1500
1723
  // Add importScripts for sibling bundles in workers.
1501
- if (this.bundle.env.isWorker() || this.bundle.env.isWorklet()) {
1724
+ if (
1725
+ this.bundle.env.isWorker() ||
1726
+ this.bundle.env.isTesseract() ||
1727
+ this.bundle.env.isWorklet()
1728
+ ) {
1502
1729
  let importScripts = '';
1503
1730
  let bundles = this.bundleGraph.getReferencedBundles(this.bundle);
1504
1731
  for (let b of bundles) {