mirror of https://github.com/nodejs/node.git
module: add ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX
PR-URL: https://github.com/nodejs/node/pull/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>
This commit is contained in:
parent
22f1518d2f
commit
cee63dcf35
|
@ -1390,7 +1390,8 @@ Node.js will try to detect the syntax with the following steps:
|
||||||
1. Run the input as CommonJS.
|
1. Run the input as CommonJS.
|
||||||
2. If step 1 fails, run the input as an ES module.
|
2. If step 1 fails, run the input as an ES module.
|
||||||
3. If step 2 fails with a SyntaxError, strip the types.
|
3. If step 2 fails with a SyntaxError, strip the types.
|
||||||
4. If step 3 fails with an error code [`ERR_INVALID_TYPESCRIPT_SYNTAX`][],
|
4. If step 3 fails with an error code [`ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`][]
|
||||||
|
or [`ERR_INVALID_TYPESCRIPT_SYNTAX`][],
|
||||||
throw the error from step 2, including the TypeScript error in the message,
|
throw the error from step 2, including the TypeScript error in the message,
|
||||||
else run as CommonJS.
|
else run as CommonJS.
|
||||||
5. If step 4 fails, run the input as an ES module.
|
5. If step 4 fails, run the input as an ES module.
|
||||||
|
@ -3708,6 +3709,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
|
||||||
[`Buffer`]: buffer.md#class-buffer
|
[`Buffer`]: buffer.md#class-buffer
|
||||||
[`CRYPTO_secure_malloc_init`]: https://www.openssl.org/docs/man3.0/man3/CRYPTO_secure_malloc_init.html
|
[`CRYPTO_secure_malloc_init`]: https://www.openssl.org/docs/man3.0/man3/CRYPTO_secure_malloc_init.html
|
||||||
[`ERR_INVALID_TYPESCRIPT_SYNTAX`]: errors.md#err_invalid_typescript_syntax
|
[`ERR_INVALID_TYPESCRIPT_SYNTAX`]: errors.md#err_invalid_typescript_syntax
|
||||||
|
[`ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`]: errors.md#err_unsupported_typescript_syntax
|
||||||
[`NODE_OPTIONS`]: #node_optionsoptions
|
[`NODE_OPTIONS`]: #node_optionsoptions
|
||||||
[`NO_COLOR`]: https://no-color.org
|
[`NO_COLOR`]: https://no-color.org
|
||||||
[`SlowBuffer`]: buffer.md#class-slowbuffer
|
[`SlowBuffer`]: buffer.md#class-slowbuffer
|
||||||
|
|
|
@ -2095,11 +2095,13 @@ does not consist of exactly two elements.
|
||||||
added:
|
added:
|
||||||
- v23.0.0
|
- v23.0.0
|
||||||
- v22.10.0
|
- v22.10.0
|
||||||
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/56610
|
||||||
|
description: This error is no longer thrown on valid yet unsupported syntax.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The provided TypeScript syntax is not valid or unsupported.
|
The provided TypeScript syntax is not valid.
|
||||||
This could happen when using TypeScript syntax that requires
|
|
||||||
transformation with [type-stripping][].
|
|
||||||
|
|
||||||
<a id="ERR_INVALID_URI"></a>
|
<a id="ERR_INVALID_URI"></a>
|
||||||
|
|
||||||
|
@ -3116,6 +3118,18 @@ try {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<a id="ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX"></a>
|
||||||
|
|
||||||
|
### `ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
The provided TypeScript syntax is unsupported.
|
||||||
|
This could happen when using TypeScript syntax that requires
|
||||||
|
transformation with [type-stripping][].
|
||||||
|
|
||||||
<a id="ERR_USE_AFTER_CLOSE"></a>
|
<a id="ERR_USE_AFTER_CLOSE"></a>
|
||||||
|
|
||||||
### `ERR_USE_AFTER_CLOSE`
|
### `ERR_USE_AFTER_CLOSE`
|
||||||
|
|
|
@ -1838,6 +1838,7 @@ E('ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING',
|
||||||
E('ERR_UNSUPPORTED_RESOLVE_REQUEST',
|
E('ERR_UNSUPPORTED_RESOLVE_REQUEST',
|
||||||
'Failed to resolve module specifier "%s" from "%s": Invalid relative URL or base scheme is not hierarchical.',
|
'Failed to resolve module specifier "%s" from "%s": Invalid relative URL or base scheme is not hierarchical.',
|
||||||
TypeError);
|
TypeError);
|
||||||
|
E('ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX', '%s', SyntaxError);
|
||||||
E('ERR_USE_AFTER_CLOSE', '%s was closed', Error);
|
E('ERR_USE_AFTER_CLOSE', '%s was closed', Error);
|
||||||
|
|
||||||
// This should probably be a `TypeError`.
|
// This should probably be a `TypeError`.
|
||||||
|
|
|
@ -12,8 +12,10 @@ const { assertTypeScript,
|
||||||
isUnderNodeModules,
|
isUnderNodeModules,
|
||||||
kEmptyObject } = require('internal/util');
|
kEmptyObject } = require('internal/util');
|
||||||
const {
|
const {
|
||||||
|
ERR_INTERNAL_ASSERTION,
|
||||||
ERR_INVALID_TYPESCRIPT_SYNTAX,
|
ERR_INVALID_TYPESCRIPT_SYNTAX,
|
||||||
ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING,
|
ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING,
|
||||||
|
ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX,
|
||||||
} = require('internal/errors').codes;
|
} = require('internal/errors').codes;
|
||||||
const { getOptionValue } = require('internal/options');
|
const { getOptionValue } = require('internal/options');
|
||||||
const assert = require('internal/assert');
|
const assert = require('internal/assert');
|
||||||
|
@ -49,7 +51,20 @@ function parseTypeScript(source, options) {
|
||||||
try {
|
try {
|
||||||
return parse(source, options);
|
return parse(source, options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
/**
|
||||||
|
* Amaro v0.3.0 (from SWC v1.10.7) throws an object with `message` and `code` properties.
|
||||||
|
* It allows us to distinguish between invalid syntax and unsupported syntax.
|
||||||
|
*/
|
||||||
|
switch (error.code) {
|
||||||
|
case 'UnsupportedSyntax':
|
||||||
|
throw new ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX(error.message);
|
||||||
|
case 'InvalidSyntax':
|
||||||
throw new ERR_INVALID_TYPESCRIPT_SYNTAX(error.message);
|
throw new ERR_INVALID_TYPESCRIPT_SYNTAX(error.message);
|
||||||
|
default:
|
||||||
|
// SWC will throw strings when something goes wrong.
|
||||||
|
// Check if has the `message` property or treat it as a string.
|
||||||
|
throw new ERR_INTERNAL_ASSERTION(error.message ?? error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ const { getOptionValue } = require('internal/options');
|
||||||
const {
|
const {
|
||||||
makeContextifyScript, runScriptInThisContext,
|
makeContextifyScript, runScriptInThisContext,
|
||||||
} = require('internal/vm');
|
} = require('internal/vm');
|
||||||
const { emitExperimentalWarning, isError } = require('internal/util');
|
const { emitExperimentalWarning } = require('internal/util');
|
||||||
// shouldAbortOnUncaughtToggle is a typed array for faster
|
// shouldAbortOnUncaughtToggle is a typed array for faster
|
||||||
// communication with JS.
|
// communication with JS.
|
||||||
const { shouldAbortOnUncaughtToggle } = internalBinding('util');
|
const { shouldAbortOnUncaughtToggle } = internalBinding('util');
|
||||||
|
@ -254,10 +254,6 @@ function evalTypeScript(name, source, breakFirstLine, print, shouldLoadESM = fal
|
||||||
try {
|
try {
|
||||||
compiledScript = compileScript(name, source, baseUrl);
|
compiledScript = compileScript(name, source, baseUrl);
|
||||||
} catch (originalError) {
|
} catch (originalError) {
|
||||||
// If it's not a SyntaxError, rethrow it.
|
|
||||||
if (!isError(originalError) || originalError.name !== 'SyntaxError') {
|
|
||||||
throw originalError;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
sourceToRun = stripTypeScriptModuleTypes(source, name, false);
|
sourceToRun = stripTypeScriptModuleTypes(source, name, false);
|
||||||
// Retry the CJS/ESM syntax detection after stripping the types.
|
// Retry the CJS/ESM syntax detection after stripping the types.
|
||||||
|
@ -270,15 +266,14 @@ function evalTypeScript(name, source, breakFirstLine, print, shouldLoadESM = fal
|
||||||
// Emit the experimental warning after the code was successfully evaluated.
|
// Emit the experimental warning after the code was successfully evaluated.
|
||||||
emitExperimentalWarning('Type Stripping');
|
emitExperimentalWarning('Type Stripping');
|
||||||
} catch (tsError) {
|
} catch (tsError) {
|
||||||
// If its not an error, or it's not an invalid typescript syntax error, rethrow it.
|
// If it's invalid or unsupported TypeScript syntax, rethrow the original error
|
||||||
if (!isError(tsError) || tsError?.code !== 'ERR_INVALID_TYPESCRIPT_SYNTAX') {
|
// with the TypeScript error message added to the stack.
|
||||||
throw tsError;
|
if (tsError.code === 'ERR_INVALID_TYPESCRIPT_SYNTAX' || tsError.code === 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX') {
|
||||||
|
originalError.stack = decorateCJSErrorWithTSMessage(originalError.stack, tsError.message);
|
||||||
|
throw originalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
throw tsError;
|
||||||
originalError.stack = decorateCJSErrorWithTSMessage(originalError.stack, tsError.message);
|
|
||||||
} catch { /* Ignore potential errors coming from `stack` getter/setter */ }
|
|
||||||
throw originalError;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,29 +317,24 @@ function evalTypeScriptModuleEntryPoint(source, print) {
|
||||||
// Compile the module to check for syntax errors.
|
// Compile the module to check for syntax errors.
|
||||||
moduleWrap = loader.createModuleWrap(source, url);
|
moduleWrap = loader.createModuleWrap(source, url);
|
||||||
} catch (originalError) {
|
} catch (originalError) {
|
||||||
// If it's not a SyntaxError, rethrow it.
|
|
||||||
if (!isError(originalError) || originalError.name !== 'SyntaxError') {
|
|
||||||
throw originalError;
|
|
||||||
}
|
|
||||||
let strippedSource;
|
|
||||||
try {
|
try {
|
||||||
strippedSource = stripTypeScriptModuleTypes(source, url, false);
|
const strippedSource = stripTypeScriptModuleTypes(source, url, false);
|
||||||
// If the moduleWrap was successfully created, execute the module job.
|
// If the moduleWrap was successfully created, execute the module job.
|
||||||
// outside the try-catch block to avoid catching runtime errors.
|
// outside the try-catch block to avoid catching runtime errors.
|
||||||
moduleWrap = loader.createModuleWrap(strippedSource, url);
|
moduleWrap = loader.createModuleWrap(strippedSource, url);
|
||||||
// Emit the experimental warning after the code was successfully compiled.
|
// Emit the experimental warning after the code was successfully compiled.
|
||||||
emitExperimentalWarning('Type Stripping');
|
emitExperimentalWarning('Type Stripping');
|
||||||
} catch (tsError) {
|
} catch (tsError) {
|
||||||
// If its not an error, or it's not an invalid typescript syntax error, rethrow it.
|
// If it's invalid or unsupported TypeScript syntax, rethrow the original error
|
||||||
if (!isError(tsError) || tsError?.code !== 'ERR_INVALID_TYPESCRIPT_SYNTAX') {
|
// with the TypeScript error message added to the stack.
|
||||||
throw tsError;
|
if (tsError.code === 'ERR_INVALID_TYPESCRIPT_SYNTAX' ||
|
||||||
}
|
tsError.code === 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX') {
|
||||||
try {
|
|
||||||
originalError.stack = `${tsError.message}\n\n${originalError.stack}`;
|
originalError.stack = `${tsError.message}\n\n${originalError.stack}`;
|
||||||
} catch { /* Ignore potential errors coming from `stack` getter/setter */ }
|
|
||||||
|
|
||||||
throw originalError;
|
throw originalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw tsError;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// If the moduleWrap was successfully created either with by just compiling
|
// If the moduleWrap was successfully created either with by just compiling
|
||||||
// or after transpilation, execute the module job.
|
// or after transpilation, execute the module job.
|
||||||
|
|
|
@ -102,33 +102,33 @@ test('expect fail eval TypeScript ESM syntax with input-type commonjs-typescript
|
||||||
strictEqual(result.code, 1);
|
strictEqual(result.code, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('check syntax error is thrown when passing invalid syntax', async () => {
|
test('check syntax error is thrown when passing unsupported syntax', async () => {
|
||||||
const result = await spawnPromisified(process.execPath, [
|
const result = await spawnPromisified(process.execPath, [
|
||||||
'--eval',
|
'--eval',
|
||||||
'enum Foo { A, B, C }']);
|
'enum Foo { A, B, C }']);
|
||||||
strictEqual(result.stdout, '');
|
strictEqual(result.stdout, '');
|
||||||
match(result.stderr, /SyntaxError/);
|
match(result.stderr, /SyntaxError/);
|
||||||
doesNotMatch(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
|
doesNotMatch(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/);
|
||||||
strictEqual(result.code, 1);
|
strictEqual(result.code, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('check syntax error is thrown when passing invalid syntax with --input-type=module-typescript', async () => {
|
test('check syntax error is thrown when passing unsupported syntax with --input-type=module-typescript', async () => {
|
||||||
const result = await spawnPromisified(process.execPath, [
|
const result = await spawnPromisified(process.execPath, [
|
||||||
'--input-type=module-typescript',
|
'--input-type=module-typescript',
|
||||||
'--eval',
|
'--eval',
|
||||||
'enum Foo { A, B, C }']);
|
'enum Foo { A, B, C }']);
|
||||||
strictEqual(result.stdout, '');
|
strictEqual(result.stdout, '');
|
||||||
match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
|
match(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/);
|
||||||
strictEqual(result.code, 1);
|
strictEqual(result.code, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('check syntax error is thrown when passing invalid syntax with --input-type=commonjs-typescript', async () => {
|
test('check syntax error is thrown when passing unsupported syntax with --input-type=commonjs-typescript', async () => {
|
||||||
const result = await spawnPromisified(process.execPath, [
|
const result = await spawnPromisified(process.execPath, [
|
||||||
'--input-type=commonjs-typescript',
|
'--input-type=commonjs-typescript',
|
||||||
'--eval',
|
'--eval',
|
||||||
'enum Foo { A, B, C }']);
|
'enum Foo { A, B, C }']);
|
||||||
strictEqual(result.stdout, '');
|
strictEqual(result.stdout, '');
|
||||||
match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
|
match(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/);
|
||||||
strictEqual(result.code, 1);
|
strictEqual(result.code, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ test('should not parse TypeScript with --type-module=commonjs', async () => {
|
||||||
|
|
||||||
strictEqual(result.stdout, '');
|
strictEqual(result.stdout, '');
|
||||||
match(result.stderr, /SyntaxError/);
|
match(result.stderr, /SyntaxError/);
|
||||||
doesNotMatch(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
|
doesNotMatch(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/);
|
||||||
strictEqual(result.code, 1);
|
strictEqual(result.code, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ test('should not parse TypeScript with --type-module=module', async () => {
|
||||||
|
|
||||||
strictEqual(result.stdout, '');
|
strictEqual(result.stdout, '');
|
||||||
match(result.stderr, /SyntaxError/);
|
match(result.stderr, /SyntaxError/);
|
||||||
doesNotMatch(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
|
doesNotMatch(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/);
|
||||||
strictEqual(result.code, 1);
|
strictEqual(result.code, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -222,3 +222,23 @@ test('typescript CJS code is throwing a syntax error at runtime', async () => {
|
||||||
strictEqual(result.stdout, '');
|
strictEqual(result.stdout, '');
|
||||||
strictEqual(result.code, 1);
|
strictEqual(result.code, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('check syntax error is thrown when passing invalid syntax with --input-type=commonjs-typescript', async () => {
|
||||||
|
const result = await spawnPromisified(process.execPath, [
|
||||||
|
'--input-type=commonjs-typescript',
|
||||||
|
'--eval',
|
||||||
|
'function foo(){ await Promise.resolve(1); }']);
|
||||||
|
strictEqual(result.stdout, '');
|
||||||
|
match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
|
||||||
|
strictEqual(result.code, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('check syntax error is thrown when passing invalid syntax with --input-type=module-typescript', async () => {
|
||||||
|
const result = await spawnPromisified(process.execPath, [
|
||||||
|
'--input-type=module-typescript',
|
||||||
|
'--eval',
|
||||||
|
'function foo(){ await Promise.resolve(1); }']);
|
||||||
|
strictEqual(result.stdout, '');
|
||||||
|
match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
|
||||||
|
strictEqual(result.code, 1);
|
||||||
|
});
|
||||||
|
|
|
@ -321,3 +321,13 @@ test('execute a TypeScript loader and a .js file', async () => {
|
||||||
match(result.stdout, /Hello, TypeScript!/);
|
match(result.stdout, /Hello, TypeScript!/);
|
||||||
strictEqual(result.code, 0);
|
strictEqual(result.code, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('execute invalid TypeScript syntax', async () => {
|
||||||
|
const result = await spawnPromisified(process.execPath, [
|
||||||
|
fixtures.path('typescript/ts/test-invalid-syntax.ts'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
match(result.stderr, /ERR_INVALID_TYPESCRIPT_SYNTAX/);
|
||||||
|
strictEqual(result.stdout, '');
|
||||||
|
strictEqual(result.code, 1);
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
function foo(): string {
|
||||||
|
await Promise.resolve(1);
|
||||||
|
}
|
Loading…
Reference in New Issue