From 79a7b4ccf0091116be57230cb8d1dafb215c2d93 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Mon, 20 Apr 2026 00:49:18 +0100 Subject: [PATCH] fs: restore fs patchability in ESM loader Temporarily restore fs patchability in ESM loader as a workaround for helping downstream projects that depend on this undocumented hidden contract transition into using hook proper APIs. This patch intentionally avoids adding a test and instead adds warning comments to hopefully steer new code away from depending on it. --- lib/internal/modules/esm/load.js | 8 ++++++-- lib/internal/modules/esm/resolve.js | 8 ++++++-- lib/internal/modules/esm/translators.js | 8 ++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/internal/modules/esm/load.js b/lib/internal/modules/esm/load.js index c284163fba86ec..e8658716f881a9 100644 --- a/lib/internal/modules/esm/load.js +++ b/lib/internal/modules/esm/load.js @@ -9,7 +9,7 @@ const { const { defaultGetFormat } = require('internal/modules/esm/get_format'); const { validateAttributes, emitImportAssertionWarning } = require('internal/modules/esm/assert'); -const { readFileSync } = require('fs'); +const fs = require('fs'); const { Buffer: { from: BufferFrom } } = require('buffer'); @@ -34,7 +34,11 @@ function getSourceSync(url, context) { const responseURL = href; let source; if (protocol === 'file:') { - source = readFileSync(url); + // If you are reading this code to figure out how to patch Node.js module loading + // behavior - DO NOT depend on the patchability in new code: Node.js + // internals may stop going through the JavaScript fs module entirely. + // Prefer module.registerHooks() or other more formal fs hooks released in the future. + source = fs.readFileSync(url); } else if (protocol === 'data:') { const result = dataURLProcessor(url); if (result === 'failure') { diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index cbdc120302443c..b028f44f013886 100644 --- a/lib/internal/modules/esm/resolve.js +++ b/lib/internal/modules/esm/resolve.js @@ -25,7 +25,7 @@ const { const assert = require('internal/assert'); const internalFS = require('internal/fs/utils'); const { BuiltinModule } = require('internal/bootstrap/realm'); -const { realpathSync } = require('fs'); +const fs = require('fs'); const { getOptionValue } = require('internal/options'); // Do not eagerly grab .manifest, it may be in TDZ const { sep, posix: { relative: relativePosixPath }, resolve } = require('path'); @@ -273,7 +273,11 @@ function finalizeResolution(resolved, base, preserveSymlinks) { } if (!preserveSymlinks) { - const real = realpathSync(path, { + // If you are reading this code to figure out how to patch Node.js module loading + // behavior - DO NOT depend on the patchability in new code: Node.js + // internals may stop going through the JavaScript fs module entirely. + // Prefer module.registerHooks() or other more formal fs hooks released in the future. + const real = fs.realpathSync(path, { [internalFS.realpathCacheKey]: realpathCache, }); const { search, hash } = resolved; diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index b7b1843ff35572..c947bf8f69c1bd 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -22,7 +22,7 @@ const { const { BuiltinModule } = require('internal/bootstrap/realm'); const assert = require('internal/assert'); -const { readFileSync } = require('fs'); +const fs = require('fs'); const { dirname, extname } = require('path'); const { assertBufferSource, @@ -343,7 +343,11 @@ translators.set('commonjs', function commonjsStrategy(url, translateContext, par try { // We still need to read the FS to detect the exports. - translateContext.source ??= readFileSync(new URL(url), 'utf8'); + // If you are reading this code to figure out how to patch Node.js module loading + // behavior - DO NOT depend on the patchability in new code: Node.js + // internals may stop going through the JavaScript fs module entirely. + // Prefer module.registerHooks() or other more formal fs hooks released in the future. + translateContext.source ??= fs.readFileSync(new URL(url), 'utf8'); } catch { // Continue regardless of error. }