Skip to content

Commit 9c5c3b3

Browse files
marco-ippolitoaduh95
authored andcommitted
module: add ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX
PR-URL: #56610 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Ethan Arrowood <ethan@arrowood.dev> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
1 parent f97cd5b commit 9c5c3b3

File tree

8 files changed

+93
-38
lines changed

8 files changed

+93
-38
lines changed

doc/api/cli.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -1403,7 +1403,8 @@ Node.js will try to detect the syntax with the following steps:
14031403
1. Run the input as CommonJS.
14041404
2. If step 1 fails, run the input as an ES module.
14051405
3. If step 2 fails with a SyntaxError, strip the types.
1406-
4. If step 3 fails with an error code [`ERR_INVALID_TYPESCRIPT_SYNTAX`][],
1406+
4. If step 3 fails with an error code [`ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`][]
1407+
or [`ERR_INVALID_TYPESCRIPT_SYNTAX`][],
14071408
throw the error from step 2, including the TypeScript error in the message,
14081409
else run as CommonJS.
14091410
5. If step 4 fails, run the input as an ES module.
@@ -3691,6 +3692,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
36913692
[`Buffer`]: buffer.md#class-buffer
36923693
[`CRYPTO_secure_malloc_init`]: https://www.openssl.org/docs/man3.0/man3/CRYPTO_secure_malloc_init.html
36933694
[`ERR_INVALID_TYPESCRIPT_SYNTAX`]: errors.md#err_invalid_typescript_syntax
3695+
[`ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`]: errors.md#err_unsupported_typescript_syntax
36943696
[`NODE_OPTIONS`]: #node_optionsoptions
36953697
[`NO_COLOR`]: https://no-color.org
36963698
[`SlowBuffer`]: buffer.md#class-slowbuffer

doc/api/errors.md

+17-3
Original file line numberDiff line numberDiff line change
@@ -2093,11 +2093,13 @@ does not consist of exactly two elements.
20932093

20942094
<!-- YAML
20952095
added: v23.0.0
2096+
changes:
2097+
- version: REPLACEME
2098+
pr-url: https://github.com/nodejs/node/pull/56610
2099+
description: This error is no longer thrown on valid yet unsupported syntax.
20962100
-->
20972101

2098-
The provided TypeScript syntax is not valid or unsupported.
2099-
This could happen when using TypeScript syntax that requires
2100-
transformation with [type-stripping][].
2102+
The provided TypeScript syntax is not valid.
21012103

21022104
<a id="ERR_INVALID_URI"></a>
21032105

@@ -3096,6 +3098,18 @@ try {
30963098
}
30973099
```
30983100

3101+
<a id="ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX"></a>
3102+
3103+
### `ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`
3104+
3105+
<!-- YAML
3106+
added: REPLACEME
3107+
-->
3108+
3109+
The provided TypeScript syntax is unsupported.
3110+
This could happen when using TypeScript syntax that requires
3111+
transformation with [type-stripping][].
3112+
30993113
<a id="ERR_USE_AFTER_CLOSE"></a>
31003114

31013115
### `ERR_USE_AFTER_CLOSE`

lib/internal/errors.js

+1
Original file line numberDiff line numberDiff line change
@@ -1838,6 +1838,7 @@ E('ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING',
18381838
E('ERR_UNSUPPORTED_RESOLVE_REQUEST',
18391839
'Failed to resolve module specifier "%s" from "%s": Invalid relative URL or base scheme is not hierarchical.',
18401840
TypeError);
1841+
E('ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX', '%s', SyntaxError);
18411842
E('ERR_USE_AFTER_CLOSE', '%s was closed', Error);
18421843

18431844
// This should probably be a `TypeError`.

lib/internal/modules/typescript.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ const { assertTypeScript,
1212
isUnderNodeModules,
1313
kEmptyObject } = require('internal/util');
1414
const {
15+
ERR_INTERNAL_ASSERTION,
1516
ERR_INVALID_TYPESCRIPT_SYNTAX,
1617
ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING,
18+
ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX,
1719
} = require('internal/errors').codes;
1820
const { getOptionValue } = require('internal/options');
1921
const assert = require('internal/assert');
@@ -49,7 +51,20 @@ function parseTypeScript(source, options) {
4951
try {
5052
return parse(source, options);
5153
} catch (error) {
52-
throw new ERR_INVALID_TYPESCRIPT_SYNTAX(error.message);
54+
/**
55+
* Amaro v0.3.0 (from SWC v1.10.7) throws an object with `message` and `code` properties.
56+
* It allows us to distinguish between invalid syntax and unsupported syntax.
57+
*/
58+
switch (error.code) {
59+
case 'UnsupportedSyntax':
60+
throw new ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX(error.message);
61+
case 'InvalidSyntax':
62+
throw new ERR_INVALID_TYPESCRIPT_SYNTAX(error.message);
63+
default:
64+
// SWC will throw strings when something goes wrong.
65+
// Check if has the `message` property or treat it as a string.
66+
throw new ERR_INTERNAL_ASSERTION(error.message ?? error);
67+
}
5368
}
5469
}
5570

lib/internal/process/execution.js

+15-25
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const { getOptionValue } = require('internal/options');
3535
const {
3636
makeContextifyScript, runScriptInThisContext,
3737
} = require('internal/vm');
38-
const { emitExperimentalWarning, isError } = require('internal/util');
38+
const { emitExperimentalWarning } = require('internal/util');
3939
// shouldAbortOnUncaughtToggle is a typed array for faster
4040
// communication with JS.
4141
const { shouldAbortOnUncaughtToggle } = internalBinding('util');
@@ -254,10 +254,6 @@ function evalTypeScript(name, source, breakFirstLine, print, shouldLoadESM = fal
254254
try {
255255
compiledScript = compileScript(name, source, baseUrl);
256256
} catch (originalError) {
257-
// If it's not a SyntaxError, rethrow it.
258-
if (!isError(originalError) || originalError.name !== 'SyntaxError') {
259-
throw originalError;
260-
}
261257
try {
262258
sourceToRun = stripTypeScriptModuleTypes(source, name, false);
263259
// Retry the CJS/ESM syntax detection after stripping the types.
@@ -270,15 +266,14 @@ function evalTypeScript(name, source, breakFirstLine, print, shouldLoadESM = fal
270266
// Emit the experimental warning after the code was successfully evaluated.
271267
emitExperimentalWarning('Type Stripping');
272268
} catch (tsError) {
273-
// If its not an error, or it's not an invalid typescript syntax error, rethrow it.
274-
if (!isError(tsError) || tsError?.code !== 'ERR_INVALID_TYPESCRIPT_SYNTAX') {
275-
throw tsError;
269+
// If it's invalid or unsupported TypeScript syntax, rethrow the original error
270+
// with the TypeScript error message added to the stack.
271+
if (tsError.code === 'ERR_INVALID_TYPESCRIPT_SYNTAX' || tsError.code === 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX') {
272+
originalError.stack = decorateCJSErrorWithTSMessage(originalError.stack, tsError.message);
273+
throw originalError;
276274
}
277275

278-
try {
279-
originalError.stack = decorateCJSErrorWithTSMessage(originalError.stack, tsError.message);
280-
} catch { /* Ignore potential errors coming from `stack` getter/setter */ }
281-
throw originalError;
276+
throw tsError;
282277
}
283278
}
284279

@@ -322,28 +317,23 @@ function evalTypeScriptModuleEntryPoint(source, print) {
322317
// Compile the module to check for syntax errors.
323318
moduleWrap = loader.createModuleWrap(source, url);
324319
} catch (originalError) {
325-
// If it's not a SyntaxError, rethrow it.
326-
if (!isError(originalError) || originalError.name !== 'SyntaxError') {
327-
throw originalError;
328-
}
329-
let strippedSource;
330320
try {
331-
strippedSource = stripTypeScriptModuleTypes(source, url, false);
321+
const strippedSource = stripTypeScriptModuleTypes(source, url, false);
332322
// If the moduleWrap was successfully created, execute the module job.
333323
// outside the try-catch block to avoid catching runtime errors.
334324
moduleWrap = loader.createModuleWrap(strippedSource, url);
335325
// Emit the experimental warning after the code was successfully compiled.
336326
emitExperimentalWarning('Type Stripping');
337327
} catch (tsError) {
338-
// If its not an error, or it's not an invalid typescript syntax error, rethrow it.
339-
if (!isError(tsError) || tsError?.code !== 'ERR_INVALID_TYPESCRIPT_SYNTAX') {
340-
throw tsError;
341-
}
342-
try {
328+
// If it's invalid or unsupported TypeScript syntax, rethrow the original error
329+
// with the TypeScript error message added to the stack.
330+
if (tsError.code === 'ERR_INVALID_TYPESCRIPT_SYNTAX' ||
331+
tsError.code === 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX') {
343332
originalError.stack = `${tsError.message}\n\n${originalError.stack}`;
344-
} catch { /* Ignore potential errors coming from `stack` getter/setter */ }
333+
throw originalError;
334+
}
345335

346-
throw originalError;
336+
throw tsError;
347337
}
348338
}
349339
// If the moduleWrap was successfully created either with by just compiling

test/es-module/test-typescript-eval.mjs

+28-8
Original file line numberDiff line numberDiff line change
@@ -102,33 +102,33 @@ test('expect fail eval TypeScript ESM syntax with input-type commonjs-typescript
102102
strictEqual(result.code, 1);
103103
});
104104

105-
test('check syntax error is thrown when passing invalid syntax', async () => {
105+
test('check syntax error is thrown when passing unsupported syntax', async () => {
106106
const result = await spawnPromisified(process.execPath, [
107107
'--eval',
108108
'enum Foo { A, B, C }']);
109109
strictEqual(result.stdout, '');
110110
match(result.stderr, /SyntaxError/);
111-
doesNotMatch(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
111+
doesNotMatch(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/);
112112
strictEqual(result.code, 1);
113113
});
114114

115-
test('check syntax error is thrown when passing invalid syntax with --input-type=module-typescript', async () => {
115+
test('check syntax error is thrown when passing unsupported syntax with --input-type=module-typescript', async () => {
116116
const result = await spawnPromisified(process.execPath, [
117117
'--input-type=module-typescript',
118118
'--eval',
119119
'enum Foo { A, B, C }']);
120120
strictEqual(result.stdout, '');
121-
match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
121+
match(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/);
122122
strictEqual(result.code, 1);
123123
});
124124

125-
test('check syntax error is thrown when passing invalid syntax with --input-type=commonjs-typescript', async () => {
125+
test('check syntax error is thrown when passing unsupported syntax with --input-type=commonjs-typescript', async () => {
126126
const result = await spawnPromisified(process.execPath, [
127127
'--input-type=commonjs-typescript',
128128
'--eval',
129129
'enum Foo { A, B, C }']);
130130
strictEqual(result.stdout, '');
131-
match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
131+
match(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/);
132132
strictEqual(result.code, 1);
133133
});
134134

@@ -140,7 +140,7 @@ test('should not parse TypeScript with --type-module=commonjs', async () => {
140140

141141
strictEqual(result.stdout, '');
142142
match(result.stderr, /SyntaxError/);
143-
doesNotMatch(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
143+
doesNotMatch(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/);
144144
strictEqual(result.code, 1);
145145
});
146146

@@ -152,7 +152,7 @@ test('should not parse TypeScript with --type-module=module', async () => {
152152

153153
strictEqual(result.stdout, '');
154154
match(result.stderr, /SyntaxError/);
155-
doesNotMatch(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
155+
doesNotMatch(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/);
156156
strictEqual(result.code, 1);
157157
});
158158

@@ -222,3 +222,23 @@ test('typescript CJS code is throwing a syntax error at runtime', async () => {
222222
strictEqual(result.stdout, '');
223223
strictEqual(result.code, 1);
224224
});
225+
226+
test('check syntax error is thrown when passing invalid syntax with --input-type=commonjs-typescript', async () => {
227+
const result = await spawnPromisified(process.execPath, [
228+
'--input-type=commonjs-typescript',
229+
'--eval',
230+
'function foo(){ await Promise.resolve(1); }']);
231+
strictEqual(result.stdout, '');
232+
match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
233+
strictEqual(result.code, 1);
234+
});
235+
236+
test('check syntax error is thrown when passing invalid syntax with --input-type=module-typescript', async () => {
237+
const result = await spawnPromisified(process.execPath, [
238+
'--input-type=module-typescript',
239+
'--eval',
240+
'function foo(){ await Promise.resolve(1); }']);
241+
strictEqual(result.stdout, '');
242+
match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
243+
strictEqual(result.code, 1);
244+
});

test/es-module/test-typescript.mjs

+10
Original file line numberDiff line numberDiff line change
@@ -321,3 +321,13 @@ test('execute a TypeScript loader and a .js file', async () => {
321321
match(result.stdout, /Hello, TypeScript!/);
322322
strictEqual(result.code, 0);
323323
});
324+
325+
test('execute invalid TypeScript syntax', async () => {
326+
const result = await spawnPromisified(process.execPath, [
327+
fixtures.path('typescript/ts/test-invalid-syntax.ts'),
328+
]);
329+
330+
match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
331+
strictEqual(result.stdout, '');
332+
strictEqual(result.code, 1);
333+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
function foo(): string {
2+
await Promise.resolve(1);
3+
}

0 commit comments

Comments
 (0)