Skip to content

Commit

Permalink
Avoid dynamic import when it's not needed (#14192)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Jan 22, 2022
1 parent e5d29f6 commit 2ea9054
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 21 deletions.
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

0 comments on commit 2ea9054

Please sign in to comment.