Skip to content

Commit a24f67a

Browse files
committed
repl: display dynamic import variant in static import error
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 <[email protected]>
1 parent 3734b95 commit a24f67a

File tree

2 files changed

+94
-2
lines changed

2 files changed

+94
-2
lines changed

graal-nodejs/lib/repl.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const {
6262
Boolean,
6363
Error,
6464
FunctionPrototypeBind,
65+
JSONStringify,
6566
MathMaxApply,
6667
NumberIsNaN,
6768
NumberParseFloat,
@@ -105,7 +106,9 @@ const {
105106
const {
106107
isIdentifierStart,
107108
isIdentifierChar,
109+
parse: acornParse,
108110
} = require('internal/deps/acorn/acorn/dist/acorn');
111+
const acornWalk = require('internal/deps/acorn/acorn-walk/dist/walk');
109112
const {
110113
decorateErrorStack,
111114
isError,
@@ -225,6 +228,28 @@ module.paths = CJSModule._nodeModulePaths(module.filename);
225228
const writer = (obj) => inspect(obj, writer.options);
226229
writer.options = { ...inspect.defaultOptions, showProxy: true };
227230

231+
// Converts static import statement to dynamic import statement
232+
const toDynamicImport = (codeLine) => {
233+
let dynamicImportStatement = '';
234+
const ast = acornParse(codeLine, { __proto__: null, sourceType: 'module', ecmaVersion: 'latest' });
235+
acornWalk.ancestor(ast, {
236+
ImportDeclaration(node) {
237+
const awaitDynamicImport = `await import(${JSONStringify(node.source.value)});`;
238+
if (node.specifiers.length === 0) {
239+
dynamicImportStatement += awaitDynamicImport;
240+
} else if (node.specifiers.length === 1 && node.specifiers[0].type === 'ImportNamespaceSpecifier') {
241+
dynamicImportStatement += `const ${node.specifiers[0].local.name} = ${awaitDynamicImport}`;
242+
} else {
243+
const importNames = ArrayPrototypeJoin(ArrayPrototypeMap(node.specifiers, ({ local, imported }) =>
244+
(local.name === imported?.name ? local.name : `${imported?.name ?? 'default'}: ${local.name}`),
245+
), ', ');
246+
dynamicImportStatement += `const { ${importNames} } = ${awaitDynamicImport}`;
247+
}
248+
},
249+
});
250+
return dynamicImportStatement;
251+
};
252+
228253
function REPLServer(prompt,
229254
stream,
230255
eval_,
@@ -690,7 +715,7 @@ function REPLServer(prompt,
690715
'module';
691716
if (StringPrototypeIncludes(e.message, importErrorStr)) {
692717
e.message = 'Cannot use import statement inside the Node.js ' +
693-
'REPL, alternatively use dynamic import';
718+
'REPL, alternatively use dynamic import: ' + toDynamicImport(self.lines.at(-1));
694719
e.stack = SideEffectFreeRegExpPrototypeSymbolReplace(
695720
/SyntaxError:.*\n/,
696721
e.stack,

graal-nodejs/test/parallel/test-repl.js

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,74 @@ const tcpTests = [
817817
kArrow,
818818
'',
819819
'Uncaught:',
820-
/^SyntaxError: .* dynamic import/,
820+
'SyntaxError: Cannot use import statement inside the Node.js REPL, \
821+
alternatively use dynamic import: const { default: comeOn } = await import("fhqwhgads");',
822+
]
823+
},
824+
{
825+
send: 'import { export1, export2 } from "module-name"',
826+
expect: [
827+
kSource,
828+
kArrow,
829+
'',
830+
'Uncaught:',
831+
'SyntaxError: Cannot use import statement inside the Node.js REPL, \
832+
alternatively use dynamic import: const { export1, export2 } = await import("module-name");',
833+
]
834+
},
835+
{
836+
send: 'import * as name from "module-name";',
837+
expect: [
838+
kSource,
839+
kArrow,
840+
'',
841+
'Uncaught:',
842+
'SyntaxError: Cannot use import statement inside the Node.js REPL, \
843+
alternatively use dynamic import: const name = await import("module-name");',
844+
]
845+
},
846+
{
847+
send: 'import "module-name";',
848+
expect: [
849+
kSource,
850+
kArrow,
851+
'',
852+
'Uncaught:',
853+
'SyntaxError: Cannot use import statement inside the Node.js REPL, \
854+
alternatively use dynamic import: await import("module-name");',
855+
]
856+
},
857+
{
858+
send: 'import { export1 as localName1, export2 } from "bar";',
859+
expect: [
860+
kSource,
861+
kArrow,
862+
'',
863+
'Uncaught:',
864+
'SyntaxError: Cannot use import statement inside the Node.js REPL, \
865+
alternatively use dynamic import: const { export1: localName1, export2 } = await import("bar");',
866+
]
867+
},
868+
{
869+
send: 'import alias from "bar";',
870+
expect: [
871+
kSource,
872+
kArrow,
873+
'',
874+
'Uncaught:',
875+
'SyntaxError: Cannot use import statement inside the Node.js REPL, \
876+
alternatively use dynamic import: const { default: alias } = await import("bar");',
877+
]
878+
},
879+
{
880+
send: 'import alias, {namedExport} from "bar";',
881+
expect: [
882+
kSource,
883+
kArrow,
884+
'',
885+
'Uncaught:',
886+
'SyntaxError: Cannot use import statement inside the Node.js REPL, \
887+
alternatively use dynamic import: const { default: alias, namedExport } = await import("bar");',
821888
]
822889
},
823890
];

0 commit comments

Comments
 (0)