diff --git a/packages/babel-core/src/config/files/import-meta-resolve.ts b/packages/babel-core/src/config/files/import-meta-resolve.ts index f44bf2c11cf1..11c9731aa912 100644 --- a/packages/babel-core/src/config/files/import-meta-resolve.ts +++ b/packages/babel-core/src/config/files/import-meta-resolve.ts @@ -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 = 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 { - return resolveP; +export default async function resolve( + specifier: Parameters[0], + parent?: Parameters[1], +): ReturnType { + return (await importMetaResolveP)(specifier, parent); } diff --git a/packages/babel-core/src/config/files/plugins.ts b/packages/babel-core/src/config/files/plugins.ts index 4cc017d5b3f8..76a77b7c985a 100644 --- a/packages/babel-core/src/config/files/plugins.ts +++ b/packages/babel-core/src/config/files/plugins.ts @@ -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); @@ -134,7 +134,6 @@ async function tryImportMetaResolve( id: Parameters[0], options: Parameters[1], ): Promise> { - const importMetaResolve = await getImportMetaResolve(); try { return { error: null, value: await importMetaResolve(id, options) }; } catch (error) {