Skip to content

Commit 90b632e

Browse files
GeoffreyBoothtargos
authored andcommitted
module: warn on detection in typeless package
PR-URL: #52168 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent d53e536 commit 90b632e

3 files changed

Lines changed: 66 additions & 3 deletions

File tree

‎lib/internal/modules/esm/get_format.js‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const {
55
ObjectPrototypeHasOwnProperty,
66
PromisePrototypeThen,
77
PromiseResolve,
8+
SafeSet,
89
StringPrototypeIncludes,
910
StringPrototypeCharCodeAt,
1011
StringPrototypeSlice,
@@ -19,7 +20,7 @@ const {
1920
constexperimentalNetworkImports=
2021
getOptionValue('--experimental-network-imports');
2122
const{ containsModuleSyntax }=internalBinding('contextify');
22-
const{ getPackageType }=require('internal/modules/esm/resolve');
23+
const{getPackageScopeConfig,getPackageType }=require('internal/modules/esm/resolve');
2324
const{ fileURLToPath }=require('internal/url');
2425
const{ERR_UNKNOWN_FILE_EXTENSION}=require('internal/errors').codes;
2526

@@ -81,6 +82,7 @@ function underNodeModules(url) {
8182
returnStringPrototypeIncludes(url.pathname,'/node_modules/');
8283
}
8384

85+
lettypelessPackageJsonFilesWarnedAbout;
8486
/**
8587
* @param {URL} url
8688
* @param {{parentURL: string; source?: Buffer}} context
@@ -92,7 +94,7 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE
9294
constext=extname(url);
9395

9496
if(ext==='.js'){
95-
constpackageType=getPackageType(url);
97+
const{type: packageType, pjsonPath }=getPackageScopeConfig(url);
9698
if(packageType!=='none'){
9799
returnpackageType;
98100
}
@@ -111,9 +113,23 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE
111113
// `source` is undefined when this is called from `defaultResolve`;
112114
// but this gets called again from `defaultLoad`/`defaultLoadSync`.
113115
if(getOptionValue('--experimental-detect-module')){
114-
returnsource ?
116+
constformat=source ?
115117
(containsModuleSyntax(`${source}`,fileURLToPath(url)) ? 'module' : 'commonjs') :
116118
null;
119+
if(format==='module'){
120+
// This module has a .js extension, a package.json with no `type` field, and ESM syntax.
121+
// Warn about the missing `type` field so that the user can avoid the performance penalty of detection.
122+
typelessPackageJsonFilesWarnedAbout??=newSafeSet();
123+
if(!typelessPackageJsonFilesWarnedAbout.has(pjsonPath)){
124+
constwarning=`${url} parsed as an ES module because module syntax was detected;`+
125+
` to avoid the performance penalty of syntax detection, add "type": "module" to ${pjsonPath}`;
126+
process.emitWarning(warning,{
127+
code: 'MODULE_TYPELESS_PACKAGE_JSON',
128+
});
129+
typelessPackageJsonFilesWarnedAbout.add(pjsonPath);
130+
}
131+
}
132+
returnformat;
117133
}
118134
return'commonjs';
119135
}

‎test/es-module/test-esm-detect-ambiguous.mjs‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ describe('--experimental-detect-module', { concurrency: true }, () => {
101101
]){
102102
it(testName,async()=>{
103103
const{ stdout, stderr, code, signal }=awaitspawnPromisified(process.execPath,[
104+
'--no-warnings',
104105
'--experimental-detect-module',
105106
entryPath,
106107
]);
@@ -142,6 +143,7 @@ describe('--experimental-detect-module', { concurrency: true }, () => {
142143
]){
143144
it(testName,async()=>{
144145
const{ stdout, stderr, code, signal }=awaitspawnPromisified(process.execPath,[
146+
'--no-warnings',
145147
'--experimental-detect-module',
146148
entryPath,
147149
]);
@@ -291,6 +293,7 @@ describe('--experimental-detect-module', { concurrency: true }, () => {
291293

292294
it('permits declaration of CommonJS module variables',async()=>{
293295
const{ stdout, stderr, code, signal }=awaitspawnPromisified(process.execPath,[
296+
'--no-warnings',
294297
'--experimental-detect-module',
295298
fixtures.path('es-modules/package-without-type/commonjs-wrapper-variables.js'),
296299
]);
@@ -327,6 +330,48 @@ describe('--experimental-detect-module', { concurrency: true }, () => {
327330
strictEqual(signal,null);
328331
});
329332
});
333+
334+
describe('warn about typeless packages for .js files with ESM syntax',{concurrency: true},()=>{
335+
for(const{ testName, entryPath }of[
336+
{
337+
testName: 'warns for ESM syntax in a .js entry point in a typeless package',
338+
entryPath: fixtures.path('es-modules/package-without-type/module.js'),
339+
},
340+
{
341+
testName: 'warns for ESM syntax in a .js file imported by a CommonJS entry point in a typeless package',
342+
entryPath: fixtures.path('es-modules/package-without-type/imports-esm.js'),
343+
},
344+
{
345+
testName: 'warns for ESM syntax in a .js file imported by an ESM entry point in a typeless package',
346+
entryPath: fixtures.path('es-modules/package-without-type/imports-esm.mjs'),
347+
},
348+
]){
349+
it(testName,async()=>{
350+
const{ stdout, stderr, code, signal }=awaitspawnPromisified(process.execPath,[
351+
'--experimental-detect-module',
352+
entryPath,
353+
]);
354+
355+
match(stderr,/MODULE_TYPELESS_PACKAGE_JSON/);
356+
strictEqual(stdout,'executed\n');
357+
strictEqual(code,0);
358+
strictEqual(signal,null);
359+
});
360+
}
361+
362+
it('warns only once for a package.json that affects multiple files',async()=>{
363+
const{ stdout, stderr, code, signal }=awaitspawnPromisified(process.execPath,[
364+
'--experimental-detect-module',
365+
fixtures.path('es-modules/package-without-type/detected-as-esm.js'),
366+
]);
367+
368+
match(stderr,/MODULE_TYPELESS_PACKAGE_JSON/);
369+
strictEqual(stderr.match(/MODULE_TYPELESS_PACKAGE_JSON/g).length,1);
370+
strictEqual(stdout,'executed\nexecuted\n');
371+
strictEqual(code,0);
372+
strictEqual(signal,null);
373+
});
374+
});
330375
});
331376

332377
// Validate temporarily disabling `--abort-on-uncaught-exception`
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import'./module.js';
2+
console.log('executed');

0 commit comments

Comments
 (0)