Commit 45df6b73d59 for nodejs

commit 45df6b73d59e5133118df73e8d5537e30fa33440
Author: Gürgün Dayıoğlu <hey@gurgun.day>
Date:   Mon Nov 2 17:51:36 2026 +0300

    module: avoid reparsing package.json in esm format detection

    Determining the format of a `.js` or `.ts` module called
    `getPackageScopeConfig()`, which spread the deserialized package
    configuration and thereby invoked its lazy `exports` and `imports`
    getters. Every resolved module parsed those maps again, so load time
    and memory grew with the number of modules in a package times the
    size of its `exports` map.

    Use `getPackageType()` for format detection and only look up the
    package.json path when warning about a typeless package. Stop
    spreading the configuration in `getPackageScopeConfig()` so that the
    maps are parsed only when accessed.

    Fixes: https://github.com/nodejs/node/issues/66485
    Signed-off-by: Gürgün Dayıoğlu <hey@gurgun.day>
    PR-URL: https://github.com/nodejs/node/pull/66982
    Reviewed-By: Matteo Collina <matteo.collina@gmail.com>

diff --git a/lib/internal/modules/esm/get_format.js b/lib/internal/modules/esm/get_format.js
index e69a0a01d99..0b7e1ed438e 100644
--- a/lib/internal/modules/esm/get_format.js
+++ b/lib/internal/modules/esm/get_format.js
@@ -149,9 +149,17 @@ function underNodeModules(url) {
 }

 let typelessPackageJsonFilesWarnedAbout;
-function warnTypelessPackageJsonFile(pjsonPath, url) {
+/**
+ * Called for a module with a package.json without a `type` field (or none at all) and ESM syntax.
+ * Warns about the missing `type` field so that the user can avoid the performance penalty of detection.
+ * @param {URL} url
+ */
+function warnIfTypelessPackageJsonFile(url) {
+  if (underNodeModules(url)) { return; }
+  const { pjsonPath, exists } = getPackageScopeConfig(url);
+  if (!exists) { return; }
   typelessPackageJsonFilesWarnedAbout ??= new SafeSet();
-  if (!underNodeModules(url) && !typelessPackageJsonFilesWarnedAbout.has(pjsonPath)) {
+  if (!typelessPackageJsonFilesWarnedAbout.has(pjsonPath)) {
     const warning = `Module type of ${url} is not specified and it doesn't parse as CommonJS.\n` +
       'Reparsing as ES module because module syntax was detected. This incurs a performance overhead.\n' +
       `To eliminate this warning, add "type": "module" to ${pjsonPath}.`;
@@ -173,7 +181,7 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE
   const ext = extname(url);

   if (ext === '.js') {
-    const { type: packageType, pjsonPath, exists: foundPackageJson } = getPackageScopeConfig(url);
+    const packageType = getPackageType(url);
     if (packageType !== 'none') {
       return packageType;
     }
@@ -183,15 +191,13 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE
     // but this gets called again from `defaultLoad`/`defaultLoadSync`.
     // For ambiguous files (.js, no type field) we return undefined from `resolve` and re-run the check in `load`.
     const format = detectModuleFormat(source, url);
-    if (format === 'module' && foundPackageJson) {
-      // This module has a .js extension, a package.json with no `type` field, and ESM syntax.
-      // Warn about the missing `type` field so that the user can avoid the performance penalty of detection.
-      warnTypelessPackageJsonFile(pjsonPath, url);
+    if (format === 'module') {
+      warnIfTypelessPackageJsonFile(url);
     }
     return format;
   }
   if (ext === '.ts' && getOptionValue('--strip-types')) {
-    const { type: packageType, pjsonPath, exists: foundPackageJson } = getPackageScopeConfig(url);
+    const packageType = getPackageType(url);
     if (packageType !== 'none') {
       return `${packageType}-typescript`;
     }
@@ -206,10 +212,8 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE
     const parsedSource = stripTypeScriptModuleTypes(stringifiedSource, fileURLToPath(url));
     const detectedFormat = detectModuleFormat(parsedSource, url);
     const format = `${detectedFormat}-typescript`;
-    if (format === 'module-typescript' && foundPackageJson) {
-      // This module has a .js extension, a package.json with no `type` field, and ESM syntax.
-      // Warn about the missing `type` field so that the user can avoid the performance penalty of detection.
-      warnTypelessPackageJsonFile(pjsonPath, url);
+    if (format === 'module-typescript') {
+      warnIfTypelessPackageJsonFile(url);
     }
     return format;
   }
diff --git a/lib/internal/modules/package_json_reader.js b/lib/internal/modules/package_json_reader.js
index 2ef5d1ebed3..e4455da046a 100644
--- a/lib/internal/modules/package_json_reader.js
+++ b/lib/internal/modules/package_json_reader.js
@@ -264,12 +264,11 @@ function getPackageScopeConfig(resolved) {
   if (ArrayIsArray(result)) {
     const { data, exists, path } = deserializePackageJSON(`${resolved}`, result);

-    return {
-      __proto__: null,
-      ...data,
-      exists,
-      pjsonPath: path,
-    };
+    // `data` is not shared, so extend it in place instead of spreading it:
+    // spreading would invoke the lazy `imports` and `exports` getters.
+    data.exists = exists;
+    data.pjsonPath = path;
+    return data;
   }

   // This means that the response is a string