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.
|
* It allows us to distinguish between invalid syntax and unsupported syntax.
|
||||||
*/
|
*/
|
||||||
switch (error?.code) {
|
switch (error?.code) {
|
||||||
case 'UnsupportedSyntax':
|
case 'UnsupportedSyntax': {
|
||||||
throw new ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX(error.message);
|
const unsupportedSyntaxError = new ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX(error.message);
|
||||||
case 'InvalidSyntax':
|
throw decorateErrorWithSnippet(unsupportedSyntaxError, error); /* node-do-not-add-exception-line */
|
||||||
throw new ERR_INVALID_TYPESCRIPT_SYNTAX(error.message);
|
}
|
||||||
|
case 'InvalidSyntax': {
|
||||||
|
const invalidSyntaxError = new ERR_INVALID_TYPESCRIPT_SYNTAX(error.message);
|
||||||
|
throw decorateErrorWithSnippet(invalidSyntaxError, error); /* node-do-not-add-exception-line */
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
// SWC may throw strings when something goes wrong.
|
// SWC may throw strings when something goes wrong.
|
||||||
if (typeof error === 'string') { assert.fail(error); }
|
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.
|
* Performs type-stripping to TypeScript source code.
|
||||||
* @param {string} code TypeScript code to parse.
|
* @param {string} code TypeScript code to parse.
|
||||||
|
@ -44,6 +44,8 @@ const { emitExperimentalWarning } = require('internal/util');
|
|||||||
// communication with JS.
|
// communication with JS.
|
||||||
const { shouldAbortOnUncaughtToggle } = internalBinding('util');
|
const { shouldAbortOnUncaughtToggle } = internalBinding('util');
|
||||||
|
|
||||||
|
const kEvalTag = '[eval]';
|
||||||
|
|
||||||
function tryGetCwd() {
|
function tryGetCwd() {
|
||||||
try {
|
try {
|
||||||
return process.cwd();
|
return process.cwd();
|
||||||
@ -259,7 +261,7 @@ function evalTypeScript(name, source, breakFirstLine, print, shouldLoadESM = fal
|
|||||||
compiledScript = compileScript(name, source, baseUrl);
|
compiledScript = compileScript(name, source, baseUrl);
|
||||||
} catch (originalError) {
|
} catch (originalError) {
|
||||||
try {
|
try {
|
||||||
sourceToRun = stripTypeScriptModuleTypes(source, name, false);
|
sourceToRun = stripTypeScriptModuleTypes(source, kEvalTag, false);
|
||||||
// Retry the CJS/ESM syntax detection after stripping the types.
|
// Retry the CJS/ESM syntax detection after stripping the types.
|
||||||
if (shouldUseModuleEntryPoint(name, sourceToRun)) {
|
if (shouldUseModuleEntryPoint(name, sourceToRun)) {
|
||||||
return evalTypeScriptModuleEntryPoint(source, print);
|
return evalTypeScriptModuleEntryPoint(source, print);
|
||||||
@ -322,7 +324,7 @@ function evalTypeScriptModuleEntryPoint(source, print) {
|
|||||||
moduleWrap = loader.createModuleWrap(source, url);
|
moduleWrap = loader.createModuleWrap(source, url);
|
||||||
} catch (originalError) {
|
} catch (originalError) {
|
||||||
try {
|
try {
|
||||||
const strippedSource = stripTypeScriptModuleTypes(source, url, false);
|
const strippedSource = stripTypeScriptModuleTypes(source, kEvalTag, 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);
|
||||||
@ -355,7 +357,7 @@ function evalTypeScriptModuleEntryPoint(source, print) {
|
|||||||
*/
|
*/
|
||||||
function parseAndEvalModuleTypeScript(source, print) {
|
function parseAndEvalModuleTypeScript(source, print) {
|
||||||
// We know its a TypeScript module, we can safely emit the experimental warning.
|
// 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);
|
evalModuleEntryPoint(strippedSource, print);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,7 +372,7 @@ function parseAndEvalModuleTypeScript(source, print) {
|
|||||||
*/
|
*/
|
||||||
function parseAndEvalCommonjsTypeScript(name, source, breakFirstLine, print, shouldLoadESM = false) {
|
function parseAndEvalCommonjsTypeScript(name, source, breakFirstLine, print, shouldLoadESM = false) {
|
||||||
// We know its a TypeScript module, we can safely emit the experimental warning.
|
// 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);
|
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/);
|
match(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/);
|
||||||
strictEqual(result.code, 1);
|
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
|
[eval]:1
|
||||||
with(this){__filename}
|
with(this){__filename}
|
||||||
^^^^
|
^^^^
|
||||||
x The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
|
The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
|
||||||
,----
|
|
||||||
1 | with(this){__filename}
|
|
||||||
: ^^^^
|
|
||||||
`----
|
|
||||||
|
|
||||||
SyntaxError: Strict mode code may not include a with statement
|
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
|
[eval]:1
|
||||||
enum Foo{};
|
enum Foo{};
|
||||||
^^^^
|
^^^^
|
||||||
x TypeScript enum is not supported in strip-only mode
|
TypeScript enum is not supported in strip-only mode
|
||||||
,----
|
|
||||||
1 | enum Foo{};
|
|
||||||
: ^^^^^^^^^^
|
|
||||||
`----
|
|
||||||
|
|
||||||
SyntaxError: Unexpected reserved word
|
SyntaxError: Unexpected reserved word
|
||||||
|
|
||||||
@ -20,11 +16,7 @@ Node.js *
|
|||||||
[eval]:1
|
[eval]:1
|
||||||
const foo;
|
const foo;
|
||||||
^^^
|
^^^
|
||||||
x 'const' declarations must be initialized
|
'const' declarations must be initialized
|
||||||
,----
|
|
||||||
1 | const foo;
|
|
||||||
: ^^^
|
|
||||||
`----
|
|
||||||
|
|
||||||
SyntaxError: Missing initializer in const declaration
|
SyntaxError: Missing initializer in const declaration
|
||||||
|
|
||||||
@ -35,11 +27,7 @@ false
|
|||||||
[eval]:1
|
[eval]:1
|
||||||
interface Foo{};const foo;
|
interface Foo{};const foo;
|
||||||
^^^
|
^^^
|
||||||
x 'const' declarations must be initialized
|
'const' declarations must be initialized
|
||||||
,----
|
|
||||||
1 | interface Foo{};const foo;
|
|
||||||
: ^^^
|
|
||||||
`----
|
|
||||||
|
|
||||||
SyntaxError: Unexpected identifier 'Foo'
|
SyntaxError: Unexpected identifier 'Foo'
|
||||||
|
|
||||||
@ -47,11 +35,7 @@ Node.js *
|
|||||||
[eval]:1
|
[eval]:1
|
||||||
function foo(){ await Promise.resolve(1)};
|
function foo(){ await Promise.resolve(1)};
|
||||||
^^^^^
|
^^^^^
|
||||||
x await isn't allowed in non-async function
|
await isn't allowed in non-async function
|
||||||
,----
|
|
||||||
1 | function foo(){ await Promise.resolve(1)};
|
|
||||||
: ^^^^^^^
|
|
||||||
`----
|
|
||||||
|
|
||||||
SyntaxError: await is only valid in async functions and the top level bodies of modules
|
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
|
[stdin]:1
|
||||||
with(this){__filename}
|
with(this){__filename}
|
||||||
^^^^
|
^^^^
|
||||||
x The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
|
The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
|
||||||
,----
|
|
||||||
1 | with(this){__filename}
|
|
||||||
: ^^^^
|
|
||||||
`----
|
|
||||||
|
|
||||||
SyntaxError: Strict mode code may not include a with statement
|
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
|
[stdin]:1
|
||||||
enum Foo{};
|
enum Foo{};
|
||||||
^^^^
|
^^^^
|
||||||
x TypeScript enum is not supported in strip-only mode
|
TypeScript enum is not supported in strip-only mode
|
||||||
,----
|
|
||||||
1 | enum Foo{};
|
|
||||||
: ^^^^^^^^^^
|
|
||||||
`----
|
|
||||||
|
|
||||||
SyntaxError: Unexpected reserved word
|
SyntaxError: Unexpected reserved word
|
||||||
|
|
||||||
@ -13,11 +9,7 @@ Node.js *
|
|||||||
[stdin]:1
|
[stdin]:1
|
||||||
enum Foo{};
|
enum Foo{};
|
||||||
^^^^
|
^^^^
|
||||||
x TypeScript enum is not supported in strip-only mode
|
TypeScript enum is not supported in strip-only mode
|
||||||
,----
|
|
||||||
1 | enum Foo{};
|
|
||||||
: ^^^^^^^^^^
|
|
||||||
`----
|
|
||||||
|
|
||||||
SyntaxError: Unexpected reserved word
|
SyntaxError: Unexpected reserved word
|
||||||
|
|
||||||
@ -39,11 +31,7 @@ Node.js *
|
|||||||
[stdin]:1
|
[stdin]:1
|
||||||
const foo;
|
const foo;
|
||||||
^^^
|
^^^
|
||||||
x 'const' declarations must be initialized
|
'const' declarations must be initialized
|
||||||
,----
|
|
||||||
1 | const foo;
|
|
||||||
: ^^^
|
|
||||||
`----
|
|
||||||
|
|
||||||
SyntaxError: Missing initializer in const declaration
|
SyntaxError: Missing initializer in const declaration
|
||||||
|
|
||||||
@ -51,11 +39,7 @@ Node.js *
|
|||||||
[stdin]:1
|
[stdin]:1
|
||||||
const foo;
|
const foo;
|
||||||
^^^
|
^^^
|
||||||
x 'const' declarations must be initialized
|
'const' declarations must be initialized
|
||||||
,----
|
|
||||||
1 | const foo;
|
|
||||||
: ^^^
|
|
||||||
`----
|
|
||||||
|
|
||||||
SyntaxError: Missing initializer in const declaration
|
SyntaxError: Missing initializer in const declaration
|
||||||
|
|
||||||
@ -69,11 +53,7 @@ false
|
|||||||
[stdin]:1
|
[stdin]:1
|
||||||
interface Foo{};const foo;
|
interface Foo{};const foo;
|
||||||
^^^
|
^^^
|
||||||
x 'const' declarations must be initialized
|
'const' declarations must be initialized
|
||||||
,----
|
|
||||||
1 | interface Foo{};const foo;
|
|
||||||
: ^^^
|
|
||||||
`----
|
|
||||||
|
|
||||||
SyntaxError: Unexpected identifier 'Foo'
|
SyntaxError: Unexpected identifier 'Foo'
|
||||||
|
|
||||||
@ -81,11 +61,7 @@ Node.js *
|
|||||||
[stdin]:1
|
[stdin]:1
|
||||||
interface Foo{};const foo;
|
interface Foo{};const foo;
|
||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
x 'const' declarations must be initialized
|
'const' declarations must be initialized
|
||||||
,----
|
|
||||||
1 | interface Foo{};const foo;
|
|
||||||
: ^^^
|
|
||||||
`----
|
|
||||||
|
|
||||||
SyntaxError: Unexpected strict mode reserved word
|
SyntaxError: Unexpected strict mode reserved word
|
||||||
|
|
||||||
@ -93,11 +69,7 @@ Node.js *
|
|||||||
[stdin]:1
|
[stdin]:1
|
||||||
function foo(){ await Promise.resolve(1)};
|
function foo(){ await Promise.resolve(1)};
|
||||||
^^^^^
|
^^^^^
|
||||||
x await isn't allowed in non-async function
|
await isn't allowed in non-async function
|
||||||
,----
|
|
||||||
1 | function foo(){ await Promise.resolve(1)};
|
|
||||||
: ^^^^^^^
|
|
||||||
`----
|
|
||||||
|
|
||||||
SyntaxError: await is only valid in async functions and the top level bodies of modules
|
SyntaxError: await is only valid in async functions and the top level bodies of modules
|
||||||
|
|
||||||
@ -105,11 +77,7 @@ Node.js *
|
|||||||
[stdin]:1
|
[stdin]:1
|
||||||
function foo(){ await Promise.resolve(1)};
|
function foo(){ await Promise.resolve(1)};
|
||||||
^^^^^
|
^^^^^
|
||||||
x await isn't allowed in non-async function
|
await isn't allowed in non-async function
|
||||||
,----
|
|
||||||
1 | function foo(){ await Promise.resolve(1)};
|
|
||||||
: ^^^^^^^
|
|
||||||
`----
|
|
||||||
|
|
||||||
SyntaxError: await is only valid in async functions and the top level bodies of modules
|
SyntaxError: await is only valid in async functions and the top level bodies of modules
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user