Skip to content

Commit acc7d12

Browse files
authored
feat: improve error formatting to match webpack 4 convention (#641)
1 parent cdb531f commit acc7d12

12 files changed

+69
-46
lines changed

src/formatter/WebpackFormatter.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import os from 'os';
22
import chalk from 'chalk';
3-
import path from 'path';
43
import { Formatter } from './Formatter';
54
import { formatIssueLocation } from '../issue';
6-
import forwardSlash from '../utils/path/forwardSlash';
5+
import { relativeToContext } from '../utils/path/relativeToContext';
76

87
function createWebpackFormatter(formatter: Formatter): Formatter {
98
// mimics webpack error formatter
@@ -13,14 +12,14 @@ function createWebpackFormatter(formatter: Formatter): Formatter {
1312
const severity = issue.severity.toUpperCase();
1413

1514
if (issue.file) {
16-
let location = forwardSlash(path.relative(process.cwd(), issue.file));
15+
let location = chalk.whiteBright.bold(relativeToContext(issue.file, process.cwd()));
1716
if (issue.location) {
18-
location += `:${formatIssueLocation(issue.location)}`;
17+
location += ` ${chalk.green.bold(formatIssueLocation(issue.location))}`;
1918
}
2019

21-
return [color(`${severity} in ${location}`), formatter(issue), ''].join(os.EOL);
20+
return [`${color(severity)} in ${location}`, formatter(issue), ''].join(os.EOL);
2221
} else {
23-
return [color(`${severity} in `) + formatter(issue), ''].join(os.EOL);
22+
return [`${color(severity)} in ` + formatter(issue), ''].join(os.EOL);
2423
}
2524
};
2625
}

src/issue/IssueLocation.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,20 @@ function compareIssueLocations(locationA?: IssueLocation, locationB?: IssueLocat
2424
);
2525
}
2626

27-
function formatIssueLocation(location: IssueLocation) {
28-
return `${location.start.line}:${location.start.column}`;
27+
function formatIssueLocation({ start, end }: IssueLocation) {
28+
if (!end.line || start.line === end.line) {
29+
// the same line
30+
if (!end.column || start.column === end.column) {
31+
// the same column
32+
return `${start.line}:${start.column}`;
33+
} else {
34+
// different column
35+
return `${start.line}:${start.column}-${end.column}`;
36+
}
37+
} else {
38+
// different lines
39+
return `${start.line}:${start.column}-${end.line}:${end.column}`;
40+
}
2941
}
3042

3143
export { IssueLocation, compareIssueLocations, formatIssueLocation };

src/issue/IssueWebpackError.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import webpack from 'webpack';
2-
import { relative } from 'path';
32
import { Issue } from './Issue';
43
import { formatIssueLocation } from './IssueLocation';
5-
import forwardSlash from '../utils/path/forwardSlash';
4+
import { relativeToContext } from '../utils/path/relativeToContext';
5+
import chalk from 'chalk';
66

77
class IssueWebpackError extends webpack.WebpackError {
88
readonly hideStack = true;
9-
readonly file: string = '';
109

1110
constructor(message: string, readonly issue: Issue) {
1211
super(message);
@@ -15,10 +14,10 @@ class IssueWebpackError extends webpack.WebpackError {
1514
// should be a NormalModule instance.
1615
// to avoid such a dependency, we do a workaround - error.file will contain formatted location instead
1716
if (issue.file) {
18-
this.file = forwardSlash(relative(process.cwd(), issue.file));
17+
this.file = relativeToContext(issue.file, process.cwd());
1918

2019
if (issue.location) {
21-
this.file += `:${formatIssueLocation(issue.location)}`;
20+
this.file += ` ${chalk.green.bold(formatIssueLocation(issue.location))}`;
2221
}
2322
}
2423

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import forwardSlash from './forwardSlash';
2+
import path from 'path';
3+
4+
function relativeToContext(file: string, context: string) {
5+
let fileInContext = forwardSlash(path.relative(context, file));
6+
if (!fileInContext.startsWith('../')) {
7+
fileInContext = './' + fileInContext;
8+
}
9+
10+
return fileInContext;
11+
}
12+
13+
export { relativeToContext };

test/e2e/TypeScriptContextOption.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ describe('TypeScript Context Option', () => {
8888
const errors = await driver.waitForErrors();
8989
expect(errors).toEqual([
9090
[
91-
'ERROR in ../src/model/User.ts:11:16',
91+
'ERROR in ../src/model/User.ts 11:16-25',
9292
"TS2339: Property 'firstName' does not exist on type 'User'.",
9393
' 9 |',
9494
' 10 | function getUserName(user: User): string {',
@@ -99,7 +99,7 @@ describe('TypeScript Context Option', () => {
9999
' 14 | export { User, getUserName };',
100100
].join('\n'),
101101
[
102-
'ERROR in ../src/model/User.ts:11:32',
102+
'ERROR in ../src/model/User.ts 11:32-40',
103103
"TS2339: Property 'lastName' does not exist on type 'User'.",
104104
' 9 |',
105105
' 10 | function getUserName(user: User): string {',

test/e2e/TypeScriptFormatterOption.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ describe('TypeScript Formatter Option', () => {
4040
const errors = await driver.waitForErrors();
4141
expect(errors).toEqual([
4242
[
43-
'ERROR in src/model/User.ts:11:16',
43+
'ERROR in ./src/model/User.ts 11:16-25',
4444
"It is the custom issue statement - TS2339: Property 'firstName' does not exist on type 'User'.",
4545
].join('\n'),
4646
[
47-
'ERROR in src/model/User.ts:11:32',
47+
'ERROR in ./src/model/User.ts 11:32-40',
4848
"It is the custom issue statement - TS2339: Property 'lastName' does not exist on type 'User'.",
4949
].join('\n'),
5050
]);

test/e2e/TypeScriptPnpSupport.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ describe('TypeScript PnP Support', () => {
3131
errors = await driver.waitForErrors();
3232
expect(errors).toEqual([
3333
[
34-
'ERROR in src/model/User.ts:11:16',
34+
'ERROR in ./src/model/User.ts 11:16-25',
3535
"TS2339: Property 'firstName' does not exist on type 'User'.",
3636
' 9 |',
3737
' 10 | function getUserName(user: User): string {',
@@ -42,7 +42,7 @@ describe('TypeScript PnP Support', () => {
4242
' 14 | export { User, getUserName };',
4343
].join('\n'),
4444
[
45-
'ERROR in src/model/User.ts:11:32',
45+
'ERROR in ./src/model/User.ts 11:32-40',
4646
"TS2339: Property 'lastName' does not exist on type 'User'.",
4747
' 9 |',
4848
' 10 | function getUserName(user: User): string {',
@@ -69,7 +69,7 @@ describe('TypeScript PnP Support', () => {
6969
errors = await driver.waitForErrors();
7070
expect(errors).toContain(
7171
[
72-
'ERROR in src/index.ts:1:23',
72+
'ERROR in ./src/index.ts 1:23-39',
7373
"TS2307: Cannot find module './authenticate'.",
7474
" > 1 | import { login } from './authenticate';",
7575
' | ^^^^^^^^^^^^^^^^',
@@ -113,7 +113,7 @@ describe('TypeScript PnP Support', () => {
113113
errors = await driver.waitForErrors();
114114
expect(errors).toEqual([
115115
[
116-
'ERROR in src/index.ts:34:12',
116+
'ERROR in ./src/index.ts 34:12-16',
117117
"TS2339: Property 'role' does not exist on type 'void'.",
118118
' 32 | const user = await login(email, password);',
119119
' 33 |',
@@ -124,7 +124,7 @@ describe('TypeScript PnP Support', () => {
124124
' 37 | console.log(`Logged in as ${getUserName(user)}`);',
125125
].join('\n'),
126126
[
127-
'ERROR in src/index.ts:35:45',
127+
'ERROR in ./src/index.ts 35:45-49',
128128
"TS2345: Argument of type 'void' is not assignable to parameter of type 'User'.",
129129
' 33 |',
130130
" 34 | if (user.role === 'admin') {",
@@ -135,7 +135,7 @@ describe('TypeScript PnP Support', () => {
135135
' 38 | }',
136136
].join('\n'),
137137
[
138-
'ERROR in src/index.ts:37:45',
138+
'ERROR in ./src/index.ts 37:45-49',
139139
"TS2345: Argument of type 'void' is not assignable to parameter of type 'User'.",
140140
' 35 | console.log(`Logged in as ${getUserName(user)} [admin].`);',
141141
' 36 | } else {',

test/e2e/TypeScriptSolutionBuilderApi.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe('TypeScript SolutionBuilder API', () => {
2929
errors = await driver.waitForErrors();
3030
expect(errors).toEqual([
3131
[
32-
'ERROR in packages/shared/src/intersect.ts:2:41',
32+
'ERROR in ./packages/shared/src/intersect.ts 2:41-49',
3333
"TS2339: Property 'includes' does not exist on type 'T'.",
3434
' 1 | function intersect<T>(arrayA: T[] = [], arrayB: T): T[] {',
3535
' > 2 | return arrayA.filter((item) => arrayB.includes(item));',
@@ -51,7 +51,7 @@ describe('TypeScript SolutionBuilder API', () => {
5151
errors = await driver.waitForErrors();
5252
expect(errors).toEqual([
5353
[
54-
'ERROR in packages/client/src/index.ts:4:42',
54+
'ERROR in ./packages/client/src/index.ts 4:42-48',
5555
"TS2345: Argument of type 'T[]' is not assignable to parameter of type 'T'.",
5656
typescript === '~4.0.0'
5757
? " 'T' could be instantiated with an arbitrary type which could be unrelated to 'T[]'."

test/e2e/TypeScriptVueExtension.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe('TypeScript Vue Extension', () => {
6262
errors = await driver.waitForErrors();
6363
expect(errors).toEqual([
6464
[
65-
'ERROR in src/component/LoggedIn.vue:27:23',
65+
'ERROR in ./src/component/LoggedIn.vue 27:23-34',
6666
"TS2304: Cannot find name 'getUserName'.",
6767
' 25 | const user: User = this.user;',
6868
' 26 |',
@@ -90,7 +90,7 @@ describe('TypeScript Vue Extension', () => {
9090
errors = await driver.waitForErrors();
9191
expect(errors).toEqual([
9292
[
93-
'ERROR in src/component/LoggedIn.vue:27:31',
93+
'ERROR in ./src/component/LoggedIn.vue 27:31-40',
9494
"TS2339: Property 'firstName' does not exist on type 'User'.",
9595
' 25 | const user: User = this.user;',
9696
' 26 |',
@@ -101,7 +101,7 @@ describe('TypeScript Vue Extension', () => {
101101
' 30 | async logout() {',
102102
].join('\n'),
103103
[
104-
'ERROR in src/model/User.ts:11:16',
104+
'ERROR in ./src/model/User.ts 11:16-25',
105105
"TS2339: Property 'firstName' does not exist on type 'User'.",
106106
' 9 |',
107107
' 10 | function getUserName(user: User): string {',

test/e2e/TypeScriptWatchApi.spec.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe('TypeScript Watch API', () => {
2929
errors = await driver.waitForErrors();
3030
expect(errors).toEqual([
3131
[
32-
'ERROR in src/index.ts:34:7',
32+
'ERROR in ./src/index.ts 34:7-28',
3333
`TS2367: This condition will always return 'false' since the types 'Role' and '"admin"' have no overlap.`,
3434
' 32 | const user = await login(email, password);',
3535
' 33 |',
@@ -71,7 +71,7 @@ describe('TypeScript Watch API', () => {
7171
);
7272
expect(errors).toEqual([
7373
[
74-
'ERROR in src/model/User.ts:1:22',
74+
'ERROR in ./src/model/User.ts 1:22-30',
7575
"TS2307: Cannot find module './Role' or its corresponding type declarations.",
7676
" > 1 | import { Role } from './Role';",
7777
' | ^^^^^^^^',
@@ -91,7 +91,7 @@ describe('TypeScript Watch API', () => {
9191
errors = await driver.waitForErrors();
9292
expect(errors).toEqual([
9393
[
94-
'ERROR in src/index.ts:34:7',
94+
'ERROR in ./src/index.ts 34:7-31',
9595
"TS2367: This condition will always return 'false' since the types 'Role' and '\"provider\"' have no overlap.",
9696
' 32 | const user = await login(email, password);',
9797
' 33 |',
@@ -132,7 +132,7 @@ describe('TypeScript Watch API', () => {
132132
errors = await driver.waitForErrors();
133133
expect(errors).toEqual([
134134
[
135-
'ERROR in src/index.ts:34:7',
135+
'ERROR in ./src/index.ts 34:7-28',
136136
`TS2367: This condition will always return 'false' since the types 'Role' and '"admin"' have no overlap.`,
137137
' 32 | const user = await login(email, password);',
138138
' 33 |',
@@ -174,7 +174,7 @@ describe('TypeScript Watch API', () => {
174174
);
175175
expect(errors).toEqual([
176176
[
177-
'ERROR in src/model/User.ts:1:22',
177+
'ERROR in ./src/model/User.ts 1:22-30',
178178
"TS2307: Cannot find module './Role' or its corresponding type declarations.",
179179
" > 1 | import { Role } from './Role';",
180180
' | ^^^^^^^^',
@@ -194,7 +194,7 @@ describe('TypeScript Watch API', () => {
194194
errors = await driver.waitForErrors();
195195
expect(errors).toEqual([
196196
[
197-
'ERROR in src/index.ts:34:7',
197+
'ERROR in ./src/index.ts 34:7-31',
198198
"TS2367: This condition will always return 'false' since the types 'Role' and '\"provider\"' have no overlap.",
199199
' 32 | const user = await login(email, password);',
200200
' 33 |',
@@ -238,7 +238,7 @@ describe('TypeScript Watch API', () => {
238238
errors = await driver.waitForErrors();
239239
expect(errors).toEqual([
240240
[
241-
'ERROR in src/model/User.ts:11:16',
241+
'ERROR in ./src/model/User.ts 11:16-25',
242242
"TS2339: Property 'firstName' does not exist on type 'User'.",
243243
' 9 |',
244244
' 10 | function getUserName(user: User): string {',
@@ -249,7 +249,7 @@ describe('TypeScript Watch API', () => {
249249
' 14 | export { User, getUserName };',
250250
].join('\n'),
251251
[
252-
'ERROR in src/model/User.ts:11:32',
252+
'ERROR in ./src/model/User.ts 11:32-40',
253253
"TS2339: Property 'lastName' does not exist on type 'User'.",
254254
' 9 |',
255255
' 10 | function getUserName(user: User): string {',
@@ -276,7 +276,7 @@ describe('TypeScript Watch API', () => {
276276
errors = await driver.waitForErrors();
277277
expect(errors).toContain(
278278
[
279-
'ERROR in src/index.ts:1:23',
279+
'ERROR in ./src/index.ts 1:23-39',
280280
"TS2307: Cannot find module './authenticate'.",
281281
" > 1 | import { login } from './authenticate';",
282282
' | ^^^^^^^^^^^^^^^^',
@@ -320,7 +320,7 @@ describe('TypeScript Watch API', () => {
320320
errors = await driver.waitForErrors();
321321
expect(errors).toEqual([
322322
[
323-
'ERROR in src/index.ts:34:12',
323+
'ERROR in ./src/index.ts 34:12-16',
324324
"TS2339: Property 'role' does not exist on type 'void'.",
325325
' 32 | const user = await login(email, password);',
326326
' 33 |',
@@ -331,7 +331,7 @@ describe('TypeScript Watch API', () => {
331331
' 37 | console.log(`Logged in as ${getUserName(user)}`);',
332332
].join('\n'),
333333
[
334-
'ERROR in src/index.ts:35:45',
334+
'ERROR in ./src/index.ts 35:45-49',
335335
"TS2345: Argument of type 'void' is not assignable to parameter of type 'User'.",
336336
' 33 |',
337337
" 34 | if (user.role === 'admin') {",
@@ -342,7 +342,7 @@ describe('TypeScript Watch API', () => {
342342
' 38 | }',
343343
].join('\n'),
344344
[
345-
'ERROR in src/index.ts:37:45',
345+
'ERROR in ./src/index.ts 37:45-49',
346346
"TS2345: Argument of type 'void' is not assignable to parameter of type 'User'.",
347347
' 35 | console.log(`Logged in as ${getUserName(user)} [admin].`);',
348348
' 36 | } else {',

0 commit comments

Comments
 (0)