From 1d7becc612e3244ad440322b437aa67d9e1d908b Mon Sep 17 00:00:00 2001 From: mattcompiles Date: Mon, 4 Sep 2023 15:14:10 +1000 Subject: [PATCH 01/11] Initial async bundle queue implementation --- packages/packagers/js/src/ESMOutputFormat.js | 8 +++ .../packagers/js/src/ScopeHoistingPackager.js | 61 ++++++++++++++++++- packages/packagers/js/src/helpers.js | 60 ++++++++++++++++++ 3 files changed, 128 insertions(+), 1 deletion(-) diff --git a/packages/packagers/js/src/ESMOutputFormat.js b/packages/packagers/js/src/ESMOutputFormat.js index aa53bdd02cc..683f3606b37 100644 --- a/packages/packagers/js/src/ESMOutputFormat.js +++ b/packages/packagers/js/src/ESMOutputFormat.js @@ -106,6 +106,14 @@ export class ESMOutputFormat implements OutputFormat { lines++; } + if (this.packager.shouldBundleQueue(this.packager.bundle)) { + // Should be last thing the bundle executes on intial eval + res += `\n$parcel$global.rlg(${JSON.stringify( + this.packager.bundle.publicId, + )})`; + lines++; + } + return [res, lines]; } } diff --git a/packages/packagers/js/src/ScopeHoistingPackager.js b/packages/packagers/js/src/ScopeHoistingPackager.js index 477bc179a7f..c3ad01a5f15 100644 --- a/packages/packagers/js/src/ScopeHoistingPackager.js +++ b/packages/packagers/js/src/ScopeHoistingPackager.js @@ -27,7 +27,7 @@ import path from 'path'; import {ESMOutputFormat} from './ESMOutputFormat'; import {CJSOutputFormat} from './CJSOutputFormat'; import {GlobalOutputFormat} from './GlobalOutputFormat'; -import {prelude, helpers} from './helpers'; +import {prelude, helpers, bundleQueuePrelude, fnExpr} from './helpers'; import {replaceScriptDependencies, getSpecifier} from './utils'; // https://262.ecma-international.org/6.0/#sec-names-and-keywords @@ -202,6 +202,8 @@ export class ScopeHoistingPackager { mainEntry = null; } + let needsBundleQueue = this.shouldBundleQueue(this.bundle); + // If any of the entry assets are wrapped, call parcelRequire so they are executed. for (let entry of entries) { if (this.wrappedAssets.has(entry.id) && !this.isScriptEntry(entry)) { @@ -210,13 +212,22 @@ export class ScopeHoistingPackager { )});\n`; let entryExports = entry.symbols.get('*')?.local; + if ( entryExports && entry === mainEntry && this.exportedSymbols.has(entryExports) ) { + invariant( + !needsBundleQueue, + 'Entry exports are not yet compaitble with async bundles', + ); res += `\nvar ${entryExports} = ${parcelRequire}`; } else { + if (needsBundleQueue) { + parcelRequire = this.runWhenReady(this.bundle, parcelRequire); + } + res += `\n${parcelRequire}`; } @@ -264,6 +275,38 @@ export class ScopeHoistingPackager { }; } + shouldBundleQueue(bundle: NamedBundle): boolean { + return ( + Boolean(process.env.PARCEL_EXPERIMENTAL_ASYNC_RUNTIME) && + bundle.type === 'js' && + bundle.bundleBehavior !== 'inline' && + bundle.env.outputFormat === 'esmodule' && + !bundle.env.isIsolated() && + bundle.bundleBehavior !== 'isolated' && + !this.bundleGraph.hasParentBundleOfType(bundle, 'js') + ); + } + + runWhenReady(bundle: NamedBundle, codeToRun: string): string { + let deps = this.bundleGraph + .getReferencedBundles(bundle) + .filter(b => this.shouldBundleQueue(b)) + .map(b => b.publicId); + + if (deps.length === 0) { + // If no deps we can safely execute immediately + return codeToRun; + } + + let params = [ + JSON.stringify(this.bundle.publicId), + fnExpr(this.bundle.env, [], [codeToRun]), + JSON.stringify(deps), + ]; + + return `$parcel$global.rwr(${params.join(', ')});`; + } + async loadAssets(): Promise> { let queue = new PromiseQueue({maxConcurrent: 32}); let wrapped = []; @@ -596,6 +639,14 @@ ${code} lineCount += lines + 1; } + if ( + !shouldWrap && + this.shouldBundleQueue(this.bundle) && + this.bundle.getEntryAssets().some(entry => entry.id === asset.id) + ) { + code = this.runWhenReady(this.bundle, code); + } + this.needsPrelude = true; } @@ -1199,6 +1250,14 @@ ${code} if (enableSourceMaps) { lines += countLines(preludeCode) - 1; } + + if (this.shouldBundleQueue(this.bundle)) { + let bundleQueuePreludeCode = bundleQueuePrelude(this.bundle.env); + res += bundleQueuePreludeCode; + if (enableSourceMaps) { + lines += countLines(bundleQueuePreludeCode) - 1; + } + } } else { // Otherwise, get the current parcelRequire global. res += `var parcelRequire = $parcel$global[${JSON.stringify( diff --git a/packages/packagers/js/src/helpers.js b/packages/packagers/js/src/helpers.js index 581269db6eb..120bf05ffaf 100644 --- a/packages/packagers/js/src/helpers.js +++ b/packages/packagers/js/src/helpers.js @@ -32,6 +32,66 @@ if (parcelRequire == null) { } `; +export const fnExpr = ( + env: Environment, + params: Array, + body: Array, +): string => { + let block = `{ ${body.join(' ')} }`; + + if (env.supports('arrow-functions')) { + return `(${params.join(', ')}) => ${block}`; + } + + return `function (${params.join(', ')}) ${block}`; +}; + +export const bundleQueuePrelude = (env: Environment): string => ` +if (!$parcel$global.lb) { + $parcel$global.lb = new Set(); + $parcel$global.bq = []; + + // Register loaded bundle + $parcel$global.rlb = ${fnExpr( + env, + ['bundle'], + ['$parcel$global.lb.add(bundle)', '$parcel$global.pq();'], + )} + + // Run when ready + $parcel$global.rwr = ${fnExpr( + env, + ['b', 'r', 'd'], + [ + '$parcel$global.bq.push({b: bundle, r: run, d: deps});', + '$parcel$global.pq();', + ], + )} + + // Process queue + $parcel$global.pq = ${fnExpr( + env, + [], + [ + `var runnableEntry = $parcel$global.bq.find(${fnExpr( + env, + ['i'], + ['return i.d.every(dep => $parcel$global.lb.has(dep));'], + )}`, + 'if (runnableEntry) {', + `$parcel$global.bq = $parcel$global.bq.filter(${fnExpr( + env, + ['i'], + ['return i.b !== runnableEntry.b;'], + )});`, + 'runnableEntry.r();', + '$parcel$global.pq();', + '}', + ], + )} +} +`; + const $parcel$export = ` function $parcel$export(e, n, v, s) { Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true}); From 282bd817a48049aa0f7ccf3917b12d0969621a1d Mon Sep 17 00:00:00 2001 From: mattcompiles Date: Mon, 4 Sep 2023 15:48:24 +1000 Subject: [PATCH 02/11] Fix runtime errors --- packages/packagers/js/src/helpers.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/packagers/js/src/helpers.js b/packages/packagers/js/src/helpers.js index 120bf05ffaf..29a8e2dac3e 100644 --- a/packages/packagers/js/src/helpers.js +++ b/packages/packagers/js/src/helpers.js @@ -55,17 +55,14 @@ if (!$parcel$global.lb) { $parcel$global.rlb = ${fnExpr( env, ['bundle'], - ['$parcel$global.lb.add(bundle)', '$parcel$global.pq();'], + ['$parcel$global.lb.add(bundle);', '$parcel$global.pq();'], )} // Run when ready $parcel$global.rwr = ${fnExpr( env, ['b', 'r', 'd'], - [ - '$parcel$global.bq.push({b: bundle, r: run, d: deps});', - '$parcel$global.pq();', - ], + ['$parcel$global.bq.push({b, r, d});', '$parcel$global.pq();'], )} // Process queue @@ -76,7 +73,13 @@ if (!$parcel$global.lb) { `var runnableEntry = $parcel$global.bq.find(${fnExpr( env, ['i'], - ['return i.d.every(dep => $parcel$global.lb.has(dep));'], + [ + `return i.d.every(${fnExpr( + env, + ['dep'], + ['return $parcel$global.lb.has(dep));'], + )};`, + ], )}`, 'if (runnableEntry) {', `$parcel$global.bq = $parcel$global.bq.filter(${fnExpr( From d8da5c9901847edd39e7d25c871b1d2185e8ec0d Mon Sep 17 00:00:00 2001 From: mattcompiles Date: Mon, 4 Sep 2023 16:08:10 +1000 Subject: [PATCH 03/11] Fix runtime errors 2 --- packages/packagers/js/src/helpers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/packagers/js/src/helpers.js b/packages/packagers/js/src/helpers.js index 29a8e2dac3e..14bdababfeb 100644 --- a/packages/packagers/js/src/helpers.js +++ b/packages/packagers/js/src/helpers.js @@ -77,10 +77,10 @@ if (!$parcel$global.lb) { `return i.d.every(${fnExpr( env, ['dep'], - ['return $parcel$global.lb.has(dep));'], + ['return $parcel$global.lb.has(dep);'], )};`, ], - )}`, + )};`, 'if (runnableEntry) {', `$parcel$global.bq = $parcel$global.bq.filter(${fnExpr( env, From 49c1b47679b6bc61798cf6e3861a6a54c9fd6b1a Mon Sep 17 00:00:00 2001 From: mattcompiles Date: Mon, 4 Sep 2023 16:21:20 +1000 Subject: [PATCH 04/11] Fix runtime errors 3 --- packages/packagers/js/src/helpers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/packagers/js/src/helpers.js b/packages/packagers/js/src/helpers.js index 14bdababfeb..95a0331111d 100644 --- a/packages/packagers/js/src/helpers.js +++ b/packages/packagers/js/src/helpers.js @@ -78,9 +78,9 @@ if (!$parcel$global.lb) { env, ['dep'], ['return $parcel$global.lb.has(dep);'], - )};`, + )});`, ], - )};`, + )});`, 'if (runnableEntry) {', `$parcel$global.bq = $parcel$global.bq.filter(${fnExpr( env, From ee205bcbacf660c35f50d98840decc54e72734c0 Mon Sep 17 00:00:00 2001 From: mattcompiles Date: Tue, 5 Sep 2023 08:37:31 +1000 Subject: [PATCH 05/11] Fix hoisted assets and rlb call --- packages/packagers/js/src/ESMOutputFormat.js | 2 +- .../packagers/js/src/ScopeHoistingPackager.js | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/packagers/js/src/ESMOutputFormat.js b/packages/packagers/js/src/ESMOutputFormat.js index 683f3606b37..ef79036c9cd 100644 --- a/packages/packagers/js/src/ESMOutputFormat.js +++ b/packages/packagers/js/src/ESMOutputFormat.js @@ -108,7 +108,7 @@ export class ESMOutputFormat implements OutputFormat { if (this.packager.shouldBundleQueue(this.packager.bundle)) { // Should be last thing the bundle executes on intial eval - res += `\n$parcel$global.rlg(${JSON.stringify( + res += `\n$parcel$global.rlb(${JSON.stringify( this.packager.bundle.publicId, )})`; lines++; diff --git a/packages/packagers/js/src/ScopeHoistingPackager.js b/packages/packagers/js/src/ScopeHoistingPackager.js index c3ad01a5f15..f6774f0ada2 100644 --- a/packages/packagers/js/src/ScopeHoistingPackager.js +++ b/packages/packagers/js/src/ScopeHoistingPackager.js @@ -639,17 +639,17 @@ ${code} lineCount += lines + 1; } - if ( - !shouldWrap && - this.shouldBundleQueue(this.bundle) && - this.bundle.getEntryAssets().some(entry => entry.id === asset.id) - ) { - code = this.runWhenReady(this.bundle, code); - } - this.needsPrelude = true; } + if ( + !shouldWrap && + this.shouldBundleQueue(this.bundle) && + this.bundle.getEntryAssets().some(entry => entry.id === asset.id) + ) { + code = this.runWhenReady(this.bundle, code); + } + return [code, sourceMap, lineCount]; } From dfa9529d04fdfa70c158dd394ac984de6d4f2a74 Mon Sep 17 00:00:00 2001 From: mattcompiles Date: Tue, 5 Sep 2023 14:04:41 +1000 Subject: [PATCH 06/11] Make runtime configurable --- .../integration/bundle-queue-runtime/a.html | 1 + .../integration/bundle-queue-runtime/a.js | 1 + .../integration/bundle-queue-runtime/b.js | 1 + .../integration/bundle-queue-runtime/c.js | 1 + .../bundle-queue-runtime/index.html | 1 + .../integration/bundle-queue-runtime/index.js | 5 +++ .../bundle-queue-runtime/package.json | 8 ++++ .../bundle-queue-runtime/yarn.lock | 0 .../integration-tests/test/scope-hoisting.js | 33 ++++++++++++++ .../packagers/js/src/ScopeHoistingPackager.js | 5 ++- packages/packagers/js/src/index.js | 43 ++++++++++++++++++- 11 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 packages/core/integration-tests/test/integration/bundle-queue-runtime/a.html create mode 100644 packages/core/integration-tests/test/integration/bundle-queue-runtime/a.js create mode 100644 packages/core/integration-tests/test/integration/bundle-queue-runtime/b.js create mode 100644 packages/core/integration-tests/test/integration/bundle-queue-runtime/c.js create mode 100644 packages/core/integration-tests/test/integration/bundle-queue-runtime/index.html create mode 100644 packages/core/integration-tests/test/integration/bundle-queue-runtime/index.js create mode 100644 packages/core/integration-tests/test/integration/bundle-queue-runtime/package.json create mode 100644 packages/core/integration-tests/test/integration/bundle-queue-runtime/yarn.lock diff --git a/packages/core/integration-tests/test/integration/bundle-queue-runtime/a.html b/packages/core/integration-tests/test/integration/bundle-queue-runtime/a.html new file mode 100644 index 00000000000..ac8c6e251ad --- /dev/null +++ b/packages/core/integration-tests/test/integration/bundle-queue-runtime/a.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/core/integration-tests/test/integration/bundle-queue-runtime/a.js b/packages/core/integration-tests/test/integration/bundle-queue-runtime/a.js new file mode 100644 index 00000000000..0ed5c30080a --- /dev/null +++ b/packages/core/integration-tests/test/integration/bundle-queue-runtime/a.js @@ -0,0 +1 @@ +export default 'a'; diff --git a/packages/core/integration-tests/test/integration/bundle-queue-runtime/b.js b/packages/core/integration-tests/test/integration/bundle-queue-runtime/b.js new file mode 100644 index 00000000000..a68ac2819dc --- /dev/null +++ b/packages/core/integration-tests/test/integration/bundle-queue-runtime/b.js @@ -0,0 +1 @@ +export default 'b'; diff --git a/packages/core/integration-tests/test/integration/bundle-queue-runtime/c.js b/packages/core/integration-tests/test/integration/bundle-queue-runtime/c.js new file mode 100644 index 00000000000..37a4d86fac7 --- /dev/null +++ b/packages/core/integration-tests/test/integration/bundle-queue-runtime/c.js @@ -0,0 +1 @@ +export default 'c'; diff --git a/packages/core/integration-tests/test/integration/bundle-queue-runtime/index.html b/packages/core/integration-tests/test/integration/bundle-queue-runtime/index.html new file mode 100644 index 00000000000..95526304133 --- /dev/null +++ b/packages/core/integration-tests/test/integration/bundle-queue-runtime/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/core/integration-tests/test/integration/bundle-queue-runtime/index.js b/packages/core/integration-tests/test/integration/bundle-queue-runtime/index.js new file mode 100644 index 00000000000..36dc3214941 --- /dev/null +++ b/packages/core/integration-tests/test/integration/bundle-queue-runtime/index.js @@ -0,0 +1,5 @@ +import a from './a'; +import b from './b'; +import c from './c'; + +result([a, b, c]); diff --git a/packages/core/integration-tests/test/integration/bundle-queue-runtime/package.json b/packages/core/integration-tests/test/integration/bundle-queue-runtime/package.json new file mode 100644 index 00000000000..787e5fddbda --- /dev/null +++ b/packages/core/integration-tests/test/integration/bundle-queue-runtime/package.json @@ -0,0 +1,8 @@ +{ + "@parcel/bundler-default": { + "minBundleSize": 0 + }, + "@parcel/packager-js": { + "unstable_asyncBundleRuntime": true + } +} \ No newline at end of file diff --git a/packages/core/integration-tests/test/integration/bundle-queue-runtime/yarn.lock b/packages/core/integration-tests/test/integration/bundle-queue-runtime/yarn.lock new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/core/integration-tests/test/scope-hoisting.js b/packages/core/integration-tests/test/scope-hoisting.js index 91ac3df6921..cbe0e1b1d59 100644 --- a/packages/core/integration-tests/test/scope-hoisting.js +++ b/packages/core/integration-tests/test/scope-hoisting.js @@ -16,6 +16,7 @@ import { outputFS, overlayFS, run, + inputFS, runBundle, } from '@parcel/test-utils'; @@ -5861,4 +5862,36 @@ describe('scope hoisting', function () { assert.equal(res, 'target'); }); }); + + it.only('should add experimental bundle queue runtime for out of order bundle execution', async function () { + let b = await bundle( + [ + path.join(__dirname, 'integration/bundle-queue-runtime/index.html'), + path.join(__dirname, 'integration/bundle-queue-runtime/a.html'), + ], + { + mode: 'production', + defaultTargetOptions: { + shouldScopeHoist: true, + shouldOptimize: false, + outputFormat: 'esmodule', + }, + }, + ); + + let contents = await outputFS.readFile( + b.getBundles().find(b => /index.*\.js/.test(b.filePath)).filePath, + 'utf8', + ); + assert(contents.includes('$parcel$global.rwr(')); + + let result; + await run(b, { + result: r => { + result = r; + }, + }); + + assert.deepEqual(await result, ['a', 'b', 'c']); + }); }); diff --git a/packages/packagers/js/src/ScopeHoistingPackager.js b/packages/packagers/js/src/ScopeHoistingPackager.js index f6774f0ada2..18da60fa952 100644 --- a/packages/packagers/js/src/ScopeHoistingPackager.js +++ b/packages/packagers/js/src/ScopeHoistingPackager.js @@ -74,6 +74,7 @@ export class ScopeHoistingPackager { bundleGraph: BundleGraph; bundle: NamedBundle; parcelRequireName: string; + useAsyncBundleRuntime: boolean; outputFormat: OutputFormat; isAsyncBundle: boolean; globalNames: $ReadOnlySet; @@ -101,11 +102,13 @@ export class ScopeHoistingPackager { bundleGraph: BundleGraph, bundle: NamedBundle, parcelRequireName: string, + useAsyncBundleRuntime: boolean, ) { this.options = options; this.bundleGraph = bundleGraph; this.bundle = bundle; this.parcelRequireName = parcelRequireName; + this.useAsyncBundleRuntime = useAsyncBundleRuntime; let OutputFormat = OUTPUT_FORMATS[this.bundle.env.outputFormat]; this.outputFormat = new OutputFormat(this); @@ -277,7 +280,7 @@ export class ScopeHoistingPackager { shouldBundleQueue(bundle: NamedBundle): boolean { return ( - Boolean(process.env.PARCEL_EXPERIMENTAL_ASYNC_RUNTIME) && + this.useAsyncBundleRuntime && bundle.type === 'js' && bundle.bundleBehavior !== 'inline' && bundle.env.outputFormat === 'esmodule' && diff --git a/packages/packagers/js/src/index.js b/packages/packagers/js/src/index.js index 73f1ebc0aa6..1f1b043de13 100644 --- a/packages/packagers/js/src/index.js +++ b/packages/packagers/js/src/index.js @@ -2,24 +2,62 @@ import type {Async} from '@parcel/types'; import type SourceMap from '@parcel/source-map'; import {Packager} from '@parcel/plugin'; -import {replaceInlineReferences, replaceURLReferences} from '@parcel/utils'; +import { + replaceInlineReferences, + replaceURLReferences, + validateSchema, + type SchemaEntity, +} from '@parcel/utils'; +import {encodeJSONKeyComponent} from '@parcel/diagnostic'; import {hashString} from '@parcel/hash'; import path from 'path'; import nullthrows from 'nullthrows'; import {DevPackager} from './DevPackager'; import {ScopeHoistingPackager} from './ScopeHoistingPackager'; +type JSPackagerConfig = {| + parcelRequireName: string, + unstable_asyncBundleRuntime: boolean, +|}; + +const CONFIG_SCHEMA: SchemaEntity = { + type: 'object', + properties: { + unstable_asyncBundleRuntime: { + type: 'boolean', + }, + }, + additionalProperties: false, +}; + export default (new Packager({ - async loadConfig({config, options}) { + async loadConfig({config, options}): Promise { // Generate a name for the global parcelRequire function that is unique to this project. // This allows multiple parcel builds to coexist on the same page. let pkg = await config.getConfigFrom( path.join(options.projectRoot, 'index'), ['package.json'], ); + + let packageKey = '@parcel/packager-js'; + validateSchema.diagnostic( + CONFIG_SCHEMA, + { + data: pkg?.contents[packageKey], + source: await options.inputFS.readFile(pkg.filePath, 'utf8'), + filePath: pkg.filePath, + prependKey: `/${encodeJSONKeyComponent(packageKey)}`, + }, + packageKey, + `Invalid config for ${packageKey}`, + ); + let name = pkg?.contents?.name ?? ''; return { parcelRequireName: 'parcelRequire' + hashString(name).slice(-4), + unstable_asyncBundleRuntime: Boolean( + pkg?.contents[packageKey]?.unstable_asyncBundleRuntime, + ), }; }, async package({ @@ -51,6 +89,7 @@ export default (new Packager({ bundleGraph, bundle, nullthrows(config).parcelRequireName, + nullthrows(config).unstable_asyncBundleRuntime, ) : new DevPackager( options, From a917560d8d15907523956b1af2a318f566a298e5 Mon Sep 17 00:00:00 2001 From: mattcompiles Date: Tue, 5 Sep 2023 14:05:51 +1000 Subject: [PATCH 07/11] Clean up test --- packages/core/integration-tests/test/scope-hoisting.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/integration-tests/test/scope-hoisting.js b/packages/core/integration-tests/test/scope-hoisting.js index cbe0e1b1d59..af5be86b833 100644 --- a/packages/core/integration-tests/test/scope-hoisting.js +++ b/packages/core/integration-tests/test/scope-hoisting.js @@ -16,7 +16,6 @@ import { outputFS, overlayFS, run, - inputFS, runBundle, } from '@parcel/test-utils'; @@ -5863,7 +5862,7 @@ describe('scope hoisting', function () { }); }); - it.only('should add experimental bundle queue runtime for out of order bundle execution', async function () { + it('should add experimental bundle queue runtime for out of order bundle execution', async function () { let b = await bundle( [ path.join(__dirname, 'integration/bundle-queue-runtime/index.html'), From ecc229dc1b827798bc104d5fa06fe910522a78da Mon Sep 17 00:00:00 2001 From: mattcompiles Date: Tue, 5 Sep 2023 15:46:31 +1000 Subject: [PATCH 08/11] Fix weird type error --- packages/packagers/js/src/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/packagers/js/src/index.js b/packages/packagers/js/src/index.js index 1f1b043de13..8fc13515e6b 100644 --- a/packages/packagers/js/src/index.js +++ b/packages/packagers/js/src/index.js @@ -34,9 +34,10 @@ export default (new Packager({ async loadConfig({config, options}): Promise { // Generate a name for the global parcelRequire function that is unique to this project. // This allows multiple parcel builds to coexist on the same page. - let pkg = await config.getConfigFrom( - path.join(options.projectRoot, 'index'), - ['package.json'], + let pkg = nullthrows( + await config.getConfigFrom(path.join(options.projectRoot, 'index'), [ + 'package.json', + ]), ); let packageKey = '@parcel/packager-js'; From 7a0eeb504d3b36de70b58a1a0f601c53cbcc0e2c Mon Sep 17 00:00:00 2001 From: mattcompiles Date: Tue, 5 Sep 2023 16:10:20 +1000 Subject: [PATCH 09/11] Fix config validation --- packages/packagers/js/src/index.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/packagers/js/src/index.js b/packages/packagers/js/src/index.js index 8fc13515e6b..d65fb965ea5 100644 --- a/packages/packagers/js/src/index.js +++ b/packages/packagers/js/src/index.js @@ -41,17 +41,20 @@ export default (new Packager({ ); let packageKey = '@parcel/packager-js'; - validateSchema.diagnostic( - CONFIG_SCHEMA, - { - data: pkg?.contents[packageKey], - source: await options.inputFS.readFile(pkg.filePath, 'utf8'), - filePath: pkg.filePath, - prependKey: `/${encodeJSONKeyComponent(packageKey)}`, - }, - packageKey, - `Invalid config for ${packageKey}`, - ); + + if (pkg?.contents[packageKey]) { + validateSchema.diagnostic( + CONFIG_SCHEMA, + { + data: pkg?.contents[packageKey], + source: await options.inputFS.readFile(pkg.filePath, 'utf8'), + filePath: pkg.filePath, + prependKey: `/${encodeJSONKeyComponent(packageKey)}`, + }, + packageKey, + `Invalid config for ${packageKey}`, + ); + } let name = pkg?.contents?.name ?? ''; return { From efc3575438e509450a050cfb11c46831c04c91c5 Mon Sep 17 00:00:00 2001 From: mattcompiles Date: Wed, 6 Sep 2023 08:08:51 +1000 Subject: [PATCH 10/11] Fix tests --- packages/packagers/js/src/index.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/packagers/js/src/index.js b/packages/packagers/js/src/index.js index d65fb965ea5..f73e7d51ed7 100644 --- a/packages/packagers/js/src/index.js +++ b/packages/packagers/js/src/index.js @@ -34,10 +34,9 @@ export default (new Packager({ async loadConfig({config, options}): Promise { // Generate a name for the global parcelRequire function that is unique to this project. // This allows multiple parcel builds to coexist on the same page. - let pkg = nullthrows( - await config.getConfigFrom(path.join(options.projectRoot, 'index'), [ - 'package.json', - ]), + let pkg = await config.getConfigFrom( + path.join(options.projectRoot, 'index'), + ['package.json'], ); let packageKey = '@parcel/packager-js'; From 42e815cec766682fd2db1c0dac8a55bec1b71882 Mon Sep 17 00:00:00 2001 From: mattcompiles Date: Thu, 7 Sep 2023 08:22:42 +1000 Subject: [PATCH 11/11] Add bundle queue runtime comments --- packages/packagers/js/src/helpers.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/packagers/js/src/helpers.js b/packages/packagers/js/src/helpers.js index 95a0331111d..4d31038755b 100644 --- a/packages/packagers/js/src/helpers.js +++ b/packages/packagers/js/src/helpers.js @@ -48,7 +48,9 @@ export const fnExpr = ( export const bundleQueuePrelude = (env: Environment): string => ` if (!$parcel$global.lb) { + // Set of loaded bundles $parcel$global.lb = new Set(); + // Queue of bundles to execute once they're dep bundles are loaded $parcel$global.bq = []; // Register loaded bundle @@ -61,6 +63,9 @@ if (!$parcel$global.lb) { // Run when ready $parcel$global.rwr = ${fnExpr( env, + // b = bundle public id + // r = run function to execute the bundle entry + // d = list of dependent bundles this bundle requires before executing ['b', 'r', 'd'], ['$parcel$global.bq.push({b, r, d});', '$parcel$global.pq();'], )}