diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index 7451bc273d1149..529110f92eeb53 100644 --- a/lib/internal/modules/esm/resolve.js +++ b/lib/internal/modules/esm/resolve.js @@ -38,6 +38,7 @@ const experimentalNetworkImports = getOptionValue('--experimental-network-imports'); const inputTypeFlag = getOptionValue('--input-type'); const { URL, pathToFileURL, fileURLToPath, isURL, toPathIfFileURL } = require('internal/url'); +const { getCWDURL } = require('internal/util'); const { canParse: URLCanParse } = internalBinding('url'); const { ERR_INPUT_TYPE_NOT_ALLOWED, @@ -1175,7 +1176,7 @@ function defaultResolve(specifier, context = {}) { const isMain = parentURL === undefined; if (isMain) { - parentURL = pathToFileURL(`${process.cwd()}/`).href; + parentURL = getCWDURL().href; // This is the initial entry point to the program, and --input-type has // been passed as an option; but --input-type can only be used with diff --git a/lib/internal/modules/esm/utils.js b/lib/internal/modules/esm/utils.js index a5391f9b9bfb78..414e593634e413 100644 --- a/lib/internal/modules/esm/utils.js +++ b/lib/internal/modules/esm/utils.js @@ -16,7 +16,7 @@ const { loadPreloadModules, initializeFrozenIntrinsics, } = require('internal/process/pre_execution'); -const { pathToFileURL } = require('internal/url'); +const { getCWDURL } = require('internal/util'); const { setImportModuleDynamicallyCallback, setInitializeImportMetaObjectCallback, @@ -146,15 +146,6 @@ function isLoaderWorker() { async function initializeHooks() { const customLoaderURLs = getOptionValue('--experimental-loader'); - let cwd; - try { - // `process.cwd()` can fail if the parent directory is deleted while the process runs. - cwd = process.cwd() + '/'; - } catch { - cwd = '/'; - } - - const { Hooks } = require('internal/modules/esm/hooks'); const esmLoader = require('internal/process/esm_loader').esmLoader; @@ -171,7 +162,7 @@ async function initializeHooks() { loadPreloadModules(); initializeFrozenIntrinsics(); - const parentURL = pathToFileURL(cwd).href; + const parentURL = getCWDURL().href; for (let i = 0; i < customLoaderURLs.length; i++) { await hooks.register( customLoaderURLs[i], diff --git a/lib/internal/process/esm_loader.js b/lib/internal/process/esm_loader.js index e735101ab18c47..a3451ddab307f2 100644 --- a/lib/internal/process/esm_loader.js +++ b/lib/internal/process/esm_loader.js @@ -9,8 +9,7 @@ const { getOptionValue } = require('internal/options'); const { hasUncaughtExceptionCaptureCallback, } = require('internal/process/execution'); -const { pathToFileURL } = require('internal/url'); -const { kEmptyObject } = require('internal/util'); +const { kEmptyObject, getCWDURL } = require('internal/util'); let esmLoader; @@ -23,14 +22,7 @@ module.exports = { try { const userImports = getOptionValue('--import'); if (userImports.length > 0) { - let cwd; - try { - // `process.cwd()` can fail if the parent directory is deleted while the process runs. - cwd = process.cwd() + '/'; - } catch { - cwd = '/'; - } - const parentURL = pathToFileURL(cwd).href; + const parentURL = getCWDURL().href; await SafePromiseAllReturnVoid(userImports, (specifier) => esmLoader.import( specifier, parentURL, diff --git a/lib/internal/util.js b/lib/internal/util.js index d9c292c99a6710..aedfa2e98896e7 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -357,6 +357,36 @@ function getConstructorOf(obj) { return null; } +let cachedURL; +let cachedCWD; + +/** + * Get the current working directory while accounting for the possibility that it has been deleted. + * `process.cwd()` can fail if the parent directory is deleted while the process runs. + * @returns {URL} The current working directory or the volume root if it cannot be determined. + */ +function getCWDURL() { + const { sep } = require('path'); + const { pathToFileURL } = require('internal/url'); + + let cwd; + + try { + // The implementation of `process.cwd()` already uses proper cache when it can. + // It's a relatively cheap call performance-wise for the most common use case. + cwd = process.cwd(); + } catch { + cachedURL ??= pathToFileURL(sep); + } + + if (cwd != null && cwd !== cachedCWD) { + cachedURL = pathToFileURL(cwd + sep); + cachedCWD = cwd; + } + + return cachedURL; +} + function getSystemErrorName(err) { const entry = uvErrmapGet(err); return entry ? entry[0] : `Unknown system error ${err}`; @@ -784,6 +814,7 @@ module.exports = { filterDuplicateStrings, filterOwnProperties, getConstructorOf, + getCWDURL, getInternalGlobal, getSystemErrorMap, getSystemErrorName,