Skip to content

Commit 2b4e793

Browse files
authored
fix: caret position was incorrect (#14984)
1 parent c61c376 commit 2b4e793

File tree

7 files changed

+184
-31
lines changed

7 files changed

+184
-31
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`generateCodeFrames > end 1`] = `
4+
"
5+
1 | import foo from './foo'
6+
| ^
7+
2 | foo()
8+
"
9+
`;
10+
11+
exports[`generateCodeFrames > end 2`] = `
12+
"
13+
1 | import foo from './foo'
14+
| ^^^^^^^^^^^^^^^^^^^^^^^
15+
2 | foo()
16+
"
17+
`;
18+
19+
exports[`generateCodeFrames > end 3`] = `
20+
"
21+
1 | import foo from './foo'
22+
| ^^^^^^^^^^^^^^^^^^^^^^^
23+
2 | foo()
24+
| ^^^^^
25+
"
26+
`;
27+
28+
exports[`generateCodeFrames > range 1`] = `
29+
"
30+
1 |
31+
2 | import foo from './foo'
32+
3 |
33+
| ^
34+
4 | foo()
35+
5 |
36+
"
37+
`;
38+
39+
exports[`generateCodeFrames > start with number 1`] = `
40+
"
41+
1 | import foo from './foo'
42+
| ^
43+
2 | foo()
44+
"
45+
`;
46+
47+
exports[`generateCodeFrames > start with number 2`] = `
48+
"
49+
1 | import foo from './foo'
50+
| ^
51+
2 | foo()
52+
"
53+
`;
54+
55+
exports[`generateCodeFrames > start with number 3`] = `
56+
"
57+
1 | import foo from './foo'
58+
2 | foo()
59+
| ^
60+
"
61+
`;
62+
63+
exports[`generateCodeFrames > start with postion 1`] = `
64+
"
65+
1 | import foo from './foo'
66+
| ^
67+
2 | foo()
68+
"
69+
`;
70+
71+
exports[`generateCodeFrames > start with postion 2`] = `
72+
"
73+
1 | import foo from './foo'
74+
| ^
75+
2 | foo()
76+
"
77+
`;
78+
79+
exports[`generateCodeFrames > start with postion 3`] = `
80+
"
81+
1 | import foo from './foo'
82+
2 | foo()
83+
| ^
84+
"
85+
`;
86+
87+
exports[`generateCodeFrames > works with CRLF 1`] = `
88+
"
89+
1 | import foo from './foo'
90+
2 | foo()
91+
| ^
92+
"
93+
`;

packages/vite/src/node/__tests__/utils.spec.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
asyncFlatten,
66
bareImportRE,
77
flattenId,
8+
generateCodeFrame,
89
getHash,
910
getLocalhostAddressIfDiffersFromDNS,
1011
injectQuery,
@@ -176,6 +177,59 @@ describe('posToNumber', () => {
176177
})
177178
})
178179

180+
describe('generateCodeFrames', () => {
181+
const source = `
182+
import foo from './foo'
183+
foo()
184+
`.trim()
185+
const sourceCrLf = source.replace(/\n/, '\r\n')
186+
const longSource = `
187+
import foo from './foo'
188+
189+
foo()
190+
191+
// bar
192+
// baz
193+
`
194+
195+
const expectSnapshot = (value: string) => {
196+
try {
197+
// add new line to make snapshot easier to read
198+
expect('\n' + value + '\n').toMatchSnapshot()
199+
} catch (e) {
200+
// don't include this function in stacktrace
201+
Error.captureStackTrace(e, expectSnapshot)
202+
throw e
203+
}
204+
}
205+
206+
test('start with number', () => {
207+
expectSnapshot(generateCodeFrame(source, 0))
208+
expectSnapshot(generateCodeFrame(source, 1))
209+
expectSnapshot(generateCodeFrame(source, 24))
210+
})
211+
212+
test('start with postion', () => {
213+
expectSnapshot(generateCodeFrame(source, { line: 1, column: 0 }))
214+
expectSnapshot(generateCodeFrame(source, { line: 1, column: 1 }))
215+
expectSnapshot(generateCodeFrame(source, { line: 2, column: 0 }))
216+
})
217+
218+
test('works with CRLF', () => {
219+
expectSnapshot(generateCodeFrame(sourceCrLf, { line: 2, column: 0 }))
220+
})
221+
222+
test('end', () => {
223+
expectSnapshot(generateCodeFrame(source, 0, 0))
224+
expectSnapshot(generateCodeFrame(source, 0, 23))
225+
expectSnapshot(generateCodeFrame(source, 0, 29))
226+
})
227+
228+
test('range', () => {
229+
expectSnapshot(generateCodeFrame(longSource, { line: 3, column: 0 }))
230+
})
231+
})
232+
179233
describe('getHash', () => {
180234
test('8-digit hex', () => {
181235
const hash = getHash(Buffer.alloc(0))

packages/vite/src/node/plugins/css.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,13 +1185,21 @@ async function compileCSS(
11851185
deps.add(files[i])
11861186
}
11871187
} else if (message.type === 'warning') {
1188-
let msg = `[vite:css] ${message.text}`
1189-
if (message.line && message.column) {
1190-
msg += `\n${generateCodeFrame(code, {
1191-
line: message.line,
1192-
column: message.column,
1193-
})}`
1194-
}
1188+
const warning = message as PostCSS.Warning
1189+
let msg = `[vite:css] ${warning.text}`
1190+
msg += `\n${generateCodeFrame(
1191+
code,
1192+
{
1193+
line: warning.line,
1194+
column: warning.column,
1195+
},
1196+
warning.endLine !== undefined && warning.endColumn !== undefined
1197+
? {
1198+
line: warning.endLine,
1199+
column: warning.endColumn,
1200+
}
1201+
: undefined,
1202+
)}`
11951203
config.logger.warn(colors.yellow(msg))
11961204
}
11971205
}

packages/vite/src/node/plugins/esbuild.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -433,15 +433,7 @@ export function resolveEsbuildTranspileOptions(
433433
function prettifyMessage(m: Message, code: string): string {
434434
let res = colors.yellow(m.text)
435435
if (m.location) {
436-
const lines = code.split(/\r?\n/g)
437-
const line = Number(m.location.line)
438-
const column = Number(m.location.column)
439-
const offset =
440-
lines
441-
.slice(0, line - 1)
442-
.map((l) => l.length)
443-
.reduce((total, l) => total + l + 1, 0) + column
444-
res += `\n` + generateCodeFrame(code, offset, offset + 1)
436+
res += `\n` + generateCodeFrame(code, m.location)
445437
}
446438
return res + `\n`
447439
}

packages/vite/src/node/plugins/html.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,11 @@ function formatParseError(parserError: ParserError, id: string, html: string) {
244244
const formattedError = {
245245
code: parserError.code,
246246
message: `parse5 error code ${parserError.code}`,
247-
frame: generateCodeFrame(html, parserError.startOffset),
247+
frame: generateCodeFrame(
248+
html,
249+
parserError.startOffset,
250+
parserError.endOffset,
251+
),
248252
loc: {
249253
file: id,
250254
line: parserError.startLine,

packages/vite/src/node/plugins/importAnalysis.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
640640
`\n` +
641641
colors.cyan(importerModule.file) +
642642
`\n` +
643-
colors.reset(generateCodeFrame(source, start)) +
643+
colors.reset(generateCodeFrame(source, start, end)) +
644644
colors.yellow(
645645
`\nThe above dynamic import cannot be analyzed by Vite.\n` +
646646
`See ${colors.blue(

packages/vite/src/node/utils.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -460,10 +460,14 @@ export function pad(source: string, n = 2): string {
460460
return lines.map((l) => ` `.repeat(n) + l).join(`\n`)
461461
}
462462

463-
export function posToNumber(
464-
source: string,
465-
pos: number | { line: number; column: number },
466-
): number {
463+
type Pos = {
464+
/** 1-based */
465+
line: number
466+
/** 0-based */
467+
column: number
468+
}
469+
470+
export function posToNumber(source: string, pos: number | Pos): number {
467471
if (typeof pos === 'number') return pos
468472
const lines = source.split(splitRE)
469473
const { line, column } = pos
@@ -474,10 +478,7 @@ export function posToNumber(
474478
return start + column
475479
}
476480

477-
export function numberToPos(
478-
source: string,
479-
offset: number | { line: number; column: number },
480-
): { line: number; column: number } {
481+
export function numberToPos(source: string, offset: number | Pos): Pos {
481482
if (typeof offset !== 'number') return offset
482483
if (offset > source.length) {
483484
throw new Error(
@@ -501,16 +502,16 @@ export function numberToPos(
501502

502503
export function generateCodeFrame(
503504
source: string,
504-
start: number | { line: number; column: number } = 0,
505-
end?: number,
505+
start: number | Pos = 0,
506+
end?: number | Pos,
506507
): string {
507508
start = posToNumber(source, start)
508-
end = end || start
509+
end = end !== undefined ? posToNumber(source, end) : start
509510
const lines = source.split(splitRE)
510511
let count = 0
511512
const res: string[] = []
512513
for (let i = 0; i < lines.length; i++) {
513-
count += lines[i].length + 1
514+
count += lines[i].length
514515
if (count >= start) {
515516
for (let j = i - range; j <= i + range || end > count; j++) {
516517
if (j < 0 || j >= lines.length) continue
@@ -523,7 +524,7 @@ export function generateCodeFrame(
523524
const lineLength = lines[j].length
524525
if (j === i) {
525526
// push underline
526-
const pad = Math.max(start - (count - lineLength) + 1, 0)
527+
const pad = Math.max(start - (count - lineLength), 0)
527528
const length = Math.max(
528529
1,
529530
end > count ? lineLength - pad : end - start,
@@ -539,6 +540,7 @@ export function generateCodeFrame(
539540
}
540541
break
541542
}
543+
count++
542544
}
543545
return res.join('\n')
544546
}

0 commit comments

Comments
 (0)