diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js index 3f11ffc768eedb..ed681541d12723 100644 --- a/lib/internal/modules/esm/module_job.js +++ b/lib/internal/modules/esm/module_job.js @@ -107,16 +107,23 @@ class ModuleJob { await this.loader.resolve(childSpecifier, parentFileUrl); const format = await this.loader.getFormat(childFileURL); if (format === 'commonjs') { - const importStatement = splitStack[1]; - const namedImports = StringPrototypeMatch(importStatement, /{.*}/)[0]; - const destructuringAssignment = StringPrototypeReplace(namedImports, /\s+as\s+/g, ': '); e.message = `The requested module '${childSpecifier}' is expected ` + 'to be of type CommonJS, which does not support named exports. ' + 'CommonJS modules can be imported by importing the default ' + - 'export.\n' + - 'For example:\n' + - `import pkg from '${childSpecifier}';\n` + - `const ${destructuringAssignment} = pkg;`; + 'export.'; + // TODO(@ctavan): The original error stack only provides the single + // line which causes the error. For multi-line import statements we + // cannot generate an equivalent object descructuring assignment by + // just parsing the error stack. + const importStatement = splitStack[1]; + const oneLineNamedImports = StringPrototypeMatch(importStatement, /{.*}/); + if (oneLineNamedImports) { + const destructuringAssignment = + StringPrototypeReplace(oneLineNamedImports[0], /\s+as\s+/g, ': '); + e.message += '\nFor example:\n' + + `import pkg from '${childSpecifier}';\n` + + `const ${destructuringAssignment} = pkg;`; + } const newStack = StringPrototypeSplit(e.stack, '\n'); newStack[3] = `SyntaxError: ${e.message}`; e.stack = ArrayPrototypeJoin(newStack, '\n'); diff --git a/test/es-module/test-esm-cjs-named-error.mjs b/test/es-module/test-esm-cjs-named-error.mjs index d71dc959e21fb7..e9ddc67c0fbcea 100644 --- a/test/es-module/test-esm-cjs-named-error.mjs +++ b/test/es-module/test-esm-cjs-named-error.mjs @@ -10,6 +10,10 @@ const expectedRelative = 'The requested module \'./fail.cjs\' is expected to ' + 'import pkg from \'./fail.cjs\';\n' + 'const { comeOn } = pkg;'; +const expectedWithoutExample = 'The requested module \'./fail.cjs\' is ' + + 'expected to be of type CommonJS, which does not support named exports. ' + + 'CommonJS modules can be imported by importing the default export.'; + const expectedRenamed = 'The requested module \'./fail.cjs\' is expected to ' + 'be of type CommonJS, which does not support named exports. CommonJS ' + 'modules can be imported by importing the default export.\n' + @@ -52,6 +56,13 @@ rejects(async () => { message: expectedRenamed }, 'should correctly format named imports with renames'); +rejects(async () => { + await import(`${fixtureBase}/multi-line.mjs`); +}, { + name: 'SyntaxError', + message: expectedWithoutExample, +}, 'should correctly format named imports across multiple lines'); + rejects(async () => { await import(`${fixtureBase}/json-hack.mjs`); }, { diff --git a/test/fixtures/es-modules/package-cjs-named-error/fail.cjs b/test/fixtures/es-modules/package-cjs-named-error/fail.cjs index 40c512ab0e5ad2..cab82d3eb60d60 100644 --- a/test/fixtures/es-modules/package-cjs-named-error/fail.cjs +++ b/test/fixtures/es-modules/package-cjs-named-error/fail.cjs @@ -1,3 +1,4 @@ module.exports = { - comeOn: 'fhqwhgads' + comeOn: 'fhqwhgads', + everybody: 'to the limit', }; diff --git a/test/fixtures/es-modules/package-cjs-named-error/multi-line.mjs b/test/fixtures/es-modules/package-cjs-named-error/multi-line.mjs new file mode 100644 index 00000000000000..a4f80eba042576 --- /dev/null +++ b/test/fixtures/es-modules/package-cjs-named-error/multi-line.mjs @@ -0,0 +1,4 @@ +import { + comeOn, + everybody, +} from './fail.cjs';