From 2ad6f67c25c598759fece4d0bad2ed8ffe403253 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Wed, 18 May 2022 09:22:55 +0200 Subject: [PATCH] Ensure reexports are available for namespaces --- src/Chunk.ts | 22 +++++++++++++++++- src/Module.ts | 10 ++++---- .../dynamic-imports-shared-exports/_config.js | 9 ++++++++ .../_expected/amd/generated-dynamic1.js | 16 +++++++++++++ .../_expected/amd/generated-dynamic2.js | 5 ++++ .../_expected/amd/main.js | 12 ++++++++++ .../_expected/cjs/generated-dynamic1.js | 16 +++++++++++++ .../_expected/cjs/generated-dynamic2.js | 6 +++++ .../_expected/cjs/main.js | 10 ++++++++ .../_expected/es/generated-dynamic1.js | 13 +++++++++++ .../_expected/es/generated-dynamic2.js | 4 ++++ .../_expected/es/main.js | 6 +++++ .../_expected/system/generated-dynamic1.js | 23 +++++++++++++++++++ .../_expected/system/generated-dynamic2.js | 14 +++++++++++ .../_expected/system/main.js | 13 +++++++++++ .../dynamic1.js | 8 +++++++ .../dynamic2.js | 1 + .../dynamic-imports-shared-exports/main.js | 7 ++++++ .../dynamic-imports-shared-exports/shared.js | 1 + .../sharedDynamic.js | 1 + 20 files changed, 191 insertions(+), 6 deletions(-) create mode 100644 test/function/samples/dynamic-imports-shared-exports/_config.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/_expected/amd/generated-dynamic1.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/_expected/amd/generated-dynamic2.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/_expected/amd/main.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/_expected/cjs/generated-dynamic1.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/_expected/cjs/generated-dynamic2.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/_expected/cjs/main.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/_expected/es/generated-dynamic1.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/_expected/es/generated-dynamic2.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/_expected/es/main.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/_expected/system/generated-dynamic1.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/_expected/system/generated-dynamic2.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/_expected/system/main.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/dynamic1.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/dynamic2.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/main.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/shared.js create mode 100644 test/function/samples/dynamic-imports-shared-exports/sharedDynamic.js diff --git a/src/Chunk.ts b/src/Chunk.ts index 4ef5032193d..11e6e3035dd 100644 --- a/src/Chunk.ts +++ b/src/Chunk.ts @@ -148,6 +148,7 @@ export default class Chunk { private implicitEntryModules: Module[] = []; private readonly implicitlyLoadedBefore = new Set(); private readonly imports = new Set(); + private readonly includedReexportsByModule = new Map(); private indentString: string = undefined as never; // This may only be updated in the constructor private readonly isEmpty: boolean = true; @@ -164,6 +165,7 @@ export default class Chunk { private sortedExportNames: string[] | null = null; private strictFacade = false; private usedModules: Module[] = undefined as never; + constructor( private readonly orderedModules: readonly Module[], private readonly inputOptions: NormalizedInputOptions, @@ -399,6 +401,9 @@ export default class Chunk { this.exports.add(module.namespace); } } + if (!this.outputOptions.preserveModules) { + this.addNecessaryImportsForFacades(); + } return facades; } @@ -832,6 +837,16 @@ export default class Chunk { } } + private addNecessaryImportsForFacades() { + for (const [module, variables] of this.includedReexportsByModule) { + if (this.includedNamespaces.has(module)) { + for (const variable of variables) { + this.imports.add(variable); + } + } + } + } + private assignFacadeName({ fileName, name }: FacadeName, facadedModule: Module): void { if (fileName) { this.fileName = fileName; @@ -892,6 +907,7 @@ export default class Chunk { } private ensureReexportsAreAvailableForModule(module: Module): void { + const includedReexports: Variable[] = []; const map = module.getExportNamesByVariable(); for (const exportedVariable of map.keys()) { const isSynthetic = exportedVariable instanceof SyntheticNamedExportVariable; @@ -905,6 +921,7 @@ export default class Chunk { const chunk = this.chunkByModule.get(exportingModule); if (chunk && chunk !== this) { chunk.exports.add(importedVariable); + includedReexports.push(importedVariable); if (isSynthetic) { this.imports.add(importedVariable); } @@ -912,6 +929,9 @@ export default class Chunk { } } } + if (includedReexports.length) { + this.includedReexportsByModule.set(module, includedReexports); + } } private finaliseDynamicImports( @@ -1345,7 +1365,7 @@ export default class Chunk { } private setUpChunkImportsAndExportsForModule(module: Module): void { - const moduleImports = new Set(module.imports); + const moduleImports = new Set(module.includedImports); // when we are not preserving modules, we need to make all namespace variables available for // rendering the namespace object if (!this.outputOptions.preserveModules) { diff --git a/src/Module.ts b/src/Module.ts index 1e7c4ada2ba..439f8dd6c24 100644 --- a/src/Module.ts +++ b/src/Module.ts @@ -206,8 +206,8 @@ export default class Module { readonly importMetas: MetaProperty[] = []; importedFromNotTreeshaken = false; readonly importers: string[] = []; - readonly imports = new Set(); readonly includedDynamicImporters: Module[] = []; + readonly includedImports = new Set(); readonly info: ModuleInfo; isExecuted = false; isUserDefinedEntryPoint = false; @@ -383,7 +383,7 @@ export default class Module { this.relevantDependencies = new Set(); const necessaryDependencies = new Set(); const alwaysCheckedDependencies = new Set(); - const dependencyVariables = new Set(this.imports); + const dependencyVariables = new Set(this.includedImports); if ( this.info.isEntry || @@ -1134,12 +1134,12 @@ export default class Module { if (module instanceof ExternalModule) { const [externalVariable] = module.getVariableForExportName('*'); externalVariable.include(); - this.imports.add(externalVariable); + this.includedImports.add(externalVariable); externalNamespaces.add(externalVariable); } else if (module.info.syntheticNamedExports) { const syntheticNamespace = module.getSyntheticNamespace(); syntheticNamespace.include(); - this.imports.add(syntheticNamespace); + this.includedImports.add(syntheticNamespace); syntheticNamespaces.add(syntheticNamespace); } } @@ -1183,7 +1183,7 @@ export default class Module { this.includeVariable(variable); const variableModule = variable.module; if (variableModule && variableModule !== this) { - this.imports.add(variable); + this.includedImports.add(variable); } } diff --git a/test/function/samples/dynamic-imports-shared-exports/_config.js b/test/function/samples/dynamic-imports-shared-exports/_config.js new file mode 100644 index 00000000000..20609cd5fed --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/_config.js @@ -0,0 +1,9 @@ +module.exports = { + description: 'allows sharing imports between dynamic chunks', + options: { + preserveEntrySignatures: 'allow-extension' + }, + exports(exports) { + return exports.promise; + } +}; diff --git a/test/function/samples/dynamic-imports-shared-exports/_expected/amd/generated-dynamic1.js b/test/function/samples/dynamic-imports-shared-exports/_expected/amd/generated-dynamic1.js new file mode 100644 index 00000000000..a6728c32cca --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/_expected/amd/generated-dynamic1.js @@ -0,0 +1,16 @@ +define(['require', 'exports', './main'], (function (require, exports, main) { 'use strict'; + + const sharedDynamic = true; + + new Promise(function (resolve, reject) { require(['./generated-dynamic2'], resolve, reject); }); + console.log(sharedDynamic); + + var dynamic1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + shared: main.shared + }); + + exports.dynamic1 = dynamic1; + exports.sharedDynamic = sharedDynamic; + +})); diff --git a/test/function/samples/dynamic-imports-shared-exports/_expected/amd/generated-dynamic2.js b/test/function/samples/dynamic-imports-shared-exports/_expected/amd/generated-dynamic2.js new file mode 100644 index 00000000000..fdec6138885 --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/_expected/amd/generated-dynamic2.js @@ -0,0 +1,5 @@ +define(['./generated-dynamic1', './main'], (function (dynamic1, main) { 'use strict'; + + console.log(dynamic1.sharedDynamic); + +})); diff --git a/test/function/samples/dynamic-imports-shared-exports/_expected/amd/main.js b/test/function/samples/dynamic-imports-shared-exports/_expected/amd/main.js new file mode 100644 index 00000000000..ba16bea3d5f --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/_expected/amd/main.js @@ -0,0 +1,12 @@ +define(['require', 'exports'], (function (require, exports) { 'use strict'; + + const shared = true; + + new Promise(function (resolve, reject) { require(['./generated-dynamic1'], resolve, reject); }).then(function (n) { return n.dynamic1; }); + console.log(shared); + + exports.shared = shared; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); diff --git a/test/function/samples/dynamic-imports-shared-exports/_expected/cjs/generated-dynamic1.js b/test/function/samples/dynamic-imports-shared-exports/_expected/cjs/generated-dynamic1.js new file mode 100644 index 00000000000..e9488779991 --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/_expected/cjs/generated-dynamic1.js @@ -0,0 +1,16 @@ +'use strict'; + +var main = require('./main.js'); + +const sharedDynamic = true; + +Promise.resolve().then(function () { return require('./generated-dynamic2.js'); }); +console.log(sharedDynamic); + +var dynamic1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + shared: main.shared +}); + +exports.dynamic1 = dynamic1; +exports.sharedDynamic = sharedDynamic; diff --git a/test/function/samples/dynamic-imports-shared-exports/_expected/cjs/generated-dynamic2.js b/test/function/samples/dynamic-imports-shared-exports/_expected/cjs/generated-dynamic2.js new file mode 100644 index 00000000000..2edc5095062 --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/_expected/cjs/generated-dynamic2.js @@ -0,0 +1,6 @@ +'use strict'; + +var dynamic1 = require('./generated-dynamic1.js'); +require('./main.js'); + +console.log(dynamic1.sharedDynamic); diff --git a/test/function/samples/dynamic-imports-shared-exports/_expected/cjs/main.js b/test/function/samples/dynamic-imports-shared-exports/_expected/cjs/main.js new file mode 100644 index 00000000000..c79d5cbd4bd --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/_expected/cjs/main.js @@ -0,0 +1,10 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +const shared = true; + +Promise.resolve().then(function () { return require('./generated-dynamic1.js'); }).then(function (n) { return n.dynamic1; }); +console.log(shared); + +exports.shared = shared; diff --git a/test/function/samples/dynamic-imports-shared-exports/_expected/es/generated-dynamic1.js b/test/function/samples/dynamic-imports-shared-exports/_expected/es/generated-dynamic1.js new file mode 100644 index 00000000000..41080392c26 --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/_expected/es/generated-dynamic1.js @@ -0,0 +1,13 @@ +import { s as shared } from './main.js'; + +const sharedDynamic = true; + +import('./generated-dynamic2.js'); +console.log(sharedDynamic); + +var dynamic1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + shared: shared +}); + +export { dynamic1 as d, sharedDynamic as s }; diff --git a/test/function/samples/dynamic-imports-shared-exports/_expected/es/generated-dynamic2.js b/test/function/samples/dynamic-imports-shared-exports/_expected/es/generated-dynamic2.js new file mode 100644 index 00000000000..8d36350bc08 --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/_expected/es/generated-dynamic2.js @@ -0,0 +1,4 @@ +import { s as sharedDynamic } from './generated-dynamic1.js'; +import './main.js'; + +console.log(sharedDynamic); diff --git a/test/function/samples/dynamic-imports-shared-exports/_expected/es/main.js b/test/function/samples/dynamic-imports-shared-exports/_expected/es/main.js new file mode 100644 index 00000000000..85b1dbdf138 --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/_expected/es/main.js @@ -0,0 +1,6 @@ +const shared = true; + +import('./generated-dynamic1.js').then(function (n) { return n.d; }); +console.log(shared); + +export { shared as s }; diff --git a/test/function/samples/dynamic-imports-shared-exports/_expected/system/generated-dynamic1.js b/test/function/samples/dynamic-imports-shared-exports/_expected/system/generated-dynamic1.js new file mode 100644 index 00000000000..876ca43649f --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/_expected/system/generated-dynamic1.js @@ -0,0 +1,23 @@ +System.register(['./main.js'], (function (exports, module) { + 'use strict'; + var shared; + return { + setters: [function (module) { + shared = module.s; + }], + execute: (function () { + + const sharedDynamic = exports('s', true); + + module.import('./generated-dynamic2.js'); + console.log(sharedDynamic); + + var dynamic1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + shared: shared + }); + exports('d', dynamic1); + + }) + }; +})); diff --git a/test/function/samples/dynamic-imports-shared-exports/_expected/system/generated-dynamic2.js b/test/function/samples/dynamic-imports-shared-exports/_expected/system/generated-dynamic2.js new file mode 100644 index 00000000000..d6bb0f56874 --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/_expected/system/generated-dynamic2.js @@ -0,0 +1,14 @@ +System.register(['./generated-dynamic1.js', './main.js'], (function () { + 'use strict'; + var sharedDynamic; + return { + setters: [function (module) { + sharedDynamic = module.s; + }, function () {}], + execute: (function () { + + console.log(sharedDynamic); + + }) + }; +})); diff --git a/test/function/samples/dynamic-imports-shared-exports/_expected/system/main.js b/test/function/samples/dynamic-imports-shared-exports/_expected/system/main.js new file mode 100644 index 00000000000..72156af9fde --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/_expected/system/main.js @@ -0,0 +1,13 @@ +System.register([], (function (exports, module) { + 'use strict'; + return { + execute: (function () { + + const shared = exports('s', true); + + module.import('./generated-dynamic1.js').then(function (n) { return n.d; }); + console.log(shared); + + }) + }; +})); diff --git a/test/function/samples/dynamic-imports-shared-exports/dynamic1.js b/test/function/samples/dynamic-imports-shared-exports/dynamic1.js new file mode 100644 index 00000000000..35d8951ee55 --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/dynamic1.js @@ -0,0 +1,8 @@ +import { sharedDynamic } from './sharedDynamic.js'; + +assert.ok(sharedDynamic); +export const promise = import('./dynamic2.js').then(ns => + assert.deepStrictEqual(ns, { sharedDynamic: true }) +); + +export { shared } from './shared.js'; diff --git a/test/function/samples/dynamic-imports-shared-exports/dynamic2.js b/test/function/samples/dynamic-imports-shared-exports/dynamic2.js new file mode 100644 index 00000000000..2355361e83b --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/dynamic2.js @@ -0,0 +1 @@ +export { sharedDynamic } from './sharedDynamic.js'; diff --git a/test/function/samples/dynamic-imports-shared-exports/main.js b/test/function/samples/dynamic-imports-shared-exports/main.js new file mode 100644 index 00000000000..531c8c54b45 --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/main.js @@ -0,0 +1,7 @@ +import { shared } from './shared.js'; + +assert.ok(shared); +export const promise = import('./dynamic1.js').then(({ promise, ...ns }) => { + assert.deepStrictEqual(ns, { shared: true }); + return ns.promise; +}); diff --git a/test/function/samples/dynamic-imports-shared-exports/shared.js b/test/function/samples/dynamic-imports-shared-exports/shared.js new file mode 100644 index 00000000000..b60d9ba03fb --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/shared.js @@ -0,0 +1 @@ +export const shared = true; diff --git a/test/function/samples/dynamic-imports-shared-exports/sharedDynamic.js b/test/function/samples/dynamic-imports-shared-exports/sharedDynamic.js new file mode 100644 index 00000000000..5cf55fe52ef --- /dev/null +++ b/test/function/samples/dynamic-imports-shared-exports/sharedDynamic.js @@ -0,0 +1 @@ +export const sharedDynamic = true;