diff --git a/graal-nodejs/lib/repl.js b/graal-nodejs/lib/repl.js index 0d4b700838b..c50f4958a17 100644 --- a/graal-nodejs/lib/repl.js +++ b/graal-nodejs/lib/repl.js @@ -62,6 +62,7 @@ const { Boolean, Error, FunctionPrototypeBind, + JSONStringify, MathMaxApply, NumberIsNaN, NumberParseFloat, @@ -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, @@ -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_, @@ -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, diff --git a/graal-nodejs/test/parallel/test-repl.js b/graal-nodejs/test/parallel/test-repl.js index 3eb5d255927..ac499625ae7 100644 --- a/graal-nodejs/test/parallel/test-repl.js +++ b/graal-nodejs/test/parallel/test-repl.js @@ -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");', ] }, ];