Skip to content

Commit

Permalink
Deconflict global variables used inside format-specific code (#2880)
Browse files Browse the repository at this point in the history
* First draft for deconflicting

* Replace objects with Sets and Maps

* Only deconflict format globals if necessary

* Use accessed global variables to optimize wrappers

* Use different globals for file accesses

* Use require.toUrl instead of concatenating strings to resolve files for AMD
  • Loading branch information
lukastaegert committed May 31, 2019
1 parent 70b0844 commit b006d13
Show file tree
Hide file tree
Showing 701 changed files with 1,368 additions and 997 deletions.
79 changes: 43 additions & 36 deletions src/Chunk.ts
Expand Up @@ -36,7 +36,7 @@ import { makeUnique, renderNamePattern } from './utils/renderNamePattern';
import { RESERVED_NAMES } from './utils/reservedNames';
import { sanitizeFileName } from './utils/sanitizeFileName';
import { timeEnd, timeStart } from './utils/timers';
import { MISSING_EXPORT_SHIM_VARIABLE } from './utils/variableNames';
import { INTEROP_DEFAULT_VARIABLE, MISSING_EXPORT_SHIM_VARIABLE } from './utils/variableNames';

export interface ModuleDeclarations {
dependencies: ModuleDeclarationDependency[];
Expand Down Expand Up @@ -130,7 +130,6 @@ export default class Chunk {
exportMode = 'named';
facadeModule: Module | null = null;
graph: Graph;
hasDynamicImport = false;
id: string = undefined as any;
indentString: string = undefined as any;
isEmpty: boolean;
Expand Down Expand Up @@ -596,16 +595,17 @@ export default class Chunk {
if (!this.renderedSource)
throw new Error('Internal error: Chunk render called before preRender');

const finalise = finalisers[options.format as string];
const format = options.format as string;
const finalise = finalisers[format];
if (!finalise) {
error({
code: 'INVALID_OPTION',
message: `Invalid format: ${options.format} - valid options are ${Object.keys(
finalisers
).join(', ')}.`
message: `Invalid format: ${format} - valid options are ${Object.keys(finalisers).join(
', '
)}.`
});
}
if (options.dynamicImportFunction && options.format !== 'es') {
if (options.dynamicImportFunction && format !== 'es') {
this.graph.warn({
code: 'INVALID_OPTION',
message: '"output.dynamicImportFunction" is ignored for formats other than "esm".'
Expand All @@ -628,37 +628,49 @@ export default class Chunk {
renderedDependency.id = relPath;
}

this.finaliseDynamicImports(options.format as string);
const needsAmdModule = this.finaliseImportMetas(options);
this.finaliseDynamicImports(format);
this.finaliseImportMetas(format);

const hasExports =
this.renderedDeclarations.exports.length !== 0 ||
this.renderedDeclarations.dependencies.some(
dep => (dep.reexports && dep.reexports.length !== 0) as boolean
);

const usesTopLevelAwait = this.orderedModules.some(module => module.usesTopLevelAwait);
if (usesTopLevelAwait && options.format !== 'es' && options.format !== 'system') {
let usesTopLevelAwait = false;
const accessedGlobals = new Set<string>();
for (const module of this.orderedModules) {
if (module.usesTopLevelAwait) {
usesTopLevelAwait = true;
}
const accessedGlobalVariablesByFormat = module.scope.accessedGlobalVariablesByFormat;
const accessedGlobalVariables =
accessedGlobalVariablesByFormat && accessedGlobalVariablesByFormat.get(format);
if (accessedGlobalVariables) {
for (const name of accessedGlobalVariables) {
accessedGlobals.add(name);
}
}
}

if (usesTopLevelAwait && format !== 'es' && format !== 'system') {
error({
code: 'INVALID_TLA_FORMAT',
message: `Module format ${
options.format
} does not support top-level await. Use the "es" or "system" output formats rather.`
message: `Module format ${format} does not support top-level await. Use the "es" or "system" output formats rather.`
});
}

const magicString = finalise(
this.renderedSource,
{
accessedGlobals,
dependencies: this.renderedDeclarations.dependencies,
dynamicImport: this.hasDynamicImport,
exports: this.renderedDeclarations.exports,
hasExports,
indentString: this.indentString,
intro: addons.intro as string,
isEntryModuleFacade: this.facadeModule !== null && this.facadeModule.isEntryPoint,
namedExportsMode: this.exportMode !== 'default',
needsAmdModule,
outro: addons.outro as string,
usesTopLevelAwait,
varOrConst: options.preferConst ? 'const' : 'var',
Expand Down Expand Up @@ -828,25 +840,14 @@ export default class Chunk {
}
}

private finaliseImportMetas(options: OutputOptions): boolean {
let needsAmdModule = false;
private finaliseImportMetas(format: string): void {
for (let i = 0; i < this.orderedModules.length; i++) {
const module = this.orderedModules[i];
const code = this.renderedModuleSources[i];
for (const importMeta of module.importMetas) {
if (
importMeta.renderFinalMechanism(
code,
this.id,
options.format as string,
this.graph.pluginDriver
)
) {
needsAmdModule = true;
}
importMeta.renderFinalMechanism(code, this.id, format, this.graph.pluginDriver);
}
}
return needsAmdModule;
}

private getChunkDependencyDeclarations(options: OutputOptions): ChunkDependencies {
Expand Down Expand Up @@ -1050,9 +1051,18 @@ export default class Chunk {
}
}

const usedNames = Object.create(null);
const usedNames = new Set<string>();
if (this.needsExportsShim) {
usedNames[MISSING_EXPORT_SHIM_VARIABLE] = true;
usedNames.add(MISSING_EXPORT_SHIM_VARIABLE);
}
if (options.format !== 'es') {
usedNames.add('exports');
if (options.format === 'cjs') {
usedNames.add(INTEROP_DEFAULT_VARIABLE);
if (this.exportMode === 'default') {
usedNames.add('module');
}
}
}

deconflictChunk(
Expand Down Expand Up @@ -1098,11 +1108,8 @@ export default class Chunk {
}
}
for (const { node, resolution } of module.dynamicImports) {
if (node.included) {
this.hasDynamicImport = true;
if (resolution instanceof Module && resolution.chunk === this)
resolution.getOrCreateNamespace().include();
}
if (node.included && resolution instanceof Module && resolution.chunk === this)
resolution.getOrCreateNamespace().include();
}
}
}
5 changes: 3 additions & 2 deletions src/Module.ts
Expand Up @@ -618,8 +618,9 @@ export default class Module {
}

traceVariable(name: string): Variable | null {
if (name in this.scope.variables) {
return this.scope.variables[name];
const localVariable = this.scope.variables.get(name);
if (localVariable) {
return localVariable;
}

if (name in this.importDescriptions) {
Expand Down
13 changes: 11 additions & 2 deletions src/ast/nodes/Import.ts
Expand Up @@ -47,6 +47,12 @@ const getDynamicImportMechanism = (options: RenderOptions): DynamicImportMechani
return undefined as any;
};

const accessedImportGlobals = {
amd: ['require'],
cjs: ['require'],
system: ['module']
};

export default class Import extends NodeBase {
parent: CallExpression;
type: NodeType.tImport;
Expand All @@ -55,8 +61,11 @@ export default class Import extends NodeBase {
private resolutionNamespace: string;

include() {
this.included = true;
this.context.includeDynamicImport(this);
if (!this.included) {
this.included = true;
this.context.includeDynamicImport(this);
this.scope.addAccessedGlobalsByFormat(accessedImportGlobals);
}
}

initialise() {
Expand Down
34 changes: 25 additions & 9 deletions src/ast/nodes/MetaProperty.ts
@@ -1,4 +1,5 @@
import MagicString from 'magic-string';
import { accessedFileUrlGlobals, accessedMetaUrlGlobals } from '../../utils/defaultPlugin';
import { dirname, normalize, relative } from '../../utils/path';
import { PluginDriver } from '../../utils/pluginDriver';
import { ObjectPathKey } from '../values';
Expand All @@ -15,10 +16,30 @@ export default class MetaProperty extends NodeBase {
property: Identifier;
type: NodeType.tMetaProperty;

private metaProperty?: string | null;

hasEffectsWhenAccessedAtPath(path: ObjectPathKey[]): boolean {
return path.length > 1;
}

include() {
if (!this.included) {
this.included = true;
const parent = this.parent;
const metaProperty = (this.metaProperty =
parent instanceof MemberExpression && typeof parent.propertyKey === 'string'
? parent.propertyKey
: null);
if (metaProperty) {
if (metaProperty === 'url') {
this.scope.addAccessedGlobalsByFormat(accessedMetaUrlGlobals);
} else if (metaProperty.startsWith(ASSET_PREFIX) || metaProperty.startsWith(CHUNK_PREFIX)) {
this.scope.addAccessedGlobalsByFormat(accessedFileUrlGlobals);
}
}
}
}

initialise() {
if (this.meta.name === 'import') {
this.context.addImportMeta(this);
Expand All @@ -31,13 +52,10 @@ export default class MetaProperty extends NodeBase {
chunkId: string,
format: string,
pluginDriver: PluginDriver
): boolean {
if (!this.included) return false;
): void {
if (!this.included) return;
const parent = this.parent;
const importMetaProperty =
parent instanceof MemberExpression && typeof parent.propertyKey === 'string'
? parent.propertyKey
: null;
const importMetaProperty = this.metaProperty as string | null;

if (
importMetaProperty &&
Expand Down Expand Up @@ -86,7 +104,7 @@ export default class MetaProperty extends NodeBase {
(parent as MemberExpression).end,
replacement
);
return true;
return;
}

const replacement = pluginDriver.hookFirstSync('resolveImportMeta', [
Expand All @@ -103,8 +121,6 @@ export default class MetaProperty extends NodeBase {
} else {
code.overwrite(this.start, this.end, replacement);
}
return true;
}
return false;
}
}

0 comments on commit b006d13

Please sign in to comment.