From 5117de6eb628184d8d237645be7a8434f4a40d25 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Thu, 10 Oct 2024 14:12:39 +0500 Subject: [PATCH] module: wrap swc error in ERR_INVALID_TYPESCRIPT_SYNTAX PR-URL: https://github.com/nodejs/node/pull/55316 Reviewed-By: Paolo Insogna Reviewed-By: Jacob Smith Reviewed-By: Matteo Collina Reviewed-By: Antoine du Hamel --- doc/api/errors.md | 13 ++++++ lib/internal/errors.js | 1 + lib/internal/modules/helpers.js | 53 +++++++++++-------------- test/es-module/test-typescript-eval.mjs | 10 +++++ 4 files changed, 47 insertions(+), 30 deletions(-) diff --git a/doc/api/errors.md b/doc/api/errors.md index 0298c2b1e22163..127872221c0f72 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -2094,6 +2094,18 @@ An element in the `iterable` provided to the [WHATWG][WHATWG URL API] represent a `[name, value]` tuple – that is, if an element is not iterable, or does not consist of exactly two elements. + + +### `ERR_INVALID_TYPESCRIPT_SYNTAX` + + + +The provided TypeScript syntax is not valid or unsupported. +This could happen when using TypeScript syntax that requires +transformation with [type-stripping][]. + ### `ERR_INVALID_URI` @@ -4203,4 +4215,5 @@ An error occurred trying to allocate memory. This should never happen. [stream-based]: stream.md [syscall]: https://man7.org/linux/man-pages/man2/syscalls.2.html [try-catch]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch +[type-stripping]: typescript.md#type-stripping [vm]: vm.md diff --git a/lib/internal/errors.js b/lib/internal/errors.js index e1228176a2d101..29c0b607746023 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1524,6 +1524,7 @@ E('ERR_INVALID_SYNC_FORK_INPUT', TypeError); E('ERR_INVALID_THIS', 'Value of "this" must be of type %s', TypeError); E('ERR_INVALID_TUPLE', '%s must be an iterable %s tuple', TypeError); +E('ERR_INVALID_TYPESCRIPT_SYNTAX', '%s', SyntaxError); E('ERR_INVALID_URI', 'URI malformed', URIError); E('ERR_INVALID_URL', function(input, base = null) { this.input = input; diff --git a/lib/internal/modules/helpers.js b/lib/internal/modules/helpers.js index feec9f4957e7ab..6c13ed3ff5fdd0 100644 --- a/lib/internal/modules/helpers.js +++ b/lib/internal/modules/helpers.js @@ -15,6 +15,7 @@ const { const { ERR_INVALID_ARG_TYPE, ERR_INVALID_RETURN_PROPERTY_VALUE, + ERR_INVALID_TYPESCRIPT_SYNTAX, } = require('internal/errors').codes; const { BuiltinModule } = require('internal/bootstrap/realm'); @@ -312,44 +313,37 @@ function getBuiltinModule(id) { return normalizedId ? require(normalizedId) : undefined; } -/** - * TypeScript parsing function, by default Amaro.transformSync. - * @type {Function} - */ -let typeScriptParser; /** * The TypeScript parsing mode, either 'strip-only' or 'transform'. * @type {string} */ -let typeScriptParsingMode; -/** - * Whether source maps are enabled for TypeScript parsing. - * @type {boolean} - */ -let sourceMapEnabled; +const getTypeScriptParsingMode = getLazy(() => + (getOptionValue('--experimental-transform-types') ? 'transform' : 'strip-only'), +); /** * Load the TypeScript parser. - * @param {Function} parser - A function that takes a string of TypeScript code * and returns an object with a `code` property. * @returns {Function} The TypeScript parser function. */ -function loadTypeScriptParser(parser) { - if (typeScriptParser) { - return typeScriptParser; - } +const loadTypeScriptParser = getLazy(() => { + const amaro = require('internal/deps/amaro/dist/index'); + return amaro.transformSync; +}); - if (parser) { - typeScriptParser = parser; - } else { - const amaro = require('internal/deps/amaro/dist/index'); - // Default option for Amaro is to perform Type Stripping only. - typeScriptParsingMode = getOptionValue('--experimental-transform-types') ? 'transform' : 'strip-only'; - sourceMapEnabled = getOptionValue('--enable-source-maps'); - // Curry the transformSync function with the default options. - typeScriptParser = amaro.transformSync; +/** + * + * @param {string} source the source code + * @param {object} options the options to pass to the parser + * @returns {TransformOutput} an object with a `code` property. + */ +function parseTypeScript(source, options) { + const parse = loadTypeScriptParser(); + try { + return parse(source, options); + } catch (error) { + throw new ERR_INVALID_TYPESCRIPT_SYNTAX(error); } - return typeScriptParser; } /** @@ -364,14 +358,13 @@ function loadTypeScriptParser(parser) { */ function stripTypeScriptTypes(source, filename) { assert(typeof source === 'string'); - const parse = loadTypeScriptParser(); const options = { __proto__: null, - mode: typeScriptParsingMode, - sourceMap: sourceMapEnabled, + mode: getTypeScriptParsingMode(), + sourceMap: getOptionValue('--enable-source-maps'), filename, }; - const { code, map } = parse(source, options); + const { code, map } = parseTypeScript(source, options); if (map) { // TODO(@marco-ippolito) When Buffer.transcode supports utf8 to // base64 transformation, we should change this line. diff --git a/test/es-module/test-typescript-eval.mjs b/test/es-module/test-typescript-eval.mjs index be467721577826..e6d841ffa07f7e 100644 --- a/test/es-module/test-typescript-eval.mjs +++ b/test/es-module/test-typescript-eval.mjs @@ -110,3 +110,13 @@ test('expect fail eval TypeScript ESM syntax with input-type commonjs', async () match(result.stderr, /Cannot use import statement outside a module/); strictEqual(result.code, 1); }); + +test('check syntax error is thrown when passing invalid syntax', async () => { + const result = await spawnPromisified(process.execPath, [ + '--experimental-strip-types', + '--eval', + 'enum Foo { A, B, C }']); + strictEqual(result.stdout, ''); + match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/); + strictEqual(result.code, 1); +});