From 3a6d43da86ca698bcf351be30f0c4ebe0f63b643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lenon?= Date: Fri, 22 Sep 2023 09:37:42 +0100 Subject: [PATCH] util: add `getCwdSafe` internal util fn This function was first implemented in #46826, but at some point of the PR implementation this fn was no longer related to the PR. Refs: https://github.com/nodejs/node/pull/46826#discussion_r1225431178 PR-URL: https://github.com/nodejs/node/pull/48434 Reviewed-By: Jacob Smith Reviewed-By: Yagiz Nizipli Reviewed-By: Antoine du Hamel --- lib/internal/modules/esm/resolve.js | 3 ++- lib/internal/modules/esm/utils.js | 13 ++---------- lib/internal/process/esm_loader.js | 12 ++--------- lib/internal/util.js | 31 +++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index 84276919640118..03725bd0b5dfbe 100644 --- a/lib/internal/modules/esm/resolve.js +++ b/lib/internal/modules/esm/resolve.js @@ -37,6 +37,7 @@ const experimentalNetworkImports = getOptionValue('--experimental-network-imports'); const typeFlag = getOptionValue('--input-type'); const { URL, pathToFileURL, fileURLToPath, isURL } = require('internal/url'); +const { getCWDURL } = require('internal/util'); const { canParse: URLCanParse } = internalBinding('url'); const { legacyMainResolve: FSLegacyMainResolve } = internalBinding('fs'); const { @@ -1095,7 +1096,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 b101bf6f69bac8..af4e975566c83a 100644 --- a/lib/internal/modules/esm/utils.js +++ b/lib/internal/modules/esm/utils.js @@ -21,7 +21,7 @@ const { loadPreloadModules, initializeFrozenIntrinsics, } = require('internal/process/pre_execution'); -const { pathToFileURL } = require('internal/url'); +const { getCWDURL } = require('internal/util'); const { setImportModuleDynamicallyCallback, setInitializeImportMetaObjectCallback, @@ -195,15 +195,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; @@ -220,7 +211,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 3d63b983a2cbed..558a5da69773bb 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -358,6 +358,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}`; @@ -853,6 +883,7 @@ module.exports = { filterDuplicateStrings, filterOwnProperties, getConstructorOf, + getCWDURL, getInternalGlobal, getSystemErrorMap, getSystemErrorName,