Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure reexports are available for namespaces #4499

Merged
merged 2 commits into from May 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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;