Skip to content

Commit

Permalink
Feat: add syntheticNamedExports
Browse files Browse the repository at this point in the history
  • Loading branch information
manucorporat committed Dec 21, 2019
1 parent 396f238 commit 150abcf
Show file tree
Hide file tree
Showing 14 changed files with 204 additions and 10 deletions.
4 changes: 4 additions & 0 deletions src/Chunk.ts
Expand Up @@ -593,6 +593,10 @@ export default class Chunk {
else magicString.addSource(new MagicString(rendered));
}
}
module.syntheticExports.forEach(s => {
const rendered = s.renderBlock(renderOptions);
magicString.addSource(new MagicString(rendered));
});
}
const { renderedExports, removedExports } = module.getRenderedExports();
renderedModules[module.id] = {
Expand Down
37 changes: 33 additions & 4 deletions src/Module.ts
Expand Up @@ -25,6 +25,7 @@ import { PathTracker, UNKNOWN_PATH } from './ast/utils/PathTracker';
import ExportShimVariable from './ast/variables/ExportShimVariable';
import ExternalVariable from './ast/variables/ExternalVariable';
import NamespaceVariable from './ast/variables/NamespaceVariable';
import SyntheticNamedExport from './ast/variables/SyntheticNamedExport';
import Variable from './ast/variables/Variable';
import Chunk from './Chunk';
import ExternalModule from './ExternalModule';
Expand Down Expand Up @@ -206,6 +207,8 @@ export default class Module {
scope!: ModuleScope;
sourcemapChain!: DecodedSourceMapOrMissing[];
sources = new Set<string>();
syntheticExports = new Map<string, SyntheticNamedExport>();
syntheticNamedExports: boolean;
transformFiles?: EmittedFile[];
userChunkNames = new Set<string>();
usesTopLevelAwait = false;
Expand All @@ -221,12 +224,19 @@ export default class Module {
private transformDependencies: string[] = [];
private transitiveReexports: string[] | null = null;

constructor(graph: Graph, id: string, moduleSideEffects: boolean, isEntry: boolean) {
constructor(
graph: Graph,
id: string,
moduleSideEffects: boolean,
syntheticNamedExports: boolean,
isEntry: boolean
) {
this.id = id;
this.graph = graph;
this.excludeFromSourcemap = /\0/.test(id);
this.context = graph.getModuleContext(id);
this.moduleSideEffects = moduleSideEffects;
this.syntheticNamedExports = syntheticNamedExports;
this.isEntryPoint = isEntry;
}

Expand Down Expand Up @@ -439,9 +449,23 @@ export default class Module {

// we don't want to create shims when we are just
// probing export * modules for exports
if (this.graph.shimMissingExports && !isExportAllSearch) {
this.shimMissingExport(name);
return this.exportShimVariable;
if (!isExportAllSearch) {
if (this.syntheticNamedExports) {
let syntheticExport = this.syntheticExports.get(name);
if (!syntheticExport && !this.exports[name]) {
const defaultVariable = this.astContext.traceExport('default');
syntheticExport = new SyntheticNamedExport(this.astContext, name, defaultVariable as any);
this.syntheticExports.set(name, syntheticExport);
}
if (syntheticExport) {
return syntheticExport;
}
}

if (this.graph.shimMissingExports) {
this.shimMissingExport(name);
return this.exportShimVariable;
}
}
return undefined as any;
}
Expand Down Expand Up @@ -534,6 +558,7 @@ export default class Module {
originalSourcemap,
resolvedIds,
sourcemapChain,
syntheticNamedExports,
transformDependencies,
transformFiles
}: TransformModuleJSON & {
Expand All @@ -551,6 +576,9 @@ export default class Module {
if (typeof moduleSideEffects === 'boolean') {
this.moduleSideEffects = moduleSideEffects;
}
if (typeof syntheticNamedExports === 'boolean') {
this.syntheticNamedExports = syntheticNamedExports;
}

timeStart('generate ast', 3);

Expand Down Expand Up @@ -634,6 +662,7 @@ export default class Module {
originalSourcemap: this.originalSourcemap,
resolvedIds: this.resolvedIds,
sourcemapChain: this.sourcemapChain,
syntheticNamedExports: this.syntheticNamedExports,
transformDependencies: this.transformDependencies,
transformFiles: this.transformFiles
};
Expand Down
37 changes: 32 additions & 5 deletions src/ModuleLoader.ts
Expand Up @@ -17,6 +17,7 @@ import {
errBadLoader,
errCannotAssignModuleToChunk,
errEntryCannotBeExternal,
errExternalSyntheticExports,
errInternalIdCannotBeExternal,
errInvalidOption,
errNamespaceConflict,
Expand Down Expand Up @@ -272,6 +273,7 @@ export class ModuleLoader {
id: string,
importer: string,
moduleSideEffects: boolean,
syntheticNamedExports: boolean,
isEntry: boolean
): Promise<Module> {
const existingModule = this.modulesById.get(id);
Expand All @@ -280,7 +282,13 @@ export class ModuleLoader {
return Promise.resolve(existingModule);
}

const module: Module = new Module(this.graph, id, moduleSideEffects, isEntry);
const module: Module = new Module(
this.graph,
id,
moduleSideEffects,
syntheticNamedExports,
isEntry
);
this.modulesById.set(id, module);
this.graph.watchFiles[id] = true;
const manualChunkAlias = this.getManualChunk(id);
Expand Down Expand Up @@ -321,6 +329,9 @@ export class ModuleLoader {
if (typeof sourceDescription.moduleSideEffects === 'boolean') {
module.moduleSideEffects = sourceDescription.moduleSideEffects;
}
if (typeof sourceDescription.syntheticNamedExports === 'boolean') {
module.syntheticNamedExports = sourceDescription.syntheticNamedExports;
}
return transform(this.graph, sourceDescription, module);
})
.then((source: TransformModuleJSON | ModuleJSON) => {
Expand Down Expand Up @@ -370,7 +381,13 @@ export class ModuleLoader {
}
return Promise.resolve(externalModule);
} else {
return this.fetchModule(resolvedId.id, importer, resolvedId.moduleSideEffects, false);
return this.fetchModule(
resolvedId.id,
importer,
resolvedId.moduleSideEffects,
resolvedId.syntheticNamedExports,
false
);
}
}

Expand All @@ -387,7 +404,8 @@ export class ModuleLoader {
return {
external: true,
id: source,
moduleSideEffects: this.hasModuleSideEffects(source, true)
moduleSideEffects: this.hasModuleSideEffects(source, true),
syntheticNamedExports: false
};
}
return resolvedId;
Expand All @@ -407,7 +425,7 @@ export class ModuleLoader {
: resolveIdResult;

if (typeof id === 'string') {
return this.fetchModule(id, undefined as any, true, isEntry);
return this.fetchModule(id, undefined as any, true, false, isEntry);
}
return error(errUnresolvedEntry(unresolvedId));
});
Expand All @@ -420,6 +438,7 @@ export class ModuleLoader {
let id = '';
let external = false;
let moduleSideEffects = null;
let syntheticNamedExports = false;
if (resolveIdResult) {
if (typeof resolveIdResult === 'object') {
id = resolveIdResult.id;
Expand All @@ -429,6 +448,9 @@ export class ModuleLoader {
if (typeof resolveIdResult.moduleSideEffects === 'boolean') {
moduleSideEffects = resolveIdResult.moduleSideEffects;
}
if (typeof resolveIdResult.syntheticNamedExports === 'boolean') {
syntheticNamedExports = resolveIdResult.syntheticNamedExports;
}
} else {
if (this.isExternal(resolveIdResult, importer, true)) {
external = true;
Expand All @@ -442,13 +464,18 @@ export class ModuleLoader {
}
external = true;
}
if (external && syntheticNamedExports) {
syntheticNamedExports = false;
this.graph.warn(errExternalSyntheticExports(source, importer));
}
return {
external,
id,
moduleSideEffects:
typeof moduleSideEffects === 'boolean'
? moduleSideEffects
: this.hasModuleSideEffects(id, external)
: this.hasModuleSideEffects(id, external),
syntheticNamedExports
};
}

Expand Down
33 changes: 33 additions & 0 deletions src/ast/variables/SyntheticNamedExport.ts
@@ -0,0 +1,33 @@
import Module, { AstContext } from '../../Module';
import { RenderOptions } from '../../utils/renderHelpers';
import { InclusionContext } from '../ExecutionContext';
import ExportDefaultVariable from './ExportDefaultVariable';
import Variable from './Variable';

export default class SyntheticNamedExport extends Variable {
context: AstContext;
defaultVariable: ExportDefaultVariable;
module: Module;

constructor(context: AstContext, name: string, defaultVariable: ExportDefaultVariable) {
super(name);
this.context = context;
this.module = context.module;
this.defaultVariable = defaultVariable;
}

include(context: InclusionContext) {
if (!this.included) {
this.included = true;
this.context.includeVariable(context, this.defaultVariable);
}
}

renderBlock(options: RenderOptions) {
const _ = options.compact ? '' : ' ';
const name = this.getName();
const defaultVariable = this.defaultVariable.getName();
const output = `${options.varOrConst} ${name}${_}=${_}${defaultVariable}.${this.name};`;
return output;
}
}
4 changes: 4 additions & 0 deletions src/rollup/types.d.ts
Expand Up @@ -93,6 +93,7 @@ export interface SourceDescription {
code: string;
map?: SourceMapInput;
moduleSideEffects?: boolean | null;
syntheticNamedExports?: boolean;
}

export interface TransformSourceDescription extends SourceDescription {
Expand All @@ -109,6 +110,7 @@ export interface TransformModuleJSON {
originalSourcemap: ExistingDecodedSourceMap | null;
resolvedIds?: ResolvedIdMap;
sourcemapChain: DecodedSourceMapOrMissing[];
syntheticNamedExports: boolean | null;
transformDependencies: string[];
}

Expand Down Expand Up @@ -199,6 +201,7 @@ export interface ResolvedId {
external: boolean;
id: string;
moduleSideEffects: boolean;
syntheticNamedExports: boolean;
}

export interface ResolvedIdMap {
Expand All @@ -209,6 +212,7 @@ interface PartialResolvedId {
external?: boolean;
id: string;
moduleSideEffects?: boolean | null;
syntheticNamedExports?: boolean;
}

export type ResolveIdResult = string | false | null | undefined | PartialResolvedId;
Expand Down
12 changes: 11 additions & 1 deletion src/utils/error.ts
Expand Up @@ -54,7 +54,8 @@ export enum Errors {
PLUGIN_ERROR = 'PLUGIN_ERROR',
UNRESOLVED_ENTRY = 'UNRESOLVED_ENTRY',
UNRESOLVED_IMPORT = 'UNRESOLVED_IMPORT',
VALIDATION_ERROR = 'VALIDATION_ERROR'
VALIDATION_ERROR = 'VALIDATION_ERROR',
EXTERNAL_SYNTHETIC_EXPORTS = 'EXTERNAL_SYNTHETIC_EXPORTS'
}

export function errAssetNotFinalisedForFileName(name: string) {
Expand Down Expand Up @@ -263,6 +264,15 @@ export function errUnresolvedImportTreatedAsExternal(source: string, importer: s
};
}

export function errExternalSyntheticExports(source: string, importer: string) {
return {
code: Errors.EXTERNAL_SYNTHETIC_EXPORTS,
importer: relativeId(importer),
message: `External '${source}' can not have syntheticNamedExports`,
source
};
}

export function errFailedValidation(message: string) {
return {
code: Errors.VALIDATION_ERROR,
Expand Down
5 changes: 5 additions & 0 deletions src/utils/transform.ts
Expand Up @@ -35,6 +35,7 @@ export default function transform(
const emittedFiles: EmittedFile[] = [];
let customTransformCache = false;
let moduleSideEffects: boolean | null = null;
let syntheticNamedExports: boolean | null = null;
let trackedPluginCache: { cache: PluginCache; used: boolean };
let curPlugin: Plugin;
const curSource: string = source.code;
Expand Down Expand Up @@ -82,6 +83,9 @@ export default function transform(
if (typeof result.moduleSideEffects === 'boolean') {
moduleSideEffects = result.moduleSideEffects;
}
if (typeof result.syntheticNamedExports === 'boolean') {
syntheticNamedExports = result.syntheticNamedExports;
}
} else {
return code;
}
Expand Down Expand Up @@ -193,6 +197,7 @@ export default function transform(
originalCode,
originalSourcemap,
sourcemapChain,
syntheticNamedExports,
transformDependencies
};
});
Expand Down
19 changes: 19 additions & 0 deletions test/chunking-form/samples/synthetic-named-exports/_config.js
@@ -0,0 +1,19 @@
module.exports = {
description: 'simple chunking',
options: {
input: ['main.js'],
plugins: [
{
resolveId(id) {
if (id === './dep1.js') {
return {
id,
syntheticNamedExports: true
};
}
return null;
}
}
]
}
};
@@ -0,0 +1,14 @@
define(function () { 'use strict';

const d = {
fn: 42
};

var fn = d.fn;

var foo = d.foo;

console.log(fn);
console.log(foo);

});
@@ -0,0 +1,12 @@
'use strict';

const d = {
fn: 42
};

var fn = d.fn;

var foo = d.foo;

console.log(fn);
console.log(foo);
@@ -0,0 +1,10 @@
const d = {
fn: 42
};

var fn = d.fn;

var foo = d.foo;

console.log(fn);
console.log(foo);

0 comments on commit 150abcf

Please sign in to comment.