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

Avoid dynamic import when it's not needed #14192

Merged
merged 1 commit into from Jan 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 24 additions & 19 deletions packages/babel-core/src/config/files/import-meta-resolve.ts
Expand Up @@ -10,33 +10,38 @@ try {
} catch {}

// import.meta.resolve is only available in ESM, but this file is compiled to CJS.
// We can extract ir using dynamic import.
const resolveP =
// We can extract it using dynamic import.
const importMetaResolveP: Promise<ImportMeta["resolve"]> =
import_ &&
// Due to a Node.js/V8 bug (https://github.com/nodejs/node/issues/35889), we cannot
// use dynamic import when running in the default Jest environment because it
// uses vm.SourceTextModule.
// Jest defines globalThis["jest-symbol-do-not-touch"] in
// https://github.com/facebook/jest/blob/11d79ec096a25851124356095d60352f6ca2824e/packages/jest-util/src/installCommonGlobals.ts#L49
// which is called by
// https://github.com/facebook/jest/blob/11d79ec096a25851124356095d60352f6ca2824e/packages/jest-environment-node/src/index.ts#L85
// use always dynamic import because it segfaults when running in a Node.js `vm` context,
// which is used by the default Jest environment and by webpack-cli.
//
// Note that our Jest runner doesn't have this problem, because it runs ESM in the default
// Node.js context rather than using the `vm` module.
// However, import.meta.resolve is experimental and only enabled when Node.js is run
// with the `--experimental-import-meta-resolve` flag: we can avoid calling import()
// when that flag is not enabled, so that the default behavior never segfaults.
//
// When V8 fixes this bug, we can remove this check. We usually don't have package-specific hacks,
// but Jest is a big Babel consumer widely used in the community and they cannot workaround
// this problem on their side.
!Object.hasOwnProperty.call(global, "jest-symbol-do-not-touch")
// Hopefully, before Node.js unflags import.meta.resolve, either:
// - we will move to ESM, so that we have direct access to import.meta.resolve, or
// - the V8 bug will be fixed so that we can safely use dynamic import by default.
//
// I (@nicolo-ribaudo) am really anoyed by this bug, because there is no known
// work-around other than "don't use dynamic import if you are running in a `vm` context",
// but there is no reliable way to detect it (you cannot try/catch segfaults).
//
// This is the only place where we *need* to use dynamic import because we need to access
// an ES module. All the other places will first try using require() and *then*, if
// it throws because it's a module, will fallback to import().
process.execArgv.includes("--experimental-import-meta-resolve")
? import_("data:text/javascript,export default import.meta.resolve").then(
// Since import.meta.resolve is unstable and only available when
// using the --experimental-import-meta-resolve flag, we almost
// always use the polyfill for now.
m => m.default || polyfill,
() => polyfill,
)
: Promise.resolve(polyfill);

export default function getImportMetaResolve(): Promise<ImportMeta["resolve"]> {
return resolveP;
export default async function resolve(
specifier: Parameters<ImportMeta["resolve"]>[0],
parent?: Parameters<ImportMeta["resolve"]>[1],
): ReturnType<ImportMeta["resolve"]> {
return (await importMetaResolveP)(specifier, parent);
}
3 changes: 1 addition & 2 deletions packages/babel-core/src/config/files/plugins.ts
Expand Up @@ -9,7 +9,7 @@ import { isAsync } from "../../gensync-utils/async";
import loadCjsOrMjsDefault, { supportsESM } from "./module-types";
import { fileURLToPath, pathToFileURL } from "url";

import getImportMetaResolve from "./import-meta-resolve";
import importMetaResolve from "./import-meta-resolve";

import { createRequire } from "module";
const require = createRequire(import.meta.url);
Expand Down Expand Up @@ -134,7 +134,6 @@ async function tryImportMetaResolve(
id: Parameters<ImportMeta["resolve"]>[0],
options: Parameters<ImportMeta["resolve"]>[1],
): Promise<Result<string>> {
const importMetaResolve = await getImportMetaResolve();
try {
return { error: null, value: await importMetaResolve(id, options) };
} catch (error) {
Expand Down