Skip to content

Commit

Permalink
Ensure reexports are available for namespaces (#4499)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukastaegert committed May 19, 2022
1 parent 0537d93 commit 05d294f
Show file tree
Hide file tree
Showing 20 changed files with 191 additions and 6 deletions.
22 changes: 21 additions & 1 deletion src/Chunk.ts
Expand Up @@ -148,6 +148,7 @@ export default class Chunk {
private implicitEntryModules: Module[] = [];
private readonly implicitlyLoadedBefore = new Set<Chunk>();
private readonly imports = new Set<Variable>();
private readonly includedReexportsByModule = new Map<Module, Variable[]>();
private indentString: string = undefined as never;
// This may only be updated in the constructor
private readonly isEmpty: boolean = true;
Expand All @@ -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,
Expand Down Expand Up @@ -399,6 +401,9 @@ export default class Chunk {
this.exports.add(module.namespace);
}
}
if (!this.outputOptions.preserveModules) {
this.addNecessaryImportsForFacades();
}
return facades;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -905,13 +921,17 @@ 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);
}
}
}
}
}
if (includedReexports.length) {
this.includedReexportsByModule.set(module, includedReexports);
}
}

private finaliseDynamicImports(
Expand Down Expand Up @@ -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) {
Expand Down
10 changes: 5 additions & 5 deletions src/Module.ts
Expand Up @@ -206,8 +206,8 @@ export default class Module {
readonly importMetas: MetaProperty[] = [];
importedFromNotTreeshaken = false;
readonly importers: string[] = [];
readonly imports = new Set<Variable>();
readonly includedDynamicImporters: Module[] = [];
readonly includedImports = new Set<Variable>();
readonly info: ModuleInfo;
isExecuted = false;
isUserDefinedEntryPoint = false;
Expand Down Expand Up @@ -383,7 +383,7 @@ export default class Module {
this.relevantDependencies = new Set<Module | ExternalModule>();
const necessaryDependencies = new Set<Module | ExternalModule>();
const alwaysCheckedDependencies = new Set<Module>();
const dependencyVariables = new Set(this.imports);
const dependencyVariables = new Set(this.includedImports);

if (
this.info.isEntry ||
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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);
}
}

Expand Down
@@ -0,0 +1,9 @@
module.exports = {
description: 'allows sharing imports between dynamic chunks',
options: {
preserveEntrySignatures: 'allow-extension'
},
exports(exports) {
return exports.promise;
}
};
@@ -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;

}));
@@ -0,0 +1,5 @@
define(['./generated-dynamic1', './main'], (function (dynamic1, main) { 'use strict';

console.log(dynamic1.sharedDynamic);

}));
@@ -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 });

}));
@@ -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;
@@ -0,0 +1,6 @@
'use strict';

var dynamic1 = require('./generated-dynamic1.js');
require('./main.js');

console.log(dynamic1.sharedDynamic);
@@ -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;
@@ -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 };
@@ -0,0 +1,4 @@
import { s as sharedDynamic } from './generated-dynamic1.js';
import './main.js';

console.log(sharedDynamic);
@@ -0,0 +1,6 @@
const shared = true;

import('./generated-dynamic1.js').then(function (n) { return n.d; });
console.log(shared);

export { shared as s };
@@ -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);

})
};
}));
@@ -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);

})
};
}));
@@ -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);

})
};
}));
@@ -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';
@@ -0,0 +1 @@
export { sharedDynamic } from './sharedDynamic.js';
7 changes: 7 additions & 0 deletions 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;
});
@@ -0,0 +1 @@
export const shared = true;
@@ -0,0 +1 @@
export const sharedDynamic = true;

0 comments on commit 05d294f

Please sign in to comment.