diff --git a/lib/internal/modules/esm/get_format.js b/lib/internal/modules/esm/get_format.js index 5c551d4af2df22..ae7b73008f3d06 100644 --- a/lib/internal/modules/esm/get_format.js +++ b/lib/internal/modules/esm/get_format.js @@ -95,6 +95,19 @@ function underNodeModules(url) { } let typelessPackageJsonFilesWarnedAbout; +function warnTypelessPackageJsonFile(pjsonPath, url) { + typelessPackageJsonFilesWarnedAbout ??= new SafeSet(); + 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}.`; + process.emitWarning(warning, { + code: 'MODULE_TYPELESS_PACKAGE_JSON', + }); + typelessPackageJsonFilesWarnedAbout.add(pjsonPath); + } +} + /** * @param {URL} url * @param {{parentURL: string; source?: Buffer}} context @@ -106,7 +119,7 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE const ext = extname(url); if (ext === '.js') { - const { type: packageType, pjsonPath } = getPackageScopeConfig(url); + const { type: packageType, pjsonPath, exists: foundPackageJson } = getPackageScopeConfig(url); if (packageType !== 'none') { return packageType; } @@ -127,18 +140,10 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE // For ambiguous files (no type field, .js extension) we return // undefined from `resolve` and re-run the check in `load`. const format = detectModuleFormat(source, url); - if (format === 'module') { + 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. - typelessPackageJsonFilesWarnedAbout ??= new SafeSet(); - if (!typelessPackageJsonFilesWarnedAbout.has(pjsonPath)) { - const warning = `${url} parsed as an ES module because module syntax was detected;` + - ` to avoid the performance penalty of syntax detection, add "type": "module" to ${pjsonPath}`; - process.emitWarning(warning, { - code: 'MODULE_TYPELESS_PACKAGE_JSON', - }); - typelessPackageJsonFilesWarnedAbout.add(pjsonPath); - } + warnTypelessPackageJsonFile(pjsonPath, url); } return format; } diff --git a/test/es-module/test-esm-detect-ambiguous.mjs b/test/es-module/test-esm-detect-ambiguous.mjs index 91c8489d30df09..2d7df8d308082f 100644 --- a/test/es-module/test-esm-detect-ambiguous.mjs +++ b/test/es-module/test-esm-detect-ambiguous.mjs @@ -352,6 +352,18 @@ describe('Module syntax detection', { concurrency: !process.env.TEST_PARALLEL }, }); } + it('does not warn when there are no package.json', async () => { + const { stdout, stderr, code, signal } = await spawnPromisified(process.execPath, [ + fixtures.path('es-modules/loose.js'), + ]); + + strictEqual(stderr, ''); + strictEqual(stdout, 'executed\n'); + strictEqual(code, 0); + strictEqual(signal, null); + }); + + it('warns only once for a package.json that affects multiple files', async () => { const { stdout, stderr, code, signal } = await spawnPromisified(process.execPath, [ fixtures.path('es-modules/package-without-type/detected-as-esm.js'),