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

Resolve bare specifiers PRIOR to compilation. #3405

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 43 additions & 41 deletions packages/build/src/babel-plugin-dynamic-import-amd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,54 +26,56 @@ export const dynamicImportAmd = {
inherits: dyanamicImportSyntax,

visitor: {
Program(path: NodePath<Program>) {
// We transform dynamic import() into a Promise whose initializer calls
// require(). We must use the "local" require - the one provided by the
// AMD loaded when a module explicitly depends on the "require" module ID.
// To get the emitted define() call to depend on "require", we inject an
// import of a module called "require".
Program: {
exit(path: NodePath<Program>) {
// We transform dynamic import() into a Promise whose initializer calls
// require(). We must use the "local" require - the one provided by the
// AMD loaded when a module explicitly depends on the "require" module
// ID. To get the emitted define() call to depend on "require", we
// inject an import of a module called "require".

// Collect all the NodePaths to dynamic import() expressions
// and all the identifiers in scope for them.
const identifiers = new Set<string>();
const dynamicImports: NodePath<CallExpression>[] = [];
path.traverse({
CallExpression(path: NodePath<CallExpression>) {
if (path.node.callee.type as string === 'Import') {
dynamicImports.push(path);
const bindings = path.scope.getAllBindings();
for (const name of Object.keys(bindings)) {
identifiers.add(name);
// Collect all the NodePaths to dynamic import() expressions
// and all the identifiers in scope for them.
const identifiers = new Set<string>();
const dynamicImports: NodePath<CallExpression>[] = [];
path.traverse({
CallExpression(path: NodePath<CallExpression>) {
if (path.node.callee.type as string === 'Import') {
dynamicImports.push(path);
const bindings = path.scope.getAllBindings();
for (const name of Object.keys(bindings)) {
identifiers.add(name);
}
}
}
}
});
});

if (dynamicImports.length === 0) {
return;
}
if (dynamicImports.length === 0) {
return;
}

// Choose a unique name to import "require" as.
let requireId: Identifier|undefined = undefined;
do {
requireId = path.scope.generateUidIdentifier('require');
} while (identifiers.has(requireId.name));
// Choose a unique name to import "require" as.
let requireId: Identifier|undefined = undefined;
do {
requireId = path.scope.generateUidIdentifier('require');
} while (identifiers.has(requireId.name));

// Inject the import of "require"
const statements = path.node.body as Statement[];
statements.unshift(ast`import * as ${requireId} from 'require';`);
// Inject the import of "require"
const statements = path.node.body as Statement[];
statements.unshift(ast`import * as ${requireId} from 'require';`);

// Transform the dynamic import callsites
for (const importPath of dynamicImports) {
const specifier = importPath.node.arguments[0];
// Call as `require.default` because the AMD transformer that we assume
// is running next will rewrite `require` from a function to a module
// object with the function at `default`.
importPath.replaceWith(ast`(
new Promise((res, rej) => ${requireId}.default([${
specifier}], res, rej))
)`);
}
// Transform the dynamic import callsites
for (const importPath of dynamicImports) {
const specifier = importPath.node.arguments[0];
// Call as `require.default` because the AMD transformer that we
// assume is running next will rewrite `require` from a function to a
// module object with the function at `default`.
importPath.replaceWith(ast`(
new Promise((res, rej) => ${requireId}.default([${
specifier}], res, rej))
)`);
}
},
},
},
};
26 changes: 13 additions & 13 deletions packages/build/src/js-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,19 @@ export function jsTransform(js: string, options: JsTransformOptions): string {
// Minify last, so push first.
presets.push(babelPresetMinify);
}
if (options.moduleResolution === 'node') {
if (!options.filePath) {
throw new Error(
'Cannot perform node module resolution without filePath.');
}
doBabelTransform = true;
plugins.push(resolveBareSpecifiers(
options.filePath,
!!options.isComponentRequest,
options.packageName,
options.componentDir,
options.rootDir));
}
if (options.compile === true || options.compile === 'es5') {
doBabelTransform = true;
plugins.push(...babelTransformEs2015);
Expand All @@ -220,19 +233,6 @@ export function jsTransform(js: string, options: JsTransformOptions): string {
doBabelTransform = true;
plugins.push(...babelTransformEs2018);
}
if (options.moduleResolution === 'node') {
if (!options.filePath) {
throw new Error(
'Cannot perform node module resolution without filePath.');
}
doBabelTransform = true;
plugins.push(resolveBareSpecifiers(
options.filePath,
!!options.isComponentRequest,
options.packageName,
options.componentDir,
options.rootDir));
}

// When the AMD option is "auto", these options will change based on whether
// we have a module or not (unless they are already definitely true).
Expand Down