diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js index 8459ab5f4721dc..74799f84f1d6f6 100644 --- a/lib/internal/modules/esm/module_job.js +++ b/lib/internal/modules/esm/module_job.js @@ -80,10 +80,11 @@ const isCommonJSGlobalLikeNotDefinedError = (errorMessage) => */ const explainCommonJSGlobalLikeNotDefinedError = (e, url, hasTopLevelAwait) => { if (e?.name === 'ReferenceError' && - isCommonJSGlobalLikeNotDefinedError(e.message)) { + isCommonJSGlobalLikeNotDefinedError(e.message)) { if (hasTopLevelAwait) { - e.message = `Cannot determine intended module format because both require() and top-level await are present. If the code is intended to be CommonJS, wrap await in an async function. If the code is intended to be an ES module, replace require() with import.`; + const globalName = StringPrototypeSplit(e.message, ' ')[0]; + e.message = `Cannot determine intended module format because both ${globalName} and top-level await are present. If the code is intended to be CommonJS, wrap await in an async function. If the code is intended to be an ES module, replace ${globalName} with import.`; e.code = 'ERR_AMBIGUOUS_MODULE_SYNTAX'; return; } @@ -96,9 +97,9 @@ const explainCommonJSGlobalLikeNotDefinedError = (e, url, hasTopLevelAwait) => { const packageConfig = StringPrototypeStartsWith(url, 'file://') && - RegExpPrototypeExec(/\.js(\?[^#]*)?(#.*)?$/, url) !== null && - require('internal/modules/package_json_reader') - .getPackageScopeConfig(url); + RegExpPrototypeExec(/\.js(\?[^#]*)?(#.*)?$/, url) !== null && + require('internal/modules/package_json_reader') + .getPackageScopeConfig(url); if (packageConfig.type === 'module') { e.message += '\nThis file is being treated as an ES module because it has a ' + @@ -191,7 +192,7 @@ class ModuleJob extends ModuleJobBase { * @param {ModuleRequestType} requestType Type of the module request. */ constructor(loader, url, importAttributes = { __proto__: null }, moduleOrModulePromise, - phase = kEvaluationPhase, isMain, inspectBrk, requestType) { + phase = kEvaluationPhase, isMain, inspectBrk, requestType) { super(loader, url, importAttributes, phase, isMain, inspectBrk); // Expose the promise to the ModuleWrap directly for linking below. @@ -308,8 +309,8 @@ class ModuleJob extends ModuleJobBase { // stack trace originates in module_job, not the file itself. A hidden // symbol with filename could be set in node_errors.cc to facilitate this. if (!getSourceMapsSupport().enabled && - StringPrototypeIncludes(e.message, - ' does not provide an export named')) { + StringPrototypeIncludes(e.message, + ' does not provide an export named')) { const splitStack = StringPrototypeSplit(e.stack, '\n', 2); const { 1: childSpecifier, 2: name } = RegExpPrototypeExec( /module '(.*)' does not provide an export named '(.+)'/, @@ -336,9 +337,8 @@ class ModuleJob extends ModuleJobBase { ` '${childSpecifier}' is a CommonJS module, which may not support` + ' all module.exports as named exports.\nCommonJS modules can ' + 'always be imported via the default export, for example using:' + - `\n\nimport pkg from '${childSpecifier}';\n${ - destructuringAssignment ? - `const ${destructuringAssignment} = pkg;\n` : ''}`; + `\n\nimport pkg from '${childSpecifier}';\n${destructuringAssignment ? + `const ${destructuringAssignment} = pkg;\n` : ''}`; const newStack = StringPrototypeSplit(e.stack, '\n'); newStack[3] = `SyntaxError: ${e.message}`; e.stack = ArrayPrototypeJoin(newStack, '\n'); @@ -441,7 +441,7 @@ class ModuleJobSync extends ModuleJobBase { * first line paused in the debugger (because --inspect-brk is passed). */ constructor(loader, url, importAttributes, moduleWrap, phase = kEvaluationPhase, isMain, - inspectBrk, requestType) { + inspectBrk, requestType) { super(loader, url, importAttributes, phase, isMain, inspectBrk); this.module = moduleWrap; @@ -492,7 +492,7 @@ class ModuleJobSync extends ModuleJobBase { } assert.fail('Unexpected status of a module that is imported again after being required. ' + - `Status = ${status}`); + `Status = ${status}`); } runSync(parent) { diff --git a/test/es-module/test-esm-detect-ambiguous.mjs b/test/es-module/test-esm-detect-ambiguous.mjs index 234e27a947a13f..b24d55f4e4357f 100644 --- a/test/es-module/test-esm-detect-ambiguous.mjs +++ b/test/es-module/test-esm-detect-ambiguous.mjs @@ -283,7 +283,7 @@ describe('Module syntax detection', { concurrency: !process.env.TEST_PARALLEL }, assert.match( stderr, - /ReferenceError: Cannot determine intended module format because both require\(\) and top-level await are present\. If the code is intended to be CommonJS, wrap await in an async function\. If the code is intended to be an ES module, replace require\(\) with import\./ + /ReferenceError: Cannot determine intended module format because both require and top-level await are present\. If the code is intended to be CommonJS, wrap await in an async function\. If the code is intended to be an ES module, replace require with import\./ ); assert.strictEqual(stdout, ''); assert.strictEqual(code, 1); @@ -440,7 +440,26 @@ describe('cjs & esm ambiguous syntax case', () => { assert.match( stderr, - /ReferenceError: Cannot determine intended module format because both require\(\) and top-level await are present\. If the code is intended to be CommonJS, wrap await in an async function\. If the code is intended to be an ES module, replace require\(\) with import\./ + /ReferenceError: Cannot determine intended module format because both require and top-level await are present\. If the code is intended to be CommonJS, wrap await in an async function\. If the code is intended to be an ES module, replace require with import\./ + ); + + assert.strictEqual(code, 1); + assert.strictEqual(signal, null); + }); + + it('should throw an ambiguous syntax error when using top-level await with module', async () => { + const { stderr, code, signal } = await spawnPromisified( + process.execPath, + [ + '--input-type=module', + '--eval', + `await 1;\nmodule.exports = 1;`, + ] + ); + + assert.match( + stderr, + /ReferenceError: Cannot determine intended module format because both module and top-level await are present\. If the code is intended to be CommonJS, wrap await in an async function\. If the code is intended to be an ES module, replace module with import\./ ); assert.strictEqual(code, 1);