Skip to content

Commit

Permalink
repl: display dynamic import variant in static import error
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/node#48129
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
  • Loading branch information
sercher committed Apr 25, 2024
1 parent 7cde3de commit ba47e0e
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 2 deletions.
27 changes: 26 additions & 1 deletion graal-nodejs/lib/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const {
Boolean,
Error,
FunctionPrototypeBind,
JSONStringify,
MathMaxApply,
NumberIsNaN,
NumberParseFloat,
Expand Down Expand Up @@ -105,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 @@ -225,6 +228,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 @@ -690,7 +715,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(self.lines.at(-1));
e.stack = SideEffectFreeRegExpPrototypeSymbolReplace(
/SyntaxError:.*\n/,
e.stack,
Expand Down
69 changes: 68 additions & 1 deletion graal-nodejs/test/parallel/test-repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -817,7 +817,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 ba47e0e

Please sign in to comment.