Skip to content

Commit 1e490aa

Browse files
marco-ippolitoRafaelGSS
authored andcommitted
module: improve typescript error message format
PR-URL: #57687 Fixes: #56830 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
1 parent a561755 commit 1e490aa

File tree

7 files changed

+60
-78
lines changed

7 files changed

+60
-78
lines changed

lib/internal/modules/typescript.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,14 @@ function parseTypeScript(source, options) {
6363
* It allows us to distinguish between invalid syntax and unsupported syntax.
6464
*/
6565
switch (error?.code) {
66-
case 'UnsupportedSyntax':
67-
throw new ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX(error.message);
68-
case 'InvalidSyntax':
69-
throw new ERR_INVALID_TYPESCRIPT_SYNTAX(error.message);
66+
case 'UnsupportedSyntax': {
67+
const unsupportedSyntaxError = new ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX(error.message);
68+
throw decorateErrorWithSnippet(unsupportedSyntaxError, error); /* node-do-not-add-exception-line */
69+
}
70+
case 'InvalidSyntax': {
71+
const invalidSyntaxError = new ERR_INVALID_TYPESCRIPT_SYNTAX(error.message);
72+
throw decorateErrorWithSnippet(invalidSyntaxError, error); /* node-do-not-add-exception-line */
73+
}
7074
default:
7175
// SWC may throw strings when something goes wrong.
7276
if (typeof error === 'string') { assert.fail(error); }
@@ -76,6 +80,18 @@ function parseTypeScript(source, options) {
7680
}
7781
}
7882

83+
/**
84+
*
85+
* @param {Error} error the error to decorate: ERR_INVALID_TYPESCRIPT_SYNTAX, ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX
86+
* @param {object} amaroError the error object from amaro
87+
* @returns {Error} the decorated error
88+
*/
89+
function decorateErrorWithSnippet(error, amaroError) {
90+
const errorHints = `${amaroError.filename}:${amaroError.startLine}${amaroError.snippet}`;
91+
error.stack = `${errorHints}${error.stack}`;
92+
return error;
93+
}
94+
7995
/**
8096
* Performs type-stripping to TypeScript source code.
8197
* @param {string} code TypeScript code to parse.

lib/internal/process/execution.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ const { emitExperimentalWarning } = require('internal/util');
4444
// communication with JS.
4545
const { shouldAbortOnUncaughtToggle } = internalBinding('util');
4646

47+
const kEvalTag = '[eval]';
48+
4749
function tryGetCwd() {
4850
try {
4951
return process.cwd();
@@ -259,7 +261,7 @@ function evalTypeScript(name, source, breakFirstLine, print, shouldLoadESM = fal
259261
compiledScript = compileScript(name, source, baseUrl);
260262
} catch (originalError) {
261263
try {
262-
sourceToRun = stripTypeScriptModuleTypes(source, name, false);
264+
sourceToRun = stripTypeScriptModuleTypes(source, kEvalTag, false);
263265
// Retry the CJS/ESM syntax detection after stripping the types.
264266
if (shouldUseModuleEntryPoint(name, sourceToRun)) {
265267
return evalTypeScriptModuleEntryPoint(source, print);
@@ -322,7 +324,7 @@ function evalTypeScriptModuleEntryPoint(source, print) {
322324
moduleWrap = loader.createModuleWrap(source, url);
323325
} catch (originalError) {
324326
try {
325-
const strippedSource = stripTypeScriptModuleTypes(source, url, false);
327+
const strippedSource = stripTypeScriptModuleTypes(source, kEvalTag, false);
326328
// If the moduleWrap was successfully created, execute the module job.
327329
// outside the try-catch block to avoid catching runtime errors.
328330
moduleWrap = loader.createModuleWrap(strippedSource, url);
@@ -355,7 +357,7 @@ function evalTypeScriptModuleEntryPoint(source, print) {
355357
*/
356358
function parseAndEvalModuleTypeScript(source, print) {
357359
// We know its a TypeScript module, we can safely emit the experimental warning.
358-
const strippedSource = stripTypeScriptModuleTypes(source, getEvalModuleUrl());
360+
const strippedSource = stripTypeScriptModuleTypes(source, kEvalTag);
359361
evalModuleEntryPoint(strippedSource, print);
360362
}
361363

@@ -370,7 +372,7 @@ function parseAndEvalModuleTypeScript(source, print) {
370372
*/
371373
function parseAndEvalCommonjsTypeScript(name, source, breakFirstLine, print, shouldLoadESM = false) {
372374
// We know its a TypeScript module, we can safely emit the experimental warning.
373-
const strippedSource = stripTypeScriptModuleTypes(source, getEvalModuleUrl());
375+
const strippedSource = stripTypeScriptModuleTypes(source, kEvalTag);
374376
evalScript(name, strippedSource, breakFirstLine, print, shouldLoadESM);
375377
}
376378

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,3 +262,23 @@ test('should not allow declare module keyword', async () => {
262262
match(result.stderr, /ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX/);
263263
strictEqual(result.code, 1);
264264
});
265+
266+
// TODO (marco-ippolito) Remove the extra padding from the error message
267+
// The padding comes from swc it will be removed in a future amaro release
268+
test('the error message should not contain extra padding', async () => {
269+
const result = await spawnPromisified(process.execPath, [
270+
'--input-type=module-typescript',
271+
'--eval',
272+
'declare module F { export type x = number }']);
273+
strictEqual(result.stdout, '');
274+
// Windows uses \r\n as line endings
275+
const lines = result.stderr.replace(/\r\n/g, '\n').split('\n');
276+
// The extra padding at the end should not be present
277+
strictEqual(lines[0], '[eval]:1 ');
278+
// The extra padding at the beginning should not be present
279+
strictEqual(lines[2], ' declare module F { export type x = number }');
280+
strictEqual(lines[3], ' ^^^^^^^^');
281+
strictEqual(lines[5], 'SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]:' +
282+
' `module` keyword is not supported. Use `namespace` instead.');
283+
strictEqual(result.code, 1);
284+
});

test/fixtures/eval/eval_messages.snapshot

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22
[eval]:1
33
with(this){__filename}
44
^^^^
5-
x The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
6-
,----
7-
1 | with(this){__filename}
8-
: ^^^^
9-
`----
5+
The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
106

117
SyntaxError: Strict mode code may not include a with statement
128

test/fixtures/eval/eval_typescript.snapshot

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
[eval]:1
22
enum Foo{};
33
^^^^
4-
x TypeScript enum is not supported in strip-only mode
5-
,----
6-
1 | enum Foo{};
7-
: ^^^^^^^^^^
8-
`----
4+
TypeScript enum is not supported in strip-only mode
95

106
SyntaxError: Unexpected reserved word
117

@@ -20,11 +16,7 @@ Node.js *
2016
[eval]:1
2117
const foo;
2218
^^^
23-
x 'const' declarations must be initialized
24-
,----
25-
1 | const foo;
26-
: ^^^
27-
`----
19+
'const' declarations must be initialized
2820

2921
SyntaxError: Missing initializer in const declaration
3022

@@ -35,23 +27,15 @@ false
3527
[eval]:1
3628
interface Foo{};const foo;
3729
^^^
38-
x 'const' declarations must be initialized
39-
,----
40-
1 | interface Foo{};const foo;
41-
: ^^^
42-
`----
30+
'const' declarations must be initialized
4331

4432
SyntaxError: Unexpected identifier 'Foo'
4533

4634
Node.js *
4735
[eval]:1
4836
function foo(){ await Promise.resolve(1)};
4937
^^^^^
50-
x await isn't allowed in non-async function
51-
,----
52-
1 | function foo(){ await Promise.resolve(1)};
53-
: ^^^^^^^
54-
`----
38+
await isn't allowed in non-async function
5539

5640
SyntaxError: await is only valid in async functions and the top level bodies of modules
5741

test/fixtures/eval/stdin_messages.snapshot

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22
[stdin]:1
33
with(this){__filename}
44
^^^^
5-
x The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
6-
,----
7-
1 | with(this){__filename}
8-
: ^^^^
9-
`----
5+
The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
106

117
SyntaxError: Strict mode code may not include a with statement
128

test/fixtures/eval/stdin_typescript.snapshot

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,15 @@
11
[stdin]:1
22
enum Foo{};
33
^^^^
4-
x TypeScript enum is not supported in strip-only mode
5-
,----
6-
1 | enum Foo{};
7-
: ^^^^^^^^^^
8-
`----
4+
TypeScript enum is not supported in strip-only mode
95

106
SyntaxError: Unexpected reserved word
117

128
Node.js *
139
[stdin]:1
1410
enum Foo{};
1511
^^^^
16-
x TypeScript enum is not supported in strip-only mode
17-
,----
18-
1 | enum Foo{};
19-
: ^^^^^^^^^^
20-
`----
12+
TypeScript enum is not supported in strip-only mode
2113

2214
SyntaxError: Unexpected reserved word
2315

@@ -39,23 +31,15 @@ Node.js *
3931
[stdin]:1
4032
const foo;
4133
^^^
42-
x 'const' declarations must be initialized
43-
,----
44-
1 | const foo;
45-
: ^^^
46-
`----
34+
'const' declarations must be initialized
4735

4836
SyntaxError: Missing initializer in const declaration
4937

5038
Node.js *
5139
[stdin]:1
5240
const foo;
5341
^^^
54-
x 'const' declarations must be initialized
55-
,----
56-
1 | const foo;
57-
: ^^^
58-
`----
42+
'const' declarations must be initialized
5943

6044
SyntaxError: Missing initializer in const declaration
6145

@@ -69,47 +53,31 @@ false
6953
[stdin]:1
7054
interface Foo{};const foo;
7155
^^^
72-
x 'const' declarations must be initialized
73-
,----
74-
1 | interface Foo{};const foo;
75-
: ^^^
76-
`----
56+
'const' declarations must be initialized
7757

7858
SyntaxError: Unexpected identifier 'Foo'
7959

8060
Node.js *
8161
[stdin]:1
8262
interface Foo{};const foo;
8363
^^^^^^^^^
84-
x 'const' declarations must be initialized
85-
,----
86-
1 | interface Foo{};const foo;
87-
: ^^^
88-
`----
64+
'const' declarations must be initialized
8965

9066
SyntaxError: Unexpected strict mode reserved word
9167

9268
Node.js *
9369
[stdin]:1
9470
function foo(){ await Promise.resolve(1)};
9571
^^^^^
96-
x await isn't allowed in non-async function
97-
,----
98-
1 | function foo(){ await Promise.resolve(1)};
99-
: ^^^^^^^
100-
`----
72+
await isn't allowed in non-async function
10173

10274
SyntaxError: await is only valid in async functions and the top level bodies of modules
10375

10476
Node.js *
10577
[stdin]:1
10678
function foo(){ await Promise.resolve(1)};
10779
^^^^^
108-
x await isn't allowed in non-async function
109-
,----
110-
1 | function foo(){ await Promise.resolve(1)};
111-
: ^^^^^^^
112-
`----
80+
await isn't allowed in non-async function
11381

11482
SyntaxError: await is only valid in async functions and the top level bodies of modules
11583

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy