@atlaspack/packager-js 2.14.5-canary.22 → 2.14.5-canary.221

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,8 @@ import {
15
13
  relativeBundlePath,
16
14
  countLines,
17
15
  normalizeSeparators,
16
+ debugTools,
17
+ globToRegex,
18
18
  } from '@atlaspack/utils';
19
19
  import SourceMap from '@parcel/source-map';
20
20
  import nullthrows from 'nullthrows';
@@ -25,11 +25,18 @@ import ThrowableDiagnostic, {
25
25
  import globals from 'globals';
26
26
  import path from 'path';
27
27
  import {getFeatureFlag} from '@atlaspack/feature-flags';
28
+ import {outdent} from 'outdent';
28
29
 
29
30
  import {ESMOutputFormat} from './ESMOutputFormat';
30
31
  import {CJSOutputFormat} from './CJSOutputFormat';
31
32
  import {GlobalOutputFormat} from './GlobalOutputFormat';
32
- import {prelude, helpers, bundleQueuePrelude, fnExpr} from './helpers';
33
+ import {
34
+ preludeOld,
35
+ preludeNew,
36
+ helpers,
37
+ bundleQueuePrelude,
38
+ fnExpr,
39
+ } from './helpers';
33
40
  import {
34
41
  replaceScriptDependencies,
35
42
  getSpecifier,
@@ -39,7 +46,6 @@ import {
39
46
 
40
47
  // General regex used to replace imports with the resolved code, references with resolutions,
41
48
  // and count the number of newlines in the file for source maps.
42
- //
43
49
  // For conditional bundling the only difference in this regex is adding `importCond` where we have `importAsync` etc..
44
50
  const REPLACEMENT_RE_CONDITIONAL =
45
51
  /\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 +61,7 @@ const GLOBALS_BY_CONTEXT = {
55
61
  ...Object.keys(globals.serviceworker),
56
62
  ]),
57
63
  worklet: new Set([...BUILTINS]),
64
+ tesseract: new Set([...BUILTINS, ...Object.keys(globals.worker)]),
58
65
  node: new Set([...BUILTINS, ...Object.keys(globals.node)]),
59
66
  'electron-main': new Set([...BUILTINS, ...Object.keys(globals.node)]),
60
67
  'electron-renderer': new Set([
@@ -62,13 +69,13 @@ const GLOBALS_BY_CONTEXT = {
62
69
  ...Object.keys(globals.node),
63
70
  ...Object.keys(globals.browser),
64
71
  ]),
65
- };
72
+ } as const;
66
73
 
67
74
  const OUTPUT_FORMATS = {
68
75
  esmodule: ESMOutputFormat,
69
76
  commonjs: CJSOutputFormat,
70
77
  global: GlobalOutputFormat,
71
- };
78
+ } as const;
72
79
 
73
80
  export interface OutputFormat {
74
81
  buildBundlePrelude(): [string, number];
@@ -83,27 +90,38 @@ export class ScopeHoistingPackager {
83
90
  useAsyncBundleRuntime: boolean;
84
91
  outputFormat: OutputFormat;
85
92
  isAsyncBundle: boolean;
86
- globalNames: $ReadOnlySet<string>;
87
- assetOutputs: Map<string, {|code: string, map: ?Buffer|}>;
93
+ globalNames: ReadonlySet<string>;
94
+ manualStaticBindingExports: RegExp[] | null;
95
+ assetOutputs: Map<
96
+ Asset,
97
+ {
98
+ code: string;
99
+ map: Buffer | null | undefined;
100
+ }
101
+ > = new Map();
88
102
  exportedSymbols: Map<
89
103
  string,
90
- {|
91
- asset: Asset,
92
- exportSymbol: string,
93
- local: string,
94
- exportAs: Array<string>,
95
- |},
104
+ {
105
+ asset: Asset;
106
+ exportSymbol: string;
107
+ local: string;
108
+ exportAs: Array<string>;
109
+ }
96
110
  > = new Map();
97
111
  externals: Map<string, Map<string, string>> = new Map();
98
112
  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();
113
+ seenAssets: Set<Asset> = new Set();
114
+ wrappedAssets: Set<Asset> = new Set();
115
+ constantAssets: Set<Asset> = new Set();
116
+ hoistedRequires: Map<Dependency, Map<Asset, string>> = new Map();
117
+ seenHoistedRequires: Set<string> = new Set();
102
118
  needsPrelude: boolean = false;
103
119
  usedHelpers: Set<string> = new Set();
104
120
  externalAssets: Set<Asset> = new Set();
105
- forceSkipWrapAssets: Array<string> = [];
106
121
  logger: PluginLogger;
122
+ useBothScopeHoistingImprovements: boolean =
123
+ getFeatureFlag('applyScopeHoistingImprovementV2') ||
124
+ getFeatureFlag('applyScopeHoistingImprovement');
107
125
 
108
126
  constructor(
109
127
  options: PluginOptions,
@@ -111,7 +129,7 @@ export class ScopeHoistingPackager {
111
129
  bundle: NamedBundle,
112
130
  parcelRequireName: string,
113
131
  useAsyncBundleRuntime: boolean,
114
- forceSkipWrapAssets: Array<string>,
132
+ manualStaticBindingExports: string[] | null,
115
133
  logger: PluginLogger,
116
134
  ) {
117
135
  this.options = options;
@@ -119,7 +137,8 @@ export class ScopeHoistingPackager {
119
137
  this.bundle = bundle;
120
138
  this.parcelRequireName = parcelRequireName;
121
139
  this.useAsyncBundleRuntime = useAsyncBundleRuntime;
122
- this.forceSkipWrapAssets = forceSkipWrapAssets ?? [];
140
+ this.manualStaticBindingExports =
141
+ manualStaticBindingExports?.map((glob) => globToRegex(glob)) ?? null;
123
142
  this.logger = logger;
124
143
 
125
144
  let OutputFormat = OUTPUT_FORMATS[this.bundle.env.outputFormat];
@@ -128,13 +147,17 @@ export class ScopeHoistingPackager {
128
147
  this.isAsyncBundle =
129
148
  this.bundleGraph.hasParentBundleOfType(this.bundle, 'js') &&
130
149
  !this.bundle.env.isIsolated() &&
131
- this.bundle.bundleBehavior !== 'isolated';
150
+ this.bundle.bundleBehavior !== 'isolated' &&
151
+ this.bundle.bundleBehavior !== 'inlineIsolated';
132
152
 
133
153
  this.globalNames = GLOBALS_BY_CONTEXT[bundle.env.context];
134
154
  }
135
155
 
136
- async package(): Promise<{|contents: string, map: ?SourceMap|}> {
137
- let wrappedAssets = await this.loadAssets();
156
+ async package(): Promise<{
157
+ contents: string;
158
+ map: SourceMap | null | undefined;
159
+ }> {
160
+ await this.loadAssets();
138
161
  this.buildExportedSymbols();
139
162
 
140
163
  // If building a library, the target is actually another bundler rather
@@ -155,10 +178,13 @@ export class ScopeHoistingPackager {
155
178
 
156
179
  let res = '';
157
180
  let lineCount = 0;
158
- let sourceMap = null;
159
- let processAsset = (asset) => {
181
+ let sourceMap: SourceMap | null | undefined = null;
182
+ let processAsset = (asset: Asset) => {
183
+ this.seenHoistedRequires.clear();
160
184
  let [content, map, lines] = this.visitAsset(asset);
185
+
161
186
  if (sourceMap && map) {
187
+ // @ts-expect-error TS2551 - addSourceMap method exists but missing from @parcel/source-map type definitions
162
188
  sourceMap.addSourceMap(map, lineCount);
163
189
  } else if (this.bundle.env.sourceMap) {
164
190
  sourceMap = map;
@@ -168,10 +194,22 @@ export class ScopeHoistingPackager {
168
194
  lineCount += lines + 1;
169
195
  };
170
196
 
197
+ if (
198
+ getFeatureFlag('inlineConstOptimisationFix') ||
199
+ this.useBothScopeHoistingImprovements
200
+ ) {
201
+ // Write out all constant modules used by this bundle
202
+ for (let asset of this.constantAssets) {
203
+ if (!this.seenAssets.has(asset)) {
204
+ processAsset(asset);
205
+ }
206
+ }
207
+ }
208
+
171
209
  // Hoist wrapped asset to the top of the bundle to ensure that they are registered
172
210
  // before they are used.
173
- for (let asset of wrappedAssets) {
174
- if (!this.seenAssets.has(asset.id)) {
211
+ for (let asset of this.wrappedAssets) {
212
+ if (!this.seenAssets.has(asset)) {
175
213
  processAsset(asset);
176
214
  }
177
215
  }
@@ -179,7 +217,7 @@ export class ScopeHoistingPackager {
179
217
  // Add each asset that is directly connected to the bundle. Dependencies will be handled
180
218
  // by replacing `import` statements in the code.
181
219
  this.bundle.traverseAssets((asset, _, actions) => {
182
- if (this.seenAssets.has(asset.id)) {
220
+ if (this.seenAssets.has(asset)) {
183
221
  actions.skipChildren();
184
222
  return;
185
223
  }
@@ -191,14 +229,28 @@ export class ScopeHoistingPackager {
191
229
  let [prelude, preludeLines] = this.buildBundlePrelude();
192
230
  res = prelude + res;
193
231
  lineCount += preludeLines;
232
+ // @ts-expect-error TS2339 - offsetLines method exists but missing from @parcel/source-map type definitions
194
233
  sourceMap?.offsetLines(1, preludeLines);
195
234
 
196
235
  let entries = this.bundle.getEntryAssets();
197
236
  let mainEntry = this.bundle.getMainEntry();
198
237
  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);
238
+ if (
239
+ this.useBothScopeHoistingImprovements ||
240
+ getFeatureFlag('supportWebpackChunkName')
241
+ ) {
242
+ // Generally speaking, async bundles should not be executed on load, as
243
+ // they're just collections of assets that other assets require.
244
+ // However, there are some special cases where a runtime asset needs to be
245
+ // injected, but no other asset will require it (mostly the bundle
246
+ // manifest).
247
+ // In this case, those assets need to be required on load.
248
+ entries = entries.filter(
249
+ (a) => a.meta?.runtimeAssetRequiringExecutionOnLoad,
250
+ );
251
+ } else {
252
+ entries = entries.filter((a) => a.id !== mainEntry?.id);
253
+ }
202
254
  mainEntry = null;
203
255
  }
204
256
 
@@ -206,7 +258,7 @@ export class ScopeHoistingPackager {
206
258
 
207
259
  // If any of the entry assets are wrapped, call parcelRequire so they are executed.
208
260
  for (let entry of entries) {
209
- if (this.wrappedAssets.has(entry.id) && !this.isScriptEntry(entry)) {
261
+ if (this.wrappedAssets.has(entry) && !this.isScriptEntry(entry)) {
210
262
  let parcelRequire = `parcelRequire(${JSON.stringify(
211
263
  this.bundleGraph.getAssetPublicId(entry),
212
264
  )});\n`;
@@ -250,9 +302,7 @@ export class ScopeHoistingPackager {
250
302
  lineCount++;
251
303
 
252
304
  let mainEntry = nullthrows(this.bundle.getMainEntry());
253
- let {code, map: mapBuffer} = nullthrows(
254
- this.assetOutputs.get(mainEntry.id),
255
- );
305
+ let {code, map: mapBuffer} = nullthrows(this.assetOutputs.get(mainEntry));
256
306
  let map;
257
307
  if (mapBuffer) {
258
308
  map = new SourceMap(this.options.projectRoot, mapBuffer);
@@ -265,6 +315,7 @@ export class ScopeHoistingPackager {
265
315
  this.parcelRequireName,
266
316
  );
267
317
  if (sourceMap && map) {
318
+ // @ts-expect-error TS2339 - addSourceMap method exists but missing from @parcel/source-map type definitions
268
319
  sourceMap.addSourceMap(map, lineCount);
269
320
  }
270
321
  }
@@ -281,10 +332,7 @@ export class ScopeHoistingPackager {
281
332
 
282
333
  let hasConditionalReference = false;
283
334
  let isConditionalBundle = false;
284
- if (
285
- getFeatureFlag('conditionalBundlingApi') &&
286
- getFeatureFlag('conditionalBundlingAsyncRuntime')
287
- ) {
335
+ if (getFeatureFlag('conditionalBundlingApi')) {
288
336
  // If the bundle has a conditional bundle reference (has an importCond)
289
337
  hasConditionalReference =
290
338
  this.bundleGraph.getReferencedConditionalBundles(bundle).length > 0;
@@ -296,6 +344,7 @@ export class ScopeHoistingPackager {
296
344
  this.useAsyncBundleRuntime &&
297
345
  bundle.type === 'js' &&
298
346
  bundle.bundleBehavior !== 'inline' &&
347
+ bundle.bundleBehavior !== 'inlineIsolated' &&
299
348
  bundle.env.outputFormat === 'esmodule' &&
300
349
  !bundle.env.isIsolated() &&
301
350
  bundle.bundleBehavior !== 'isolated' &&
@@ -309,11 +358,8 @@ export class ScopeHoistingPackager {
309
358
  .filter((b) => this.shouldBundleQueue(b))
310
359
  .map((b) => b.publicId);
311
360
 
312
- const conditions = [];
313
- if (
314
- getFeatureFlag('conditionalBundlingApi') &&
315
- getFeatureFlag('conditionalBundlingAsyncRuntime')
316
- ) {
361
+ const conditions: Array<string> = [];
362
+ if (getFeatureFlag('conditionalBundlingApi')) {
317
363
  const conditionSet = this.bundleGraph
318
364
  .getConditionalBundleMapping()
319
365
  .get(bundle.id);
@@ -354,16 +400,20 @@ export class ScopeHoistingPackager {
354
400
  return `$parcel$global.rwr(${params.join(', ')});`;
355
401
  }
356
402
 
357
- async loadAssets(): Promise<Array<Asset>> {
358
- let queue = new PromiseQueue({maxConcurrent: 32});
359
- let wrapped = [];
403
+ async loadAssets() {
404
+ type QueueItem = [Asset, {code: string; map: Buffer | undefined | null}];
405
+ let queue = new PromiseQueue<QueueItem>({
406
+ maxConcurrent: 32,
407
+ });
408
+
360
409
  this.bundle.traverseAssets((asset) => {
361
410
  queue.add(async () => {
362
411
  let [code, map] = await Promise.all([
363
412
  asset.getCode(),
364
413
  this.bundle.env.sourceMap ? asset.getMapBuffer() : null,
365
414
  ]);
366
- return [asset.id, {code, map}];
415
+
416
+ return [asset, {code, map}];
367
417
  });
368
418
 
369
419
  if (
@@ -381,52 +431,113 @@ export class ScopeHoistingPackager {
381
431
  .getIncomingDependencies(asset)
382
432
  .some((dep) => dep.priority === 'lazy')
383
433
  ) {
384
- this.wrappedAssets.add(asset.id);
385
- wrapped.push(asset);
434
+ this.wrappedAssets.add(asset);
435
+ } else if (
436
+ (getFeatureFlag('inlineConstOptimisationFix') ||
437
+ this.useBothScopeHoistingImprovements) &&
438
+ asset.meta.isConstantModule
439
+ ) {
440
+ this.constantAssets.add(asset);
386
441
  }
387
442
  }
388
443
  });
389
444
 
390
- for (let wrappedAssetRoot of [...wrapped]) {
391
- this.bundle.traverseAssets((asset, _, actions) => {
392
- if (asset === wrappedAssetRoot) {
393
- return;
445
+ if (this.useBothScopeHoistingImprovements) {
446
+ // Tracks which assets have been assigned to a wrap group
447
+ let assignedAssets = new Set<Asset>();
448
+
449
+ // In V2 scope hoisting, we iterate from the main entry, rather than
450
+ // wrapping the entry assets
451
+ if (!getFeatureFlag('applyScopeHoistingImprovementV2')) {
452
+ // Make all entry assets wrapped, to avoid any top level hoisting
453
+ for (let entryAsset of this.bundle.getEntryAssets()) {
454
+ if (!this.wrappedAssets.has(entryAsset)) {
455
+ this.wrappedAssets.add(entryAsset);
456
+ }
394
457
  }
458
+ }
395
459
 
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);
460
+ // We need to make a new copy here so that we can add to the list and
461
+ // iterate the newly added items, without mutating the wrappedAssets set
462
+ let moduleGroupParents = [...this.wrappedAssets.values()];
463
+
464
+ if (getFeatureFlag('applyScopeHoistingImprovementV2')) {
465
+ // The main entry needs to be check to find assets that would have gone in
466
+ // the top level scope
467
+ let mainEntry = this.bundle.getMainEntry();
468
+ if (mainEntry && !this.wrappedAssets.has(mainEntry)) {
469
+ moduleGroupParents.unshift(mainEntry);
424
470
  }
425
- }, wrappedAssetRoot);
471
+ }
472
+
473
+ for (let moduleGroupParentAsset of moduleGroupParents) {
474
+ this.bundle.traverseAssets((asset, _, actions) => {
475
+ if (asset === moduleGroupParentAsset) {
476
+ return;
477
+ }
478
+
479
+ if (this.wrappedAssets.has(asset)) {
480
+ actions.skipChildren();
481
+ return;
482
+ }
483
+
484
+ if (
485
+ !asset.meta.isConstantModule &&
486
+ (assignedAssets.has(asset) || this.isReExported(asset))
487
+ ) {
488
+ this.wrappedAssets.add(asset);
489
+
490
+ // This also needs to be added to the traversal so that we iterate
491
+ // it during this check.
492
+ moduleGroupParents.push(asset);
493
+
494
+ actions.skipChildren();
495
+ return;
496
+ }
497
+
498
+ assignedAssets.add(asset);
499
+ }, moduleGroupParentAsset);
500
+ }
501
+ } else {
502
+ for (let wrappedAssetRoot of this.wrappedAssets) {
503
+ this.bundle.traverseAssets((asset, _, actions) => {
504
+ if (asset === wrappedAssetRoot) {
505
+ return;
506
+ }
507
+
508
+ if (this.wrappedAssets.has(asset)) {
509
+ actions.skipChildren();
510
+ return;
511
+ }
512
+
513
+ if (!asset.meta.isConstantModule) {
514
+ this.wrappedAssets.add(asset);
515
+ }
516
+ }, wrappedAssetRoot);
517
+ }
426
518
  }
427
519
 
428
520
  this.assetOutputs = new Map(await queue.run());
429
- return wrapped;
521
+ }
522
+
523
+ isReExported(asset: Asset): boolean {
524
+ let parentSymbols = this.bundleGraph
525
+ .getIncomingDependencies(asset)
526
+ .map((dep) => this.bundleGraph.getAssetWithDependency(dep))
527
+ .flatMap((parent) => {
528
+ if (parent == null) {
529
+ return [];
530
+ }
531
+ return this.bundleGraph.getExportedSymbols(parent, this.bundle);
532
+ });
533
+
534
+ let assetSymbols = this.bundleGraph.getExportedSymbols(asset, this.bundle);
535
+
536
+ return assetSymbols.some((assetSymbol) =>
537
+ parentSymbols.some(
538
+ (parentSymbol) => parentSymbol.symbol === assetSymbol.symbol,
539
+ ),
540
+ );
430
541
  }
431
542
 
432
543
  buildExportedSymbols() {
@@ -439,7 +550,7 @@ export class ScopeHoistingPackager {
439
550
 
440
551
  // TODO: handle ESM exports of wrapped entry assets...
441
552
  let entry = this.bundle.getMainEntry();
442
- if (entry && !this.wrappedAssets.has(entry.id)) {
553
+ if (entry && !this.wrappedAssets.has(entry)) {
443
554
  let hasNamespace = entry.symbols.hasExportSymbol('*');
444
555
 
445
556
  for (let {
@@ -464,6 +575,7 @@ export class ScopeHoistingPackager {
464
575
  symbols = [];
465
576
  this.exportedSymbols.set(symbol, {
466
577
  asset,
578
+
467
579
  exportSymbol,
468
580
  local: symbol,
469
581
  exportAs: symbols,
@@ -519,20 +631,24 @@ export class ScopeHoistingPackager {
519
631
  return `${obj}[${JSON.stringify(property)}]`;
520
632
  }
521
633
 
522
- visitAsset(asset: Asset): [string, ?SourceMap, number] {
523
- invariant(!this.seenAssets.has(asset.id), 'Already visited asset');
524
- this.seenAssets.add(asset.id);
634
+ visitAsset(asset: Asset): [string, SourceMap | null | undefined, number] {
635
+ invariant(!this.seenAssets.has(asset), 'Already visited asset');
636
+ this.seenAssets.add(asset);
525
637
 
526
- let {code, map} = nullthrows(this.assetOutputs.get(asset.id));
638
+ let {code, map} = nullthrows(this.assetOutputs.get(asset));
527
639
  return this.buildAsset(asset, code, map);
528
640
  }
529
641
 
642
+ getAssetFilePath(asset: Asset): string {
643
+ return path.relative(this.options.projectRoot, asset.filePath);
644
+ }
645
+
530
646
  buildAsset(
531
647
  asset: Asset,
532
648
  code: string,
533
- map: ?Buffer,
534
- ): [string, ?SourceMap, number] {
535
- let shouldWrap = this.wrappedAssets.has(asset.id);
649
+ map?: Buffer | null,
650
+ ): [string, SourceMap | null | undefined, number] {
651
+ let shouldWrap = this.wrappedAssets.has(asset);
536
652
  let deps = this.bundleGraph.getDependencies(asset);
537
653
 
538
654
  let sourceMap =
@@ -559,16 +675,24 @@ export class ScopeHoistingPackager {
559
675
  continue;
560
676
  }
561
677
 
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);
678
+ if (this.bundle.hasAsset(resolved) && !this.seenAssets.has(resolved)) {
679
+ if (
680
+ this.useBothScopeHoistingImprovements &&
681
+ this.wrappedAssets.has(resolved)
682
+ ) {
683
+ // When the dep is wrapped then we just need to drop a side effect
684
+ // require instead of inlining
685
+ depCode += `parcelRequire("${this.bundleGraph.getAssetPublicId(resolved)}");\n`;
686
+ lineCount += 1;
687
+ } else {
688
+ let [code, map, lines] = this.visitAsset(resolved);
689
+ depCode += code + '\n';
690
+ if (sourceMap && map) {
691
+ // @ts-expect-error TS2551 - addSourceMap method exists but missing from @parcel/source-map type definitions
692
+ sourceMap.addSourceMap(map, lineCount);
693
+ }
694
+ lineCount += lines + 1;
570
695
  }
571
- lineCount += lines + 1;
572
696
  }
573
697
  }
574
698
 
@@ -602,7 +726,7 @@ export class ScopeHoistingPackager {
602
726
  code += append;
603
727
 
604
728
  let lineCount = 0;
605
- let depContent = [];
729
+ let depContent: Array<[string, SourceMap | null | undefined, number]> = [];
606
730
  if (depMap.size === 0 && replacements.size === 0) {
607
731
  // If there are no dependencies or replacements, use a simple function to count the number of lines.
608
732
  lineCount = countLines(code) - 1;
@@ -647,27 +771,68 @@ export class ScopeHoistingPackager {
647
771
  // after the dependency is declared. This handles the case where the resulting asset
648
772
  // is wrapped, but the dependency in this asset is not marked as wrapped. This means
649
773
  // 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
- );
774
+ let res = '';
775
+ let lines = 0;
655
776
  let map;
777
+
778
+ if (!getFeatureFlag('applyScopeHoistingImprovementV2')) {
779
+ [res, lines] = this.getHoistedParcelRequires(
780
+ asset,
781
+ dep,
782
+ resolved,
783
+ );
784
+ }
785
+
656
786
  if (
657
787
  this.bundle.hasAsset(resolved) &&
658
- !this.seenAssets.has(resolved.id)
788
+ !this.seenAssets.has(resolved)
659
789
  ) {
660
790
  // If this asset is wrapped, we need to hoist the code for the dependency
661
791
  // outside our parcelRequire.register wrapper. This is safe because all
662
792
  // assets referenced by this asset will also be wrapped. Otherwise, inline the
663
793
  // asset content where the import statement was.
664
- if (shouldWrap) {
665
- depContent.push(this.visitAsset(resolved));
794
+ if (this.useBothScopeHoistingImprovements) {
795
+ if (
796
+ !resolved.meta.isConstantModule &&
797
+ !this.wrappedAssets.has(resolved)
798
+ ) {
799
+ let [depCode, depMap, depLines] =
800
+ this.visitAsset(resolved);
801
+ if (debugTools['asset-file-names-in-output']) {
802
+ let resolvedPath = this.getAssetFilePath(resolved);
803
+ res = outdent`
804
+ /* Scope hoisted asset: ${resolvedPath} */
805
+ ${depCode}
806
+ /* End: ${resolvedPath} */
807
+ ${res}
808
+ `;
809
+ lines += 3 + depLines;
810
+ } else {
811
+ res = depCode + '\n' + res;
812
+ lines += 1 + depLines;
813
+ }
814
+ map = depMap;
815
+ }
666
816
  } else {
667
- let [depCode, depMap, depLines] = this.visitAsset(resolved);
668
- res = depCode + '\n' + res;
669
- lines += 1 + depLines;
670
- map = depMap;
817
+ if (shouldWrap) {
818
+ depContent.push(this.visitAsset(resolved));
819
+ } else {
820
+ let [depCode, depMap, depLines] =
821
+ this.visitAsset(resolved);
822
+ res = depCode + '\n' + res;
823
+ lines += 1 + depLines;
824
+ map = depMap;
825
+ }
826
+ }
827
+ }
828
+
829
+ if (getFeatureFlag('applyScopeHoistingImprovementV2')) {
830
+ let [requiresCode, requiresLines] =
831
+ this.getHoistedParcelRequires(asset, dep, resolved);
832
+
833
+ if (requiresCode) {
834
+ res = requiresCode + '\n' + res;
835
+ lines += requiresLines + 1;
671
836
  }
672
837
  }
673
838
 
@@ -679,6 +844,7 @@ export class ScopeHoistingPackager {
679
844
  }
680
845
 
681
846
  if (map) {
847
+ // @ts-expect-error TS2551 - addSourceMap method exists but missing from @parcel/source-map type definitions
682
848
  sourceMap.addSourceMap(map, lineCount);
683
849
  }
684
850
  }
@@ -726,10 +892,16 @@ ${code}
726
892
 
727
893
  lineCount += 2;
728
894
 
895
+ if (debugTools['asset-file-names-in-output']) {
896
+ code = `/* ${this.getAssetFilePath(asset)} */\n` + code;
897
+ lineCount += 1;
898
+ }
899
+
729
900
  for (let [depCode, map, lines] of depContent) {
730
901
  if (!depCode) continue;
731
902
  code += depCode + '\n';
732
903
  if (sourceMap && map) {
904
+ // @ts-expect-error TS2551 - addSourceMap method exists but missing from @parcel/source-map type definitions
733
905
  sourceMap.addSourceMap(map, lineCount);
734
906
  }
735
907
  lineCount += lines + 1;
@@ -848,7 +1020,7 @@ ${code}
848
1020
  // If this asset is wrapped, we need to replace the exports namespace with `module.exports`,
849
1021
  // which will be provided to us by the wrapper.
850
1022
  if (
851
- this.wrappedAssets.has(asset.id) ||
1023
+ this.wrappedAssets.has(asset) ||
852
1024
  (this.bundle.env.outputFormat === 'commonjs' &&
853
1025
  asset === this.bundle.getMainEntry())
854
1026
  ) {
@@ -895,7 +1067,9 @@ ${code}
895
1067
 
896
1068
  for (let [imported, {local}] of dep.symbols) {
897
1069
  // If already imported, just add the already renamed variable to the mapping.
1070
+
898
1071
  let renamed = external.get(imported);
1072
+
899
1073
  if (renamed && local !== '*' && replacements) {
900
1074
  replacements.set(local, renamed);
901
1075
  continue;
@@ -908,6 +1082,7 @@ ${code}
908
1082
  if (!renamed) {
909
1083
  if (referencedBundle) {
910
1084
  let entry = nullthrows(referencedBundle.getMainEntry());
1085
+
911
1086
  renamed =
912
1087
  entry.symbols.get('*')?.local ??
913
1088
  `$${String(entry.meta.id)}$exports`;
@@ -922,6 +1097,7 @@ ${code}
922
1097
 
923
1098
  if (local !== '*' && replacements) {
924
1099
  let replacement;
1100
+
925
1101
  if (imported === '*') {
926
1102
  replacement = renamed;
927
1103
  } else if (imported === 'default') {
@@ -946,10 +1122,12 @@ ${code}
946
1122
  let property;
947
1123
  if (referencedBundle) {
948
1124
  let entry = nullthrows(referencedBundle.getMainEntry());
1125
+
949
1126
  if (entry.symbols.hasExportSymbol('*')) {
950
1127
  // If importing * and the referenced module has a * export (e.g. CJS), use default instead.
951
1128
  // This mirrors the logic in buildExportedSymbols.
952
1129
  property = imported;
1130
+
953
1131
  imported =
954
1132
  referencedBundle?.env.outputFormat === 'esmodule'
955
1133
  ? 'default'
@@ -957,6 +1135,7 @@ ${code}
957
1135
  } else {
958
1136
  if (imported === '*') {
959
1137
  let exportedSymbols = this.bundleGraph.getExportedSymbols(entry);
1138
+
960
1139
  if (local === '*') {
961
1140
  // Re-export all symbols.
962
1141
  for (let exported of exportedSymbols) {
@@ -967,11 +1146,10 @@ ${code}
967
1146
  continue;
968
1147
  }
969
1148
  }
970
- renamed = this.bundleGraph.getSymbolResolution(
971
- entry,
972
- imported,
973
- this.bundle,
974
- ).symbol;
1149
+
1150
+ renamed =
1151
+ this.bundleGraph.getSymbolResolution(entry, imported, this.bundle)
1152
+ .symbol || undefined;
975
1153
  }
976
1154
  }
977
1155
 
@@ -993,8 +1171,10 @@ ${code}
993
1171
  }
994
1172
 
995
1173
  external.set(imported, renamed);
1174
+
996
1175
  if (local !== '*' && replacements) {
997
1176
  let replacement = renamed;
1177
+
998
1178
  if (property === '*') {
999
1179
  replacement = renamed;
1000
1180
  } else if (property === 'default') {
@@ -1003,6 +1183,7 @@ ${code}
1003
1183
  } else if (property) {
1004
1184
  replacement = this.getPropertyAccess(renamed, property);
1005
1185
  }
1186
+
1006
1187
  replacements.set(local, replacement);
1007
1188
  }
1008
1189
  }
@@ -1026,7 +1207,7 @@ ${code}
1026
1207
  }
1027
1208
  return (
1028
1209
  (!this.bundle.hasAsset(resolved) && !this.externalAssets.has(resolved)) ||
1029
- (this.wrappedAssets.has(resolved.id) && resolved !== parentAsset)
1210
+ (this.wrappedAssets.has(resolved) && resolved !== parentAsset)
1030
1211
  );
1031
1212
  }
1032
1213
 
@@ -1076,14 +1257,14 @@ ${code}
1076
1257
  (!this.bundle.hasAsset(resolvedAsset) ||
1077
1258
  !this.shouldSkipAsset(resolvedAsset))
1078
1259
  ) {
1079
- let hoisted = this.hoistedRequires.get(dep.id);
1260
+ let hoisted = this.hoistedRequires.get(dep);
1080
1261
  if (!hoisted) {
1081
1262
  hoisted = new Map();
1082
- this.hoistedRequires.set(dep.id, hoisted);
1263
+ this.hoistedRequires.set(dep, hoisted);
1083
1264
  }
1084
1265
 
1085
1266
  hoisted.set(
1086
- resolvedAsset.id,
1267
+ resolvedAsset,
1087
1268
  `var $${publicId} = parcelRequire(${JSON.stringify(publicId)});`,
1088
1269
  );
1089
1270
  }
@@ -1117,6 +1298,7 @@ ${code}
1117
1298
  obj = `$${publicId}`;
1118
1299
  } else {
1119
1300
  obj = resolvedAsset.symbols.get('*')?.local || `$${assetId}$exports`;
1301
+
1120
1302
  obj = replacements?.get(obj) || obj;
1121
1303
  }
1122
1304
 
@@ -1124,7 +1306,7 @@ ${code}
1124
1306
  // Resolve to the namespace object if requested or this is a CJS default interop reqiure.
1125
1307
  if (
1126
1308
  parentAsset === resolvedAsset &&
1127
- this.wrappedAssets.has(resolvedAsset.id)
1309
+ this.wrappedAssets.has(resolvedAsset)
1128
1310
  ) {
1129
1311
  // Directly use module.exports for wrapped assets importing themselves.
1130
1312
  return 'module.exports';
@@ -1167,7 +1349,7 @@ ${code}
1167
1349
  return ['', 0];
1168
1350
  }
1169
1351
 
1170
- let hoisted = this.hoistedRequires.get(dep.id);
1352
+ let hoisted = this.hoistedRequires.get(dep);
1171
1353
  let res = '';
1172
1354
  let lineCount = 0;
1173
1355
  let isWrapped = this.isWrapped(resolved, parentAsset);
@@ -1179,7 +1361,7 @@ ${code}
1179
1361
  if (
1180
1362
  isWrapped &&
1181
1363
  !dep.meta.shouldWrap &&
1182
- (!hoisted || hoisted.keys().next().value !== resolved.id) &&
1364
+ (!hoisted || hoisted.keys().next().value !== resolved) &&
1183
1365
  !this.bundleGraph.isDependencySkipped(dep) &&
1184
1366
  !this.shouldSkipAsset(resolved)
1185
1367
  ) {
@@ -1191,8 +1373,22 @@ ${code}
1191
1373
 
1192
1374
  if (hoisted) {
1193
1375
  this.needsPrelude = true;
1194
- res += '\n' + [...hoisted.values()].join('\n');
1195
- lineCount += hoisted.size;
1376
+
1377
+ if (getFeatureFlag('applyScopeHoistingImprovementV2')) {
1378
+ let hoistedValues = [...hoisted.values()].filter(
1379
+ (val) => !this.seenHoistedRequires.has(val),
1380
+ );
1381
+
1382
+ for (let val of hoistedValues) {
1383
+ this.seenHoistedRequires.add(val);
1384
+ }
1385
+
1386
+ res += '\n' + hoistedValues.join('\n');
1387
+ lineCount += hoisted.size;
1388
+ } else {
1389
+ res += '\n' + [...hoisted.values()].join('\n');
1390
+ lineCount += hoisted.size;
1391
+ }
1196
1392
  }
1197
1393
 
1198
1394
  return [res, lineCount];
@@ -1207,7 +1403,7 @@ ${code}
1207
1403
  let prependLineCount = 0;
1208
1404
  let append = '';
1209
1405
 
1210
- let shouldWrap = this.wrappedAssets.has(asset.id);
1406
+ let shouldWrap = this.wrappedAssets.has(asset);
1211
1407
  let usedSymbols = nullthrows(this.bundleGraph.getUsedSymbols(asset));
1212
1408
  let assetId = asset.meta.id;
1213
1409
  invariant(typeof assetId === 'string');
@@ -1220,34 +1416,51 @@ ${code}
1220
1416
  usedSymbols.has('default') &&
1221
1417
  !asset.symbols.hasExportSymbol('__esModule');
1222
1418
 
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());
1419
+ let usedNamespace;
1420
+ if (
1421
+ getFeatureFlag('inlineConstOptimisationFix') &&
1422
+ asset.meta.isConstantModule
1423
+ ) {
1424
+ // Only set usedNamespace if there is an incoming dependency in the current bundle that uses '*'
1425
+ usedNamespace = this.bundleGraph
1426
+ .getIncomingDependencies(asset)
1427
+ .some(
1428
+ (dep) =>
1429
+ this.bundle.hasDependency(dep) &&
1430
+ nullthrows(this.bundleGraph.getUsedSymbols(dep)).has('*'),
1431
+ );
1432
+ } else {
1433
+ usedNamespace =
1434
+ // If the asset has * in its used symbols, we might need the exports namespace.
1435
+ // The one case where this isn't true is in ESM library entries, where the only
1436
+ // dependency on * is the entry dependency. In this case, we will use ESM exports
1437
+ // instead of the namespace object.
1438
+
1439
+ (usedSymbols.has('*') &&
1440
+ (this.bundle.env.outputFormat !== 'esmodule' ||
1441
+ !this.bundle.env.isLibrary ||
1442
+ asset !== this.bundle.getMainEntry() ||
1443
+ this.bundleGraph
1444
+ .getIncomingDependencies(asset)
1445
+ .some(
1446
+ (dep) =>
1447
+ !dep.isEntry &&
1448
+ this.bundle.hasDependency(dep) &&
1449
+ nullthrows(this.bundleGraph.getUsedSymbols(dep)).has('*'),
1450
+ ))) ||
1451
+ // If a symbol is imported (used) from a CJS asset but isn't listed in the symbols,
1452
+ // we fallback on the namespace object.
1453
+
1454
+ (asset.symbols.hasExportSymbol('*') &&
1455
+ [...usedSymbols].some((s) => !asset.symbols.hasExportSymbol(s))) ||
1456
+ // If the exports has this asset's namespace (e.g. ESM output from CJS input),
1457
+ // include the namespace object for the default export.
1458
+ this.exportedSymbols.has(`$${assetId}$exports`) ||
1459
+ // CommonJS library bundle entries always need a namespace.
1460
+ (this.bundle.env.isLibrary &&
1461
+ this.bundle.env.outputFormat === 'commonjs' &&
1462
+ asset === this.bundle.getMainEntry());
1463
+ }
1251
1464
 
1252
1465
  // If the asset doesn't have static exports, should wrap, the namespace is used,
1253
1466
  // or we need default interop, then we need to synthesize a namespace object for
@@ -1274,6 +1487,7 @@ ${code}
1274
1487
  // Insert the __esModule interop flag for this module if it has a `default` export
1275
1488
  // and the namespace symbol is used.
1276
1489
  // TODO: only if required by CJS?
1490
+
1277
1491
  if (asset.symbols.hasExportSymbol('default') && usedSymbols.has('*')) {
1278
1492
  prepend += `\n$parcel$defineInteropFlag($${assetId}$exports);\n`;
1279
1493
  prependLineCount += 2;
@@ -1337,6 +1551,7 @@ ${code}
1337
1551
  let resolvedSymbol = this.getSymbolResolution(
1338
1552
  asset,
1339
1553
  resolved,
1554
+
1340
1555
  symbol,
1341
1556
  undefined,
1342
1557
  replacements,
@@ -1388,28 +1603,39 @@ ${code}
1388
1603
  // for the symbol so that when the value changes the object property also changes. This is
1389
1604
  // required to simulate ESM live bindings. It's easier to do it this way rather than inserting
1390
1605
  // 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,
1606
+ for (let exp of usedExports) {
1607
+ let resolved = this.getSymbolResolution(
1608
+ asset,
1609
+ asset,
1610
+ exp,
1611
+ undefined,
1612
+ replacements,
1613
+ );
1614
+ const meta = asset.symbols.get(exp)?.meta;
1615
+ if (
1616
+ getFeatureFlag('exportsRebindingOptimisation') &&
1617
+ (meta?.isStaticBindingSafe ||
1618
+ this.manualStaticBindingExports?.some((regex) =>
1619
+ regex.test(asset.filePath),
1620
+ ))
1621
+ ) {
1622
+ append += `$${assetId}$exports[${JSON.stringify(
1396
1623
  exp,
1397
- undefined,
1398
- replacements,
1399
- );
1624
+ )}] = ${resolved};\n`;
1625
+ } else {
1400
1626
  let get = this.buildFunctionExpression([], resolved);
1401
1627
  let isEsmExport = !!asset.symbols.get(exp)?.meta?.isEsm;
1402
1628
  let set =
1403
1629
  !isEsmExport && asset.meta.hasCJSExports
1404
1630
  ? ', ' + this.buildFunctionExpression(['v'], `${resolved} = v`)
1405
1631
  : '';
1406
- return `$parcel$export($${assetId}$exports, ${JSON.stringify(
1632
+ prepend += `$parcel$export($${assetId}$exports, ${JSON.stringify(
1407
1633
  exp,
1408
- )}, ${get}${set});`;
1409
- })
1410
- .join('\n')}\n`;
1411
- this.usedHelpers.add('$parcel$export');
1412
- prependLineCount += 1 + usedExports.length;
1634
+ )}, ${get}${set});\n`;
1635
+ this.usedHelpers.add('$parcel$export');
1636
+ prependLineCount += 1 + usedExports.length;
1637
+ }
1638
+ }
1413
1639
  }
1414
1640
  }
1415
1641
 
@@ -1448,9 +1674,11 @@ ${code}
1448
1674
  }
1449
1675
 
1450
1676
  for (let helper of this.usedHelpers) {
1451
- let currentHelper = helpers[helper];
1677
+ let currentHelper = (helpers as Record<string, any>)[helper];
1452
1678
  if (typeof currentHelper === 'function') {
1453
- currentHelper = helpers[helper](this.bundle.env);
1679
+ currentHelper = (helpers as Record<string, any>)[helper](
1680
+ this.bundle.env,
1681
+ );
1454
1682
  }
1455
1683
  res += currentHelper;
1456
1684
  if (enableSourceMaps) {
@@ -1470,11 +1698,14 @@ ${code}
1470
1698
  .some((g) => this.bundleGraph.isEntryBundleGroup(g)) ||
1471
1699
  this.bundle.env.isIsolated() ||
1472
1700
  this.bundle.bundleBehavior === 'isolated' ||
1701
+ this.bundle.bundleBehavior === 'inlineIsolated' ||
1473
1702
  // Conditional deps may be loaded before entrypoints on the server
1474
1703
  this.hasConditionalDependency();
1475
1704
 
1476
1705
  if (mightBeFirstJS) {
1477
- let preludeCode = prelude(this.parcelRequireName);
1706
+ let preludeCode = (
1707
+ getFeatureFlag('useNewPrelude') ? preludeNew : preludeOld
1708
+ )(this.parcelRequireName);
1478
1709
  res += preludeCode;
1479
1710
  if (enableSourceMaps) {
1480
1711
  lines += countLines(preludeCode) - 1;
@@ -1498,7 +1729,11 @@ ${code}
1498
1729
  }
1499
1730
 
1500
1731
  // Add importScripts for sibling bundles in workers.
1501
- if (this.bundle.env.isWorker() || this.bundle.env.isWorklet()) {
1732
+ if (
1733
+ this.bundle.env.isWorker() ||
1734
+ this.bundle.env.isTesseract() ||
1735
+ this.bundle.env.isWorklet()
1736
+ ) {
1502
1737
  let importScripts = '';
1503
1738
  let bundles = this.bundleGraph.getReferencedBundles(this.bundle);
1504
1739
  for (let b of bundles) {