module: improve typescript error message format
PR-URL: https://github.com/nodejs/node/pull/57687 Fixes: https://github.com/nodejs/node/issues/56830 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
This commit is contained in:
parent
e5ff73133a
commit
1c2d98d380
@ -63,10 +63,14 @@ function parseTypeScript(source, options) {
|
||||
* 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);
|
||||
case 'UnsupportedSyntax': {
|
||||
const unsupportedSyntaxError = new ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX(error.message);
|
||||
throw decorateErrorWithSnippet(unsupportedSyntaxError, error); /* node-do-not-add-exception-line */
|
||||
}
|
||||
case 'InvalidSyntax': {
|
||||
const invalidSyntaxError = new ERR_INVALID_TYPESCRIPT_SYNTAX(error.message);
|
||||
throw decorateErrorWithSnippet(invalidSyntaxError, error); /* node-do-not-add-exception-line */
|
||||
}
|
||||
default:
|
||||
// SWC may throw strings when something goes wrong.
|
||||
if (typeof error === 'string') { assert.fail(error); }
|
||||
@ -76,6 +80,18 @@ function parseTypeScript(source, options) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Error} error the error to decorate: ERR_INVALID_TYPESCRIPT_SYNTAX, ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX
|
||||
* @param {object} amaroError the error object from amaro
|
||||
* @returns {Error} the decorated error
|
||||
*/
|
||||
function decorateErrorWithSnippet(error, amaroError) {
|
||||
const errorHints = `${amaroError.filename}:${amaroError.startLine}${amaroError.snippet}`;
|
||||
error.stack = `${errorHints}${error.stack}`;
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs type-stripping to TypeScript source code.
|
||||
* @param {string} code TypeScript code to parse.
|
||||
|
@ -44,6 +44,8 @@ const { emitExperimentalWarning } = require('internal/util');
|
||||
// communication with JS.
|
||||
const { shouldAbortOnUncaughtToggle } = internalBinding('util');
|
||||
|
||||
const kEvalTag = '[eval]';
|
||||
|
||||
function tryGetCwd() {
|
||||
try {
|
||||
return process.cwd();
|
||||
@ -259,7 +261,7 @@ function evalTypeScript(name, source, breakFirstLine, print, shouldLoadESM = fal
|
||||
compiledScript = compileScript(name, source, baseUrl);
|
||||
} catch (originalError) {
|
||||
try {
|
||||
sourceToRun = stripTypeScriptModuleTypes(source, name, false);
|
||||
sourceToRun = stripTypeScriptModuleTypes(source, kEvalTag, false);
|
||||
// Retry the CJS/ESM syntax detection after stripping the types.
|
||||
if (shouldUseModuleEntryPoint(name, sourceToRun)) {
|
||||
return evalTypeScriptModuleEntryPoint(source, print);
|
||||
@ -322,7 +324,7 @@ function evalTypeScriptModuleEntryPoint(source, print) {
|
||||
moduleWrap = loader.createModuleWrap(source, url);
|
||||
} catch (originalError) {
|
||||
try {
|
||||
const strippedSource = stripTypeScriptModuleTypes(source, url, false);
|
||||
const strippedSource = stripTypeScriptModuleTypes(source, kEvalTag, false);
|
||||
// If the moduleWrap was successfully created, execute the module job.
|
||||
// outside the try-catch block to avoid catching runtime errors.
|
||||
moduleWrap = loader.createModuleWrap(strippedSource, url);
|
||||
@ -355,7 +357,7 @@ function evalTypeScriptModuleEntryPoint(source, print) {
|
||||
*/
|
||||
function parseAndEvalModuleTypeScript(source, print) {
|
||||
// We know its a TypeScript module, we can safely emit the experimental warning.
|
||||
const strippedSource = stripTypeScriptModuleTypes(source, getEvalModuleUrl());
|
||||
const strippedSource = stripTypeScriptModuleTypes(source, kEvalTag);
|
||||
evalModuleEntryPoint(strippedSource, print);
|
||||
}
|
||||
|
||||
@ -370,7 +372,7 @@ function parseAndEvalModuleTypeScript(source, print) {
|
||||
*/
|
||||
function parseAndEvalCommonjsTypeScript(name, source, breakFirstLine, print, shouldLoadESM = false) {
|
||||
// We know its a TypeScript module, we can safely emit the experimental warning.
|
||||
const strippedSource = stripTypeScriptModuleTypes(source, getEvalModuleUrl());
|
||||
const strippedSource = stripTypeScriptModuleTypes(source, kEvalTag);
|
||||
evalScript(name, strippedSource, breakFirstLine, print, shouldLoadESM);
|
||||
}
|
||||
|
||||
|
@ -262,3 +262,23 @@ test('should not allow declare module keyword', async () => {
|
||||
match(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/);
|
||||
strictEqual(result.code, 1);
|
||||
});
|
||||
|
||||
// TODO (marco-ippolito) Remove the extra padding from the error message
|
||||
// The padding comes from swc it will be removed in a future amaro release
|
||||
test('the error message should not contain extra padding', async () => {
|
||||
const result = await spawnPromisified(process.execPath, [
|
||||
'--input-type=module-typescript',
|
||||
'--eval',
|
||||
'declare module F { export type x = number }']);
|
||||
strictEqual(result.stdout, '');
|
||||
// Windows uses \r\n as line endings
|
||||
const lines = result.stderr.replace(/\r\n/g, '\n').split('\n');
|
||||
// The extra padding at the end should not be present
|
||||
strictEqual(lines[0], '[eval]:1 ');
|
||||
// The extra padding at the beginning should not be present
|
||||
strictEqual(lines[2], ' declare module F { export type x = number }');
|
||||
strictEqual(lines[3], ' ^^^^^^^^');
|
||||
strictEqual(lines[5], 'SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]:' +
|
||||
' `module` keyword is not supported. Use `namespace` instead.');
|
||||
strictEqual(result.code, 1);
|
||||
});
|
||||
|
6
test/fixtures/eval/eval_messages.snapshot
vendored
6
test/fixtures/eval/eval_messages.snapshot
vendored
@ -2,11 +2,7 @@
|
||||
[eval]:1
|
||||
with(this){__filename}
|
||||
^^^^
|
||||
x The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
|
||||
,----
|
||||
1 | with(this){__filename}
|
||||
: ^^^^
|
||||
`----
|
||||
The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
|
||||
|
||||
SyntaxError: Strict mode code may not include a with statement
|
||||
|
||||
|
24
test/fixtures/eval/eval_typescript.snapshot
vendored
24
test/fixtures/eval/eval_typescript.snapshot
vendored
@ -1,11 +1,7 @@
|
||||
[eval]:1
|
||||
enum Foo{};
|
||||
^^^^
|
||||
x TypeScript enum is not supported in strip-only mode
|
||||
,----
|
||||
1 | enum Foo{};
|
||||
: ^^^^^^^^^^
|
||||
`----
|
||||
TypeScript enum is not supported in strip-only mode
|
||||
|
||||
SyntaxError: Unexpected reserved word
|
||||
|
||||
@ -20,11 +16,7 @@ Node.js *
|
||||
[eval]:1
|
||||
const foo;
|
||||
^^^
|
||||
x 'const' declarations must be initialized
|
||||
,----
|
||||
1 | const foo;
|
||||
: ^^^
|
||||
`----
|
||||
'const' declarations must be initialized
|
||||
|
||||
SyntaxError: Missing initializer in const declaration
|
||||
|
||||
@ -35,11 +27,7 @@ false
|
||||
[eval]:1
|
||||
interface Foo{};const foo;
|
||||
^^^
|
||||
x 'const' declarations must be initialized
|
||||
,----
|
||||
1 | interface Foo{};const foo;
|
||||
: ^^^
|
||||
`----
|
||||
'const' declarations must be initialized
|
||||
|
||||
SyntaxError: Unexpected identifier 'Foo'
|
||||
|
||||
@ -47,11 +35,7 @@ Node.js *
|
||||
[eval]:1
|
||||
function foo(){ await Promise.resolve(1)};
|
||||
^^^^^
|
||||
x await isn't allowed in non-async function
|
||||
,----
|
||||
1 | function foo(){ await Promise.resolve(1)};
|
||||
: ^^^^^^^
|
||||
`----
|
||||
await isn't allowed in non-async function
|
||||
|
||||
SyntaxError: await is only valid in async functions and the top level bodies of modules
|
||||
|
||||
|
6
test/fixtures/eval/stdin_messages.snapshot
vendored
6
test/fixtures/eval/stdin_messages.snapshot
vendored
@ -2,11 +2,7 @@
|
||||
[stdin]:1
|
||||
with(this){__filename}
|
||||
^^^^
|
||||
x The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
|
||||
,----
|
||||
1 | with(this){__filename}
|
||||
: ^^^^
|
||||
`----
|
||||
The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
|
||||
|
||||
SyntaxError: Strict mode code may not include a with statement
|
||||
|
||||
|
48
test/fixtures/eval/stdin_typescript.snapshot
vendored
48
test/fixtures/eval/stdin_typescript.snapshot
vendored
@ -1,11 +1,7 @@
|
||||
[stdin]:1
|
||||
enum Foo{};
|
||||
^^^^
|
||||
x TypeScript enum is not supported in strip-only mode
|
||||
,----
|
||||
1 | enum Foo{};
|
||||
: ^^^^^^^^^^
|
||||
`----
|
||||
TypeScript enum is not supported in strip-only mode
|
||||
|
||||
SyntaxError: Unexpected reserved word
|
||||
|
||||
@ -13,11 +9,7 @@ Node.js *
|
||||
[stdin]:1
|
||||
enum Foo{};
|
||||
^^^^
|
||||
x TypeScript enum is not supported in strip-only mode
|
||||
,----
|
||||
1 | enum Foo{};
|
||||
: ^^^^^^^^^^
|
||||
`----
|
||||
TypeScript enum is not supported in strip-only mode
|
||||
|
||||
SyntaxError: Unexpected reserved word
|
||||
|
||||
@ -39,11 +31,7 @@ Node.js *
|
||||
[stdin]:1
|
||||
const foo;
|
||||
^^^
|
||||
x 'const' declarations must be initialized
|
||||
,----
|
||||
1 | const foo;
|
||||
: ^^^
|
||||
`----
|
||||
'const' declarations must be initialized
|
||||
|
||||
SyntaxError: Missing initializer in const declaration
|
||||
|
||||
@ -51,11 +39,7 @@ Node.js *
|
||||
[stdin]:1
|
||||
const foo;
|
||||
^^^
|
||||
x 'const' declarations must be initialized
|
||||
,----
|
||||
1 | const foo;
|
||||
: ^^^
|
||||
`----
|
||||
'const' declarations must be initialized
|
||||
|
||||
SyntaxError: Missing initializer in const declaration
|
||||
|
||||
@ -69,11 +53,7 @@ false
|
||||
[stdin]:1
|
||||
interface Foo{};const foo;
|
||||
^^^
|
||||
x 'const' declarations must be initialized
|
||||
,----
|
||||
1 | interface Foo{};const foo;
|
||||
: ^^^
|
||||
`----
|
||||
'const' declarations must be initialized
|
||||
|
||||
SyntaxError: Unexpected identifier 'Foo'
|
||||
|
||||
@ -81,11 +61,7 @@ Node.js *
|
||||
[stdin]:1
|
||||
interface Foo{};const foo;
|
||||
^^^^^^^^^
|
||||
x 'const' declarations must be initialized
|
||||
,----
|
||||
1 | interface Foo{};const foo;
|
||||
: ^^^
|
||||
`----
|
||||
'const' declarations must be initialized
|
||||
|
||||
SyntaxError: Unexpected strict mode reserved word
|
||||
|
||||
@ -93,11 +69,7 @@ Node.js *
|
||||
[stdin]:1
|
||||
function foo(){ await Promise.resolve(1)};
|
||||
^^^^^
|
||||
x await isn't allowed in non-async function
|
||||
,----
|
||||
1 | function foo(){ await Promise.resolve(1)};
|
||||
: ^^^^^^^
|
||||
`----
|
||||
await isn't allowed in non-async function
|
||||
|
||||
SyntaxError: await is only valid in async functions and the top level bodies of modules
|
||||
|
||||
@ -105,11 +77,7 @@ Node.js *
|
||||
[stdin]:1
|
||||
function foo(){ await Promise.resolve(1)};
|
||||
^^^^^
|
||||
x await isn't allowed in non-async function
|
||||
,----
|
||||
1 | function foo(){ await Promise.resolve(1)};
|
||||
: ^^^^^^^
|
||||
`----
|
||||
await isn't allowed in non-async function
|
||||
|
||||
SyntaxError: await is only valid in async functions and the top level bodies of modules
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user