Skip to content

Commit

Permalink
Separate namespace inclusion tracking per output, handle dynamic impo…
Browse files Browse the repository at this point in the history
…rts of synthetic namespace modules
  • Loading branch information
lukastaegert committed Aug 19, 2020
1 parent d917447 commit 2a1f487
Show file tree
Hide file tree
Showing 18 changed files with 132 additions and 42 deletions.
2 changes: 2 additions & 0 deletions src/Bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { timeEnd, timeStart } from './utils/timers';

export default class Bundle {
private facadeChunkByModule = new Map<Module, Chunk>();
private includedNamespaces = new Set<Module>();

constructor(
private readonly outputOptions: NormalizedOutputOptions,
Expand Down Expand Up @@ -202,6 +203,7 @@ export default class Bundle {
this.graph.modulesById,
chunkByModule,
this.facadeChunkByModule,
this.includedNamespaces,
alias
);
chunks.push(chunk);
Expand Down
31 changes: 19 additions & 12 deletions src/Chunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export default class Chunk {
modulesById: Map<string, Module | ExternalModule>,
chunkByModule: Map<Module, Chunk>,
facadeChunkByModule: Map<Module, Chunk>,
includedNamespaces: Set<Module>,
facadedModule: Module,
facadeName: FacadeName
): Chunk {
Expand All @@ -147,6 +148,7 @@ export default class Chunk {
modulesById,
chunkByModule,
facadeChunkByModule,
includedNamespaces,
null
);
chunk.assignFacadeName(facadeName, facadedModule);
Expand Down Expand Up @@ -219,12 +221,16 @@ export default class Chunk {
private readonly modulesById: Map<string, Module | ExternalModule>,
private readonly chunkByModule: Map<Module, Chunk>,
private readonly facadeChunkByModule: Map<Module, Chunk>,
private readonly includedNamespaces: Set<Module>,
private readonly manualChunkAlias: string | null
) {
this.execIndex = orderedModules.length > 0 ? orderedModules[0].execIndex : Infinity;
const chunkModules = new Set(orderedModules);

for (const module of orderedModules) {
if (module.namespace.included) {
includedNamespaces.add(module);
}
if (this.isEmpty && module.isIncluded()) {
this.isEmpty = false;
}
Expand All @@ -235,8 +241,8 @@ export default class Chunk {
if (!chunkModules.has(importer)) {
this.dynamicEntryModules.push(module);
// Modules with synthetic exports need an artificial namespace for dynamic imports
if (module.syntheticNamedExports) {
module.namespace.include();
if (module.syntheticNamedExports && !outputOptions.preserveModules) {
includedNamespaces.add(module);
this.exports.add(module.namespace);
}
}
Expand Down Expand Up @@ -360,6 +366,7 @@ export default class Chunk {
this.modulesById,
this.chunkByModule,
this.facadeChunkByModule,
this.includedNamespaces,
module,
facadeName
)
Expand All @@ -380,7 +387,7 @@ export default class Chunk {
) {
this.strictFacade = true;
} else if (!this.facadeChunkByModule.get(module)?.strictFacade) {
module.namespace.include();
this.includedNamespaces.add(module);
this.exports.add(module.namespace);
}
}
Expand Down Expand Up @@ -582,7 +589,7 @@ export default class Chunk {

for (const module of this.orderedModules) {
let renderedLength = 0;
if (module.isIncluded()) {
if (module.isIncluded() || this.includedNamespaces.has(module)) {
const source = module.render(renderOptions).trim();
renderedLength = source.length();
if (renderedLength) {
Expand All @@ -592,7 +599,7 @@ export default class Chunk {
this.usedModules.push(module);
}
const namespace = module.namespace;
if (namespace.included && !this.outputOptions.preserveModules) {
if (this.includedNamespaces.has(module) && !this.outputOptions.preserveModules) {
const rendered = namespace.renderBlock(renderOptions);
if (namespace.renderFirst()) hoistedSource += n + rendered;
else magicString.addSource(new MagicString(rendered));
Expand Down Expand Up @@ -1260,7 +1267,8 @@ export default class Chunk {
this.chunkByModule,
syntheticExports,
this.exportNamesByVariable,
this.accessedGlobalsByScope
this.accessedGlobalsByScope,
this.includedNamespaces
);
}

Expand All @@ -1269,9 +1277,8 @@ export default class Chunk {
// when we are not preserving modules, we need to make all namespace variables available for
// rendering the namespace object
if (!this.outputOptions.preserveModules) {
const namespace = module.namespace;
if (namespace.included) {
const memberVariables = namespace.getMemberVariables();
if (this.includedNamespaces.has(module)) {
const memberVariables = module.namespace.getMemberVariables();
for (const name of Object.keys(memberVariables)) {
moduleImports.add(memberVariables[name]);
}
Expand All @@ -1296,7 +1303,7 @@ export default class Chunk {
}
}
if (
module.namespace.included ||
this.includedNamespaces.has(module) ||
(module.isEntryPoint && module.preserveSignature !== false) ||
module.includedDynamicImporters.some(importer => this.chunkByModule.get(importer) !== this)
) {
Expand All @@ -1307,9 +1314,9 @@ export default class Chunk {
node.included &&
resolution instanceof Module &&
this.chunkByModule.get(resolution) === this &&
!resolution.namespace.included
!this.includedNamespaces.has(resolution)
) {
resolution.namespace.include();
this.includedNamespaces.add(resolution);
this.ensureReexportsAreAvailableForModule(resolution);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export default class Graph {
private includeStatements() {
for (const module of [...this.entryModules, ...this.implicitEntryModules]) {
if (module.preserveSignature !== false) {
module.includeAllExports();
module.includeAllExports(false);
} else {
markModuleAndImpureDependenciesAsExecuted(module);
}
Expand Down
16 changes: 9 additions & 7 deletions src/Module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export interface AstContext {
getModuleName: () => string;
getReexports: () => string[];
importDescriptions: { [name: string]: ImportDescription };
includeAndGetAdditionalMergedNamespaces: () => Variable[];
includeAllExports: () => void;
includeDynamicImport: (node: ImportExpression) => void;
includeVariable: (variable: Variable) => void;
magicString: MagicString;
Expand Down Expand Up @@ -529,14 +529,14 @@ export default class Module {
if (this.ast.shouldBeIncluded(context)) this.ast.include(context, false);
}

includeAllExports() {
includeAllExports(includeNamespaceMembers: boolean) {
if (!this.isExecuted) {
this.graph.needsTreeshakingPass = true;
markModuleAndImpureDependenciesAsExecuted(this);
}

for (const exportName of this.getExports()) {
if (exportName !== this.syntheticNamedExports) {
if (includeNamespaceMembers || exportName !== this.syntheticNamedExports) {
const variable = this.getVariableForExportName(exportName);
variable.deoptimizePath(UNKNOWN_PATH);
if (!variable.included) {
Expand All @@ -557,6 +557,10 @@ export default class Module {
variable.module.reexported = true;
}
}

if (includeNamespaceMembers) {
this.namespace.prepareNamespace(this.includeAndGetAdditionalMergedNamespaces());
}
}

includeAllInBundle() {
Expand Down Expand Up @@ -681,9 +685,7 @@ export default class Module {
getModuleName: this.basename.bind(this),
getReexports: this.getReexports.bind(this),
importDescriptions: this.importDescriptions,
includeAndGetAdditionalMergedNamespaces: this.includeAndGetAdditionalMergedNamespaces.bind(
this
),
includeAllExports: () => this.includeAllExports(true),
includeDynamicImport: this.includeDynamicImport.bind(this),
includeVariable: this.includeVariable.bind(this),
magicString: this.magicString,
Expand Down Expand Up @@ -928,7 +930,7 @@ export default class Module {
}).resolution;
if (resolution instanceof Module) {
resolution.includedDynamicImporters.push(this);
resolution.includeAllExports();
resolution.includeAllExports(true);
}
}

Expand Down
25 changes: 11 additions & 14 deletions src/ast/variables/NamespaceVariable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,23 @@ export default class NamespaceVariable extends Variable {
memberVariables[name] = this.context.traceExport(name);
}
}
this.memberVariables = memberVariables;
return (this.memberVariables = memberVariables);
}

include() {
if (!this.included) {
this.included = true;
for (const identifier of this.references) {
if (identifier.context.getModuleExecIndex() <= this.context.getModuleExecIndex()) {
this.referencedEarly = true;
break;
}
}
this.mergedNamespaces = this.context.includeAndGetAdditionalMergedNamespaces();
const memberVariables = this.getMemberVariables();
// We directly include the variables instead of context.include to not automatically
// generate imports for members from other modules
for (const memberName of Object.keys(memberVariables)) memberVariables[memberName].include();
if (typeof this.syntheticNamedExports === 'string') {
this.context.traceExport(this.syntheticNamedExports).include();
this.context.includeAllExports();
}
}

prepareNamespace(mergedNamespaces: Variable[]) {
this.mergedNamespaces = mergedNamespaces;
const moduleExecIndex = this.context.getModuleExecIndex();
for (const identifier of this.references) {
if (identifier.context.getModuleExecIndex() <= moduleExecIndex) {
this.referencedEarly = true;
break;
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/finalisers/shared/getInteropBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ export default function getInteropBlock(
...(imports || []),
...(reexports || [])
] as ReexportSpecifier[]) {
let helper: string | null = null;
let variableName: string;
let helper: string | undefined | null;
let variableName: string | undefined;
if (imported === 'default') {
if (!hasDefault) {
hasDefault = true;
Expand All @@ -79,7 +79,7 @@ export default function getInteropBlock(
}
}
if (helper) {
addInteropStatement(variableName, helper, name);
addInteropStatement(variableName!, helper, name);
}
}
}
Expand Down
15 changes: 10 additions & 5 deletions src/utils/deconflictChunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ export function deconflictChunk(
chunkByModule: Map<Module, Chunk>,
syntheticExports: Set<SyntheticNamedExportVariable>,
exportNamesByVariable: Map<Variable, string[]>,
accessedGlobalsByScope: Map<ChildScope, Set<string>>
accessedGlobalsByScope: Map<ChildScope, Set<string>>,
includedNamespaces: Set<Module>
) {
for (const module of modules) {
module.scope.addUsedOutsideNames(
Expand All @@ -62,7 +63,7 @@ export function deconflictChunk(
accessedGlobalsByScope
);
}
deconflictTopLevelVariables(usedNames, modules);
deconflictTopLevelVariables(usedNames, modules, includedNamespaces);
DECONFLICT_IMPORTED_VARIABLES_BY_FORMAT[format](
usedNames,
imports,
Expand Down Expand Up @@ -201,7 +202,11 @@ function deconflictImportsOther(
}
}

function deconflictTopLevelVariables(usedNames: Set<string>, modules: Module[]) {
function deconflictTopLevelVariables(
usedNames: Set<string>,
modules: Module[],
includedNamespaces: Set<Module>
) {
for (const module of modules) {
for (const variable of module.scope.variables.values()) {
if (
Expand All @@ -215,8 +220,8 @@ function deconflictTopLevelVariables(usedNames: Set<string>, modules: Module[])
variable.setRenderNames(null, getSafeName(variable.name, usedNames));
}
}
const namespace = module.namespace;
if (namespace.included) {
if (includedNamespaces.has(module)) {
const namespace = module.namespace;
namespace.setRenderNames(null, getSafeName(namespace.name, usedNames));
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
description:
'handle dynamically importing a module with synthetic named exports when preserving modules',
options: {
input: ['main', 'lib'],
plugins: {
name: 'test-plugin',
transform(code, id) {
if (id.endsWith('lib.js')) {
return { code, syntheticNamedExports: '__moduleExports' };
}
}
},
output: {
preserveModules: true
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
define(function () { 'use strict';

const __moduleExports = { foo: 'bar' };
var lib = 'baz';

return lib;

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
define(['require'], function (require) { 'use strict';

function _interopNamespaceDefaultOnly(e) {
return Object.freeze({__proto__: null, 'default': e});
}

new Promise(function (resolve, reject) { require(['./lib'], function (m) { resolve(/*#__PURE__*/_interopNamespaceDefaultOnly(m)); }, reject) }).then(console.log);

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use strict';

const __moduleExports = { foo: 'bar' };
var lib = 'baz';

module.exports = lib;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

function _interopNamespaceDefaultOnly(e) {
return Object.freeze({__proto__: null, 'default': e});
}

Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefaultOnly(require('./lib.js')); }).then(console.log);
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const __moduleExports = { foo: 'bar' };
var lib = 'baz';

export default lib;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import('./lib.js').then(console.log);
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
System.register([], function (exports) {
'use strict';
return {
execute: function () {

const __moduleExports = { foo: 'bar' };
var lib = exports('default', 'baz');

}
};
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
System.register([], function (exports, module) {
'use strict';
return {
execute: function () {

module.import('./lib.js').then(console.log);

}
};
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const __moduleExports = { foo: 'bar' };
export default 'baz';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import('./lib.js').then(console.log);

0 comments on commit 2a1f487

Please sign in to comment.