Skip to content

Commit

Permalink
repl: display dynamic import variant in static import error messages
Browse files Browse the repository at this point in the history
Enhance the REPL message for static import error message.

```
> import {foo, bar} from 'moo';
import {foo, bar} from 'moo';
^^^^^^

Uncaught:
SyntaxError: .* dynamic import: const {foo,bar} = await import('moo');
```

PR-URL: nodejs#48129
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
  • Loading branch information
hemanth authored and Ceres6 committed Aug 14, 2023
1 parent 4e0167e commit 9710666
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 2 deletions.
28 changes: 27 additions & 1 deletion lib/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
'use strict';

const {
ArrayPrototypeAt,
ArrayPrototypeFilter,
ArrayPrototypeFindIndex,
ArrayPrototypeForEach,
Expand All @@ -62,6 +63,7 @@ const {
Boolean,
Error,
FunctionPrototypeBind,
JSONStringify,
MathMaxApply,
NumberIsNaN,
NumberParseFloat,
Expand Down Expand Up @@ -104,7 +106,9 @@ const {
const {
isIdentifierStart,
isIdentifierChar,
parse: acornParse,
} = require('internal/deps/acorn/acorn/dist/acorn');
const acornWalk = require('internal/deps/acorn/acorn-walk/dist/walk');
const {
decorateErrorStack,
isError,
Expand Down Expand Up @@ -223,6 +227,28 @@ module.paths = CJSModule._nodeModulePaths(module.filename);
const writer = (obj) => inspect(obj, writer.options);
writer.options = { ...inspect.defaultOptions, showProxy: true };

// Converts static import statement to dynamic import statement
const toDynamicImport = (codeLine) => {
let dynamicImportStatement = '';
const ast = acornParse(codeLine, { __proto__: null, sourceType: 'module', ecmaVersion: 'latest' });
acornWalk.ancestor(ast, {
ImportDeclaration(node) {
const awaitDynamicImport = `await import(${JSONStringify(node.source.value)});`;
if (node.specifiers.length === 0) {
dynamicImportStatement += awaitDynamicImport;
} else if (node.specifiers.length === 1 && node.specifiers[0].type === 'ImportNamespaceSpecifier') {
dynamicImportStatement += `const ${node.specifiers[0].local.name} = ${awaitDynamicImport}`;
} else {
const importNames = ArrayPrototypeJoin(ArrayPrototypeMap(node.specifiers, ({ local, imported }) =>
(local.name === imported?.name ? local.name : `${imported?.name ?? 'default'}: ${local.name}`),
), ', ');
dynamicImportStatement += `const { ${importNames} } = ${awaitDynamicImport}`;
}
},
});
return dynamicImportStatement;
};

function REPLServer(prompt,
stream,
eval_,
Expand Down Expand Up @@ -684,7 +710,7 @@ function REPLServer(prompt,
'module';
if (StringPrototypeIncludes(e.message, importErrorStr)) {
e.message = 'Cannot use import statement inside the Node.js ' +
'REPL, alternatively use dynamic import';
'REPL, alternatively use dynamic import: ' + toDynamicImport(ArrayPrototypeAt(self.lines, -1));
e.stack = SideEffectFreeRegExpPrototypeSymbolReplace(
/SyntaxError:.*\n/,
e.stack,
Expand Down
69 changes: 68 additions & 1 deletion test/parallel/test-repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,74 @@ const tcpTests = [
kArrow,
'',
'Uncaught:',
/^SyntaxError: .* dynamic import/,
'SyntaxError: Cannot use import statement inside the Node.js REPL, \
alternatively use dynamic import: const { default: comeOn } = await import("fhqwhgads");',
]
},
{
send: 'import { export1, export2 } from "module-name"',
expect: [
kSource,
kArrow,
'',
'Uncaught:',
'SyntaxError: Cannot use import statement inside the Node.js REPL, \
alternatively use dynamic import: const { export1, export2 } = await import("module-name");',
]
},
{
send: 'import * as name from "module-name";',
expect: [
kSource,
kArrow,
'',
'Uncaught:',
'SyntaxError: Cannot use import statement inside the Node.js REPL, \
alternatively use dynamic import: const name = await import("module-name");',
]
},
{
send: 'import "module-name";',
expect: [
kSource,
kArrow,
'',
'Uncaught:',
'SyntaxError: Cannot use import statement inside the Node.js REPL, \
alternatively use dynamic import: await import("module-name");',
]
},
{
send: 'import { export1 as localName1, export2 } from "bar";',
expect: [
kSource,
kArrow,
'',
'Uncaught:',
'SyntaxError: Cannot use import statement inside the Node.js REPL, \
alternatively use dynamic import: const { export1: localName1, export2 } = await import("bar");',
]
},
{
send: 'import alias from "bar";',
expect: [
kSource,
kArrow,
'',
'Uncaught:',
'SyntaxError: Cannot use import statement inside the Node.js REPL, \
alternatively use dynamic import: const { default: alias } = await import("bar");',
]
},
{
send: 'import alias, {namedExport} from "bar";',
expect: [
kSource,
kArrow,
'',
'Uncaught:',
'SyntaxError: Cannot use import statement inside the Node.js REPL, \
alternatively use dynamic import: const { default: alias, namedExport } = await import("bar");',
]
},
];
Expand Down

0 comments on commit 9710666

Please sign in to comment.