From 7f92698de6dba643562228aece3f3ea392f57a8a Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 15 Nov 2023 00:09:06 +0900 Subject: [PATCH 1/8] test: add test for generateCodeFrame --- .../__snapshots__/utils.spec.ts.snap | 84 +++++++++++++++++++ .../vite/src/node/__tests__/utils.spec.ts | 53 ++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 packages/vite/src/node/__tests__/__snapshots__/utils.spec.ts.snap diff --git a/packages/vite/src/node/__tests__/__snapshots__/utils.spec.ts.snap b/packages/vite/src/node/__tests__/__snapshots__/utils.spec.ts.snap new file mode 100644 index 00000000000000..4c7f4863beb4b9 --- /dev/null +++ b/packages/vite/src/node/__tests__/__snapshots__/utils.spec.ts.snap @@ -0,0 +1,84 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`generateCodeFrames > end 1`] = ` +" +1 | import foo from './foo' + | ^^^^^^^^^^^^^^^^^^^^^^^ +2 | foo() +" +`; + +exports[`generateCodeFrames > end 2`] = ` +" +1 | import foo from './foo' + | ^^^^^^^^^^^^^^^^^^^^^^^ +2 | foo() + | ^^^^^ +" +`; + +exports[`generateCodeFrames > range 1`] = ` +" +1 | +2 | import foo from './foo' + | ^ +3 | +4 | foo() +" +`; + +exports[`generateCodeFrames > start with number 1`] = ` +" +1 | import foo from './foo' + | ^ +2 | foo() +" +`; + +exports[`generateCodeFrames > start with number 2`] = ` +" +1 | import foo from './foo' + | ^ +2 | foo() +" +`; + +exports[`generateCodeFrames > start with number 3`] = ` +" +1 | import foo from './foo' + | ^ +2 | foo() +" +`; + +exports[`generateCodeFrames > start with postion 1`] = ` +" +1 | import foo from './foo' + | ^ +2 | foo() +" +`; + +exports[`generateCodeFrames > start with postion 2`] = ` +" +1 | import foo from './foo' + | ^ +2 | foo() +" +`; + +exports[`generateCodeFrames > start with postion 3`] = ` +" +1 | import foo from './foo' + | ^ +2 | foo() +" +`; + +exports[`generateCodeFrames > works with CRLF 1`] = ` +" +1 | import foo from './foo' + | ^ +2 | foo() +" +`; diff --git a/packages/vite/src/node/__tests__/utils.spec.ts b/packages/vite/src/node/__tests__/utils.spec.ts index 18d55b9f18b6fc..b432729b5ba667 100644 --- a/packages/vite/src/node/__tests__/utils.spec.ts +++ b/packages/vite/src/node/__tests__/utils.spec.ts @@ -5,6 +5,7 @@ import { asyncFlatten, bareImportRE, flattenId, + generateCodeFrame, getHash, getLocalhostAddressIfDiffersFromDNS, injectQuery, @@ -176,6 +177,58 @@ describe('posToNumber', () => { }) }) +describe('generateCodeFrames', () => { + const source = ` +import foo from './foo' +foo() +`.trim() + const sourceCrLf = source.replace(/\n/, '\r\n') + const longSource = ` +import foo from './foo' + +foo() + +// bar +// baz + ` + + const expectSnapshot = (value: string) => { + try { + // add new line to make snapshot easier to read + expect('\n' + value + '\n').toMatchSnapshot() + } catch (e) { + // don't include this function in stacktrace + Error.captureStackTrace(e, expectSnapshot) + throw e + } + } + + test('start with number', () => { + expectSnapshot(generateCodeFrame(source, 0)) + expectSnapshot(generateCodeFrame(source, 1)) + expectSnapshot(generateCodeFrame(source, 24)) + }) + + test('start with postion', () => { + expectSnapshot(generateCodeFrame(source, { line: 1, column: 0 })) + expectSnapshot(generateCodeFrame(source, { line: 1, column: 1 })) + expectSnapshot(generateCodeFrame(source, { line: 2, column: 0 })) + }) + + test('works with CRLF', () => { + expectSnapshot(generateCodeFrame(sourceCrLf, { line: 2, column: 0 })) + }) + + test('end', () => { + expectSnapshot(generateCodeFrame(source, 0, 23)) + expectSnapshot(generateCodeFrame(source, 0, 29)) + }) + + test('range', () => { + expectSnapshot(generateCodeFrame(longSource, { line: 3, column: 0 })) + }) +}) + describe('getHash', () => { test('8-digit hex', () => { const hash = getHash(Buffer.alloc(0)) From c0cac2a0f73d634a28a337ec95587e1e6cf421cd Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 15 Nov 2023 00:11:20 +0900 Subject: [PATCH 2/8] refactor: extract Pos type and clarify n-based --- packages/vite/src/node/utils.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 1197693d7a5cc1..9399e5c8249a30 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -460,10 +460,14 @@ export function pad(source: string, n = 2): string { return lines.map((l) => ` `.repeat(n) + l).join(`\n`) } -export function posToNumber( - source: string, - pos: number | { line: number; column: number }, -): number { +type Pos = { + /** 1-based */ + line: number + /** 0-based */ + column: number +} + +export function posToNumber(source: string, pos: number | Pos): number { if (typeof pos === 'number') return pos const lines = source.split(splitRE) const { line, column } = pos @@ -474,10 +478,7 @@ export function posToNumber( return start + column } -export function numberToPos( - source: string, - offset: number | { line: number; column: number }, -): { line: number; column: number } { +export function numberToPos(source: string, offset: number | Pos): Pos { if (typeof offset !== 'number') return offset if (offset > source.length) { throw new Error( @@ -501,7 +502,7 @@ export function numberToPos( export function generateCodeFrame( source: string, - start: number | { line: number; column: number } = 0, + start: number | Pos = 0, end?: number, ): string { start = posToNumber(source, start) From 37f5786018e8f2d258f60fc795771e8759cabd3c Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 15 Nov 2023 00:13:16 +0900 Subject: [PATCH 3/8] fix: caret position was incorrect --- .../src/node/__tests__/__snapshots__/utils.spec.ts.snap | 9 +++++---- packages/vite/src/node/utils.ts | 5 +++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/vite/src/node/__tests__/__snapshots__/utils.spec.ts.snap b/packages/vite/src/node/__tests__/__snapshots__/utils.spec.ts.snap index 4c7f4863beb4b9..006c9ca3274460 100644 --- a/packages/vite/src/node/__tests__/__snapshots__/utils.spec.ts.snap +++ b/packages/vite/src/node/__tests__/__snapshots__/utils.spec.ts.snap @@ -21,9 +21,10 @@ exports[`generateCodeFrames > range 1`] = ` " 1 | 2 | import foo from './foo' - | ^ 3 | + | ^ 4 | foo() +5 | " `; @@ -46,8 +47,8 @@ exports[`generateCodeFrames > start with number 2`] = ` exports[`generateCodeFrames > start with number 3`] = ` " 1 | import foo from './foo' - | ^ 2 | foo() + | ^ " `; @@ -70,15 +71,15 @@ exports[`generateCodeFrames > start with postion 2`] = ` exports[`generateCodeFrames > start with postion 3`] = ` " 1 | import foo from './foo' - | ^ 2 | foo() + | ^ " `; exports[`generateCodeFrames > works with CRLF 1`] = ` " 1 | import foo from './foo' - | ^ 2 | foo() + | ^ " `; diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 9399e5c8249a30..39c13a658b733e 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -511,7 +511,7 @@ export function generateCodeFrame( let count = 0 const res: string[] = [] for (let i = 0; i < lines.length; i++) { - count += lines[i].length + 1 + count += lines[i].length if (count >= start) { for (let j = i - range; j <= i + range || end > count; j++) { if (j < 0 || j >= lines.length) continue @@ -524,7 +524,7 @@ export function generateCodeFrame( const lineLength = lines[j].length if (j === i) { // push underline - const pad = Math.max(start - (count - lineLength) + 1, 0) + const pad = Math.max(start - (count - lineLength), 0) const length = Math.max( 1, end > count ? lineLength - pad : end - start, @@ -540,6 +540,7 @@ export function generateCodeFrame( } break } + count++ } return res.join('\n') } From 68486ae8d49d517c0493d64acfd87c754073e39f Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 15 Nov 2023 00:57:57 +0900 Subject: [PATCH 4/8] fix: esbuild codeFrame was too long --- .../node/__tests__/__snapshots__/utils.spec.ts.snap | 10 +++++++++- packages/vite/src/node/__tests__/utils.spec.ts | 1 + packages/vite/src/node/plugins/esbuild.ts | 10 +--------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/vite/src/node/__tests__/__snapshots__/utils.spec.ts.snap b/packages/vite/src/node/__tests__/__snapshots__/utils.spec.ts.snap index 006c9ca3274460..69d8fe2143be4c 100644 --- a/packages/vite/src/node/__tests__/__snapshots__/utils.spec.ts.snap +++ b/packages/vite/src/node/__tests__/__snapshots__/utils.spec.ts.snap @@ -3,13 +3,21 @@ exports[`generateCodeFrames > end 1`] = ` " 1 | import foo from './foo' - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^ 2 | foo() " `; exports[`generateCodeFrames > end 2`] = ` " +1 | import foo from './foo' + | ^^^^^^^^^^^^^^^^^^^^^^^ +2 | foo() +" +`; + +exports[`generateCodeFrames > end 3`] = ` +" 1 | import foo from './foo' | ^^^^^^^^^^^^^^^^^^^^^^^ 2 | foo() diff --git a/packages/vite/src/node/__tests__/utils.spec.ts b/packages/vite/src/node/__tests__/utils.spec.ts index b432729b5ba667..05bb953e239651 100644 --- a/packages/vite/src/node/__tests__/utils.spec.ts +++ b/packages/vite/src/node/__tests__/utils.spec.ts @@ -220,6 +220,7 @@ foo() }) test('end', () => { + expectSnapshot(generateCodeFrame(source, 0, 0)) expectSnapshot(generateCodeFrame(source, 0, 23)) expectSnapshot(generateCodeFrame(source, 0, 29)) }) diff --git a/packages/vite/src/node/plugins/esbuild.ts b/packages/vite/src/node/plugins/esbuild.ts index 65d173f5b15266..6f37877e6b07aa 100644 --- a/packages/vite/src/node/plugins/esbuild.ts +++ b/packages/vite/src/node/plugins/esbuild.ts @@ -433,15 +433,7 @@ export function resolveEsbuildTranspileOptions( function prettifyMessage(m: Message, code: string): string { let res = colors.yellow(m.text) if (m.location) { - const lines = code.split(/\r?\n/g) - const line = Number(m.location.line) - const column = Number(m.location.column) - const offset = - lines - .slice(0, line - 1) - .map((l) => l.length) - .reduce((total, l) => total + l + 1, 0) + column - res += `\n` + generateCodeFrame(code, offset, offset + 1) + res += `\n` + generateCodeFrame(code, m.location) } return res + `\n` } From 2d348c9e8e0288506c8b206b3bac9312a4562e18 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 15 Nov 2023 01:02:50 +0900 Subject: [PATCH 5/8] feat: add end position to code frame --- packages/vite/src/node/plugins/css.ts | 22 +++++++++++++------ packages/vite/src/node/plugins/html.ts | 6 ++++- .../vite/src/node/plugins/importAnalysis.ts | 2 +- packages/vite/src/node/utils.ts | 4 ++-- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index c7c5bd4bf9cacd..1fdc0e204e4793 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -1185,13 +1185,21 @@ async function compileCSS( deps.add(files[i]) } } else if (message.type === 'warning') { - let msg = `[vite:css] ${message.text}` - if (message.line && message.column) { - msg += `\n${generateCodeFrame(code, { - line: message.line, - column: message.column, - })}` - } + const warning = message as PostCSS.Warning + let msg = `[vite:css] ${warning.text}` + msg += `\n${generateCodeFrame( + code, + { + line: warning.line, + column: warning.column, + }, + warning.endLine !== undefined && warning.endColumn !== undefined + ? { + line: warning.endLine, + column: warning.endColumn, + } + : undefined, + )}` config.logger.warn(colors.yellow(msg)) } } diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index f12531bdc2c698..1912143b70fdde 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -244,7 +244,11 @@ function formatParseError(parserError: ParserError, id: string, html: string) { const formattedError = { code: parserError.code, message: `parse5 error code ${parserError.code}`, - frame: generateCodeFrame(html, parserError.startOffset), + frame: generateCodeFrame( + html, + parserError.startOffset, + parserError.endOffset, + ), loc: { file: id, line: parserError.startLine, diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index 18dbebf0376e39..528b68a221dcc5 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -640,7 +640,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { `\n` + colors.cyan(importerModule.file) + `\n` + - colors.reset(generateCodeFrame(source, start)) + + colors.reset(generateCodeFrame(source, start, end)) + colors.yellow( `\nThe above dynamic import cannot be analyzed by Vite.\n` + `See ${colors.blue( diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 39c13a658b733e..64526eb3ebd3ea 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -503,10 +503,10 @@ export function numberToPos(source: string, offset: number | Pos): Pos { export function generateCodeFrame( source: string, start: number | Pos = 0, - end?: number, + end?: number | Pos, ): string { start = posToNumber(source, start) - end = end || start + end = end !== undefined ? posToNumber(source, end) : start const lines = source.split(splitRE) let count = 0 const res: string[] = [] From 8692b60c7613fec5f3b9d6424b4307b000d90de8 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 15 Nov 2023 01:24:03 +0900 Subject: [PATCH 6/8] fix: code frame was not generated for postcss errors --- packages/vite/src/node/plugins/css.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 1fdc0e204e4793..577fd8a978d63f 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -1207,6 +1207,7 @@ async function compileCSS( e.message = `[postcss] ${e.message}` e.code = code e.loc = { + file: e.file, column: e.column, line: e.line, } From e2a3a846221ea107f71fbabd3aaf7fe1f505ee12 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 15 Nov 2023 01:24:37 +0900 Subject: [PATCH 7/8] refactor: revert #6293 --- packages/vite/src/node/server/pluginContainer.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/vite/src/node/server/pluginContainer.ts b/packages/vite/src/node/server/pluginContainer.ts index 82f1d8d5d77310..bf8c6337f8bc49 100644 --- a/packages/vite/src/node/server/pluginContainer.ts +++ b/packages/vite/src/node/server/pluginContainer.ts @@ -62,7 +62,6 @@ import { TraceMap, originalPositionFor } from '@jridgewell/trace-mapping' import MagicString from 'magic-string' import type { FSWatcher } from 'chokidar' import colors from 'picocolors' -import type * as postcss from 'postcss' import type { Plugin } from '../plugin' import { cleanUrl, @@ -429,15 +428,10 @@ export async function createPluginContainer( position: number | { column: number; line: number } | undefined, ctx: Context, ) { - const err = ( - typeof e === 'string' ? new Error(e) : e - ) as postcss.CssSyntaxError & RollupError + const err = (typeof e === 'string' ? new Error(e) : e) as RollupError if (err.pluginCode) { return err // The plugin likely called `this.error` } - if (err.file && err.name === 'CssSyntaxError') { - err.id = normalizePath(err.file) - } if (ctx._activePlugin) err.plugin = ctx._activePlugin.name if (ctx._activeId && !err.id) err.id = ctx._activeId if (ctx._activeCode) { @@ -483,7 +477,7 @@ export async function createPluginContainer( line: (err as any).line, column: (err as any).column, } - err.frame = err.frame || generateCodeFrame(err.id!, err.loc) + err.frame = err.frame || generateCodeFrame(ctx._activeCode, err.loc) } if ( From 8e22e3034b8ac9970ed8d0d04a57f55aee45c560 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 15 Nov 2023 01:24:57 +0900 Subject: [PATCH 8/8] fix: postcss column is 1-based --- packages/vite/src/node/plugins/css.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 577fd8a978d63f..2f91ea93bacac0 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -1191,12 +1191,12 @@ async function compileCSS( code, { line: warning.line, - column: warning.column, + column: warning.column - 1, // 1-based }, warning.endLine !== undefined && warning.endColumn !== undefined ? { line: warning.endLine, - column: warning.endColumn, + column: warning.endColumn - 1, // 1-based } : undefined, )}` @@ -1208,8 +1208,8 @@ async function compileCSS( e.code = code e.loc = { file: e.file, - column: e.column, line: e.line, + column: e.column - 1, // 1-based } throw e }