Skip to content

Commit

Permalink
Support custom rendering for dynamic imports
Browse files Browse the repository at this point in the history
  • Loading branch information
lukastaegert committed Mar 20, 2020
1 parent a79ac92 commit fc29fa6
Show file tree
Hide file tree
Showing 19 changed files with 166 additions and 42 deletions.
19 changes: 12 additions & 7 deletions src/Chunk.ts
Expand Up @@ -16,6 +16,7 @@ import Module from './Module';
import {
DecodedSourceMapOrMissing,
GlobalsOption,
InternalModuleFormat,
OutputOptions,
PreRenderedChunk,
RenderedChunk,
Expand Down Expand Up @@ -411,7 +412,7 @@ export default class Chunk {
}

// prerender allows chunk hashes and names to be generated before finalizing
preRender(options: OutputOptions, inputBase: string) {
preRender(options: OutputOptions, inputBase: string, outputPluginDriver: PluginDriver) {
timeStart('render modules', 3);

const magicString = new MagicStringBundle({ separator: options.compact ? '' : '\n\n' });
Expand All @@ -424,10 +425,11 @@ export default class Chunk {
const renderOptions: RenderOptions = {
compact: options.compact as boolean,
dynamicImportFunction: options.dynamicImportFunction as string,
format: options.format as string,
format: options.format as InternalModuleFormat,
freeze: options.freeze !== false,
indent: this.indentString,
namespaceToStringTag: options.namespaceToStringTag === true,
outputPluginDriver,
varOrConst: options.preferConst ? 'const' : 'var'
};

Expand Down Expand Up @@ -520,7 +522,7 @@ export default class Chunk {
timeStart('render format', 3);

const chunkId = this.id!;
const format = options.format as string;
const format = options.format as InternalModuleFormat;
const finalise = finalisers[format];
if (options.dynamicImportFunction && format !== 'es') {
this.graph.warn({
Expand Down Expand Up @@ -720,7 +722,10 @@ export default class Chunk {
}
}

private finaliseImportMetas(format: string, outputPluginDriver: PluginDriver): void {
private finaliseImportMetas(
format: InternalModuleFormat,
outputPluginDriver: PluginDriver
): void {
for (const [module, code] of this.renderedModuleSources) {
for (const importMeta of module.importMetas) {
importMeta.renderFinalMechanism(code, this.id!, format, outputPluginDriver);
Expand Down Expand Up @@ -923,12 +928,12 @@ export default class Chunk {
if (resolution instanceof Module) {
if (resolution.chunk === this) {
const namespace = resolution.getOrCreateNamespace();
node.setResolution('named', namespace);
node.setResolution('named', resolution, namespace);
} else {
node.setResolution(resolution.chunk!.exportMode);
node.setResolution(resolution.chunk!.exportMode, resolution);
}
} else {
node.setResolution('auto');
node.setResolution('auto', resolution);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/ModuleLoader.ts
Expand Up @@ -13,7 +13,6 @@ import {
ResolveIdResult,
TransformModuleJSON
} from './rollup/types';
import { resolveId } from './utils/defaultPlugin';
import {
errBadLoader,
errCannotAssignModuleToChunk,
Expand All @@ -31,6 +30,7 @@ import { readFile } from './utils/fs';
import { isRelative, resolve } from './utils/path';
import { PluginDriver } from './utils/PluginDriver';
import relativeId from './utils/relativeId';
import { resolveId } from './utils/resolveId';
import { timeEnd, timeStart } from './utils/timers';
import transform from './utils/transform';

Expand Down
31 changes: 20 additions & 11 deletions src/ast/nodes/ImportExpression.ts
@@ -1,4 +1,6 @@
import MagicString from 'magic-string';
import ExternalModule from '../../ExternalModule';
import Module from '../../Module';
import { findFirstOccurrenceOutsideComment, RenderOptions } from '../../utils/renderHelpers';
import { INTEROP_NAMESPACE_VARIABLE } from '../../utils/variableNames';
import { InclusionContext } from '../ExecutionContext';
Expand All @@ -12,11 +14,12 @@ interface DynamicImportMechanism {
}

export default class Import extends NodeBase {
inlineNamespace?: NamespaceVariable;
inlineNamespace: NamespaceVariable | null = null;
source!: ExpressionNode;
type!: NodeType.tImportExpression;

private exportMode: 'none' | 'named' | 'default' | 'auto' = 'auto';
private resolution: Module | ExternalModule | string | null = null;

hasEffects(): boolean {
return true;
Expand Down Expand Up @@ -47,7 +50,6 @@ export default class Import extends NodeBase {
return;
}

// TODO Lukas get the import mechanism via the hook
const importMechanism = this.getDynamicImportMechanism(options);
if (importMechanism) {
code.overwrite(
Expand All @@ -66,9 +68,11 @@ export default class Import extends NodeBase {

setResolution(
exportMode: 'none' | 'named' | 'default' | 'auto',
inlineNamespace?: NamespaceVariable
resolution: Module | ExternalModule | string | null,
inlineNamespace: NamespaceVariable | false = false
): void {
this.exportMode = exportMode;
this.resolution = resolution;
if (inlineNamespace) {
this.inlineNamespace = inlineNamespace;
} else {
Expand All @@ -86,14 +90,19 @@ export default class Import extends NodeBase {
}
}

private getDynamicImportMechanism(
options: RenderOptions
// pluginDriver: PluginDriver
): DynamicImportMechanism | null {
// const mechanism = pluginDriver.hookFirstSync('renderDynamicImport', [{}]);
// if (mechanism) {
// return mechanism;
// }
private getDynamicImportMechanism(options: RenderOptions): DynamicImportMechanism | null {
const mechanism = options.outputPluginDriver.hookFirstSync('renderDynamicImport', [
{
customResolution: typeof this.resolution === 'string' ? this.resolution : null,
format: options.format,
moduleId: this.context.module.id,
targetModuleId:
this.resolution && typeof this.resolution !== 'string' ? this.resolution.id : null
}
]);
if (mechanism) {
return mechanism;
}
switch (options.format) {
case 'cjs': {
const _ = options.compact ? '' : ' ';
Expand Down
3 changes: 2 additions & 1 deletion src/ast/nodes/MetaProperty.ts
@@ -1,4 +1,5 @@
import MagicString from 'magic-string';
import { InternalModuleFormat } from '../../rollup/types';
import { dirname, normalize, relative } from '../../utils/path';
import { PluginDriver } from '../../utils/PluginDriver';
import { ObjectPathKey } from '../utils/PathTracker';
Expand Down Expand Up @@ -56,7 +57,7 @@ export default class MetaProperty extends NodeBase {
renderFinalMechanism(
code: MagicString,
chunkId: string,
format: string,
format: InternalModuleFormat,
outputPluginDriver: PluginDriver
): void {
if (!this.included) return;
Expand Down
2 changes: 1 addition & 1 deletion src/rollup/rollup.ts
Expand Up @@ -212,7 +212,7 @@ export async function rollupInternal(
chunk.exportMode = getExportMode(chunk, outputOptions, chunk.facadeModule!.id);
}
for (const chunk of chunks) {
chunk.preRender(outputOptions, inputBase);
chunk.preRender(outputOptions, inputBase, outputPluginDriver);
}
assignChunkIds(
chunks,
Expand Down
35 changes: 16 additions & 19 deletions src/rollup/types.d.ts
Expand Up @@ -261,15 +261,15 @@ export type ResolveDynamicImportHook = (
export type ResolveImportMetaHook = (
this: PluginContext,
prop: string | null,
options: { chunkId: string; format: string; moduleId: string }
options: { chunkId: string; format: InternalModuleFormat; moduleId: string }
) => string | null | undefined;

export type ResolveAssetUrlHook = (
this: PluginContext,
options: {
assetFileName: string;
chunkId: string;
format: string;
format: InternalModuleFormat;
moduleId: string;
relativeAssetPath: string;
}
Expand All @@ -282,7 +282,7 @@ export type ResolveFileUrlHook = (
chunkId: string;
chunkReferenceId: string | null;
fileName: string;
format: string;
format: InternalModuleFormat;
moduleId: string;
referenceId: string;
relativePath: string;
Expand Down Expand Up @@ -337,10 +337,15 @@ interface OutputPluginHooks {
) => void | Promise<void>;
outputOptions: (this: PluginContext, options: OutputOptions) => OutputOptions | null | undefined;
renderChunk: RenderChunkHook;
// renderDynamicImport: (
// this: PluginContext,
// options: {}
// ) => { left: string; right: string } | null | undefined;
renderDynamicImport: (
this: PluginContext,
options: {
customResolution: string | null;
format: InternalModuleFormat;
moduleId: string;
targetModuleId: string | null;
}
) => { left: string; right: string } | null | undefined;
renderError: (this: PluginContext, err?: Error) => Promise<void> | void;
renderStart: (
this: PluginContext,
Expand Down Expand Up @@ -377,7 +382,7 @@ export type SyncPluginHooks = Exclude<keyof PluginHooks, AsyncPluginHooks>;

export type FirstPluginHooks =
| 'load'
// | 'renderDynamicImport'
| 'renderDynamicImport'
| 'resolveAssetUrl'
| 'resolveDynamicImport'
| 'resolveFileUrl'
Expand Down Expand Up @@ -461,17 +466,9 @@ export interface InputOptions {
watch?: WatcherOptions;
}

export type ModuleFormat =
| 'amd'
| 'cjs'
| 'commonjs'
| 'es'
| 'esm'
| 'iife'
| 'module'
| 'system'
| 'systemjs'
| 'umd';
export type InternalModuleFormat = 'amd' | 'cjs' | 'es' | 'iife' | 'system' | 'umd';

export type ModuleFormat = InternalModuleFormat | 'commonjs' | 'esm' | 'module' | 'systemjs';

export type OptionsPaths = Record<string, string> | ((id: string) => string);

Expand Down
5 changes: 4 additions & 1 deletion src/utils/renderHelpers.ts
@@ -1,14 +1,17 @@
import MagicString from 'magic-string';
import { Node, StatementNode } from '../ast/nodes/shared/Node';
import { InternalModuleFormat } from '../rollup/types';
import { PluginDriver } from './PluginDriver';
import { treeshakeNode } from './treeshakeNode';

export interface RenderOptions {
compact: boolean;
dynamicImportFunction: string;
format: string;
format: InternalModuleFormat;
freeze: boolean;
indent: string;
namespaceToStringTag: boolean;
outputPluginDriver: PluginDriver;
varOrConst: 'var' | 'const';
}

Expand Down
1 change: 0 additions & 1 deletion src/utils/defaultPlugin.ts → src/utils/resolveId.ts
Expand Up @@ -3,7 +3,6 @@ import { lstatSync, readdirSync, realpathSync } from './fs';
import { basename, dirname, isAbsolute, resolve } from './path';
import { PluginDriver } from './PluginDriver';

// TODO Lukas rename file
export async function resolveId(
source: string,
importer: string | undefined,
Expand Down
22 changes: 22 additions & 0 deletions test/chunking-form/samples/render-dynamic-import/_config.js
@@ -0,0 +1,22 @@
const path = require('path');

module.exports = {
description: 'supports custom rendering for dynamic imports',
options: {
plugins: {
name: 'test-plugin',
resolveDynamicImport(specifier) {
if (typeof specifier === 'object' && specifier.name === 'someResolvedVariable') {
return 'someCustomlyResolvedVariable';
}
},
renderDynamicImport({ customResolution, format, moduleId, targetModuleId }) {
return {
left: `${format}SpecialHandler(`,
right: `, '${path.relative(__dirname, moduleId)}', '${targetModuleId &&
path.relative(__dirname, targetModuleId)}', ${customResolution})`
};
}
}
}
};
@@ -0,0 +1,5 @@
define(function () { 'use strict';

console.log('special');

});
@@ -0,0 +1,26 @@
define(['require'], function (require) { 'use strict';

function _interopNamespace(e) {
if (e && e.__esModule) { return e; } else {
var n = {};
if (e) {
Object.keys(e).forEach(function (k) {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () {
return e[k];
}
});
});
}
n['default'] = e;
return n;
}
}

amdSpecialHandler('./generated-imported-via-special-handler', 'main.js', 'imported-via-special-handler.js', null);
amdSpecialHandler(someVariable, 'main.js', 'null', null);
amdSpecialHandler(someCustomlyResolvedVariable, 'main.js', 'null', someCustomlyResolvedVariable);

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

console.log('special');
@@ -0,0 +1,24 @@
'use strict';

function _interopNamespace(e) {
if (e && e.__esModule) { return e; } else {
var n = {};
if (e) {
Object.keys(e).forEach(function (k) {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () {
return e[k];
}
});
});
}
n['default'] = e;
return n;
}
}

cjsSpecialHandler('./generated-imported-via-special-handler.js', 'main.js', 'imported-via-special-handler.js', null);
cjsSpecialHandler(someVariable, 'main.js', 'null', null);
cjsSpecialHandler(someCustomlyResolvedVariable, 'main.js', 'null', someCustomlyResolvedVariable);
@@ -0,0 +1 @@
console.log('special');
@@ -0,0 +1,3 @@
esSpecialHandler('./generated-imported-via-special-handler.js', 'main.js', 'imported-via-special-handler.js', null);
esSpecialHandler(someVariable, 'main.js', 'null', null);
esSpecialHandler(someCustomlyResolvedVariable, 'main.js', 'null', someCustomlyResolvedVariable);
@@ -0,0 +1,10 @@
System.register([], function () {
'use strict';
return {
execute: function () {

console.log('special');

}
};
});
@@ -0,0 +1,12 @@
System.register([], function (exports, module) {
'use strict';
return {
execute: function () {

systemSpecialHandler('./generated-imported-via-special-handler.js', 'main.js', 'imported-via-special-handler.js', null);
systemSpecialHandler(someVariable, 'main.js', 'null', null);
systemSpecialHandler(someCustomlyResolvedVariable, 'main.js', 'null', someCustomlyResolvedVariable);

}
};
});
@@ -0,0 +1 @@
console.log('special');

0 comments on commit fc29fa6

Please sign in to comment.