From 887dd6311867363f2c4428ac7b7f5abaecf12ea8 Mon Sep 17 00:00:00 2001 From: MK Date: Tue, 12 May 2026 16:08:43 +0800 Subject: [PATCH 1/3] fix(test): preserve string-literal tabs in convertTabsToSpaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The build step `convertTabsToSpaces()` did a blanket `\t` → 2-space rewrite over every dist .js file. Upstream `@vitest/snapshot` decides multi-line inline snapshot indentation via `indent.includes("\t")`, where the `"\t"` is a literal tab byte in the bundled source. The blanket replace turned that into `indent.includes(" ")`, so every 2-space indent matched and the tab-appending branch always ran — producing tab-indented snapshots in 2-space files. Restrict the replacement to leading tabs only via `/^\t+/gm`. Indentation is still normalized (downstream patches that rely on space indent keep working), but tabs inside string and template literals are now preserved. Adds two regression tests: - `packages/test/__tests__/build-artifacts.spec.ts` asserts the bundled snapshot file keeps the literal tab inside `indent.includes("\t")`. - `packages/cli/snap-tests/test-inline-snapshot-indent/` reproduces the issue's exact `vp test --update` flow on a 2-space indented file and captures the rewritten content. Closes #1553 --- .../test-inline-snapshot-indent/package.json | 4 ++++ .../test-inline-snapshot-indent/snap.txt | 23 ++++++++++++++++++ .../src/inline-snapshot.test.ts | 7 ++++++ .../test-inline-snapshot-indent/steps.json | 6 +++++ .../test/__tests__/build-artifacts.spec.ts | 24 +++++++++++++++++++ packages/test/build.ts | 21 ++++++++++++---- 6 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 packages/cli/snap-tests/test-inline-snapshot-indent/package.json create mode 100644 packages/cli/snap-tests/test-inline-snapshot-indent/snap.txt create mode 100644 packages/cli/snap-tests/test-inline-snapshot-indent/src/inline-snapshot.test.ts create mode 100644 packages/cli/snap-tests/test-inline-snapshot-indent/steps.json diff --git a/packages/cli/snap-tests/test-inline-snapshot-indent/package.json b/packages/cli/snap-tests/test-inline-snapshot-indent/package.json new file mode 100644 index 0000000000..d007f670b3 --- /dev/null +++ b/packages/cli/snap-tests/test-inline-snapshot-indent/package.json @@ -0,0 +1,4 @@ +{ + "name": "test-inline-snapshot-indent", + "private": true +} diff --git a/packages/cli/snap-tests/test-inline-snapshot-indent/snap.txt b/packages/cli/snap-tests/test-inline-snapshot-indent/snap.txt new file mode 100644 index 0000000000..2fb66bcc52 --- /dev/null +++ b/packages/cli/snap-tests/test-inline-snapshot-indent/snap.txt @@ -0,0 +1,23 @@ +> vp test run -u src/inline-snapshot.test.ts # write inline snapshot via --update (regression test for #1553) + RUN + + ✓ src/inline-snapshot.test.ts (1 test) ms + + Snapshots 1 written + Test Files 1 passed (1) + Tests 1 passed (1) + Start at + Duration ms (transform ms, setup ms, import ms, tests ms, environment ms) + + +> cat src/inline-snapshot.test.ts # snapshot must use 2-space indentation, not tabs +import { describe, expect, it } from 'vite-plus/test'; + +describe('inline snapshot indentation', () => { + it('writes multiline snapshots using the surrounding file indentation style', () => { + expect('alpha\nbeta').toMatchInlineSnapshot(` + "alpha + beta" + `); + }); +}); diff --git a/packages/cli/snap-tests/test-inline-snapshot-indent/src/inline-snapshot.test.ts b/packages/cli/snap-tests/test-inline-snapshot-indent/src/inline-snapshot.test.ts new file mode 100644 index 0000000000..1e6d7eb2bb --- /dev/null +++ b/packages/cli/snap-tests/test-inline-snapshot-indent/src/inline-snapshot.test.ts @@ -0,0 +1,7 @@ +import { describe, expect, it } from 'vite-plus/test'; + +describe('inline snapshot indentation', () => { + it('writes multiline snapshots using the surrounding file indentation style', () => { + expect('alpha\nbeta').toMatchInlineSnapshot(); + }); +}); diff --git a/packages/cli/snap-tests/test-inline-snapshot-indent/steps.json b/packages/cli/snap-tests/test-inline-snapshot-indent/steps.json new file mode 100644 index 0000000000..95763b2aad --- /dev/null +++ b/packages/cli/snap-tests/test-inline-snapshot-indent/steps.json @@ -0,0 +1,6 @@ +{ + "commands": [ + "vp test run -u src/inline-snapshot.test.ts # write inline snapshot via --update (regression test for #1553)", + "cat src/inline-snapshot.test.ts # snapshot must use 2-space indentation, not tabs" + ] +} diff --git a/packages/test/__tests__/build-artifacts.spec.ts b/packages/test/__tests__/build-artifacts.spec.ts index 0767207323..e14e02a783 100644 --- a/packages/test/__tests__/build-artifacts.spec.ts +++ b/packages/test/__tests__/build-artifacts.spec.ts @@ -65,6 +65,30 @@ describe('build artifacts', () => { }); }); + /** + * `convertTabsToSpaces()` in build.ts must not touch tabs inside string + * literals. Upstream `@vitest/snapshot` decides multi-line snapshot + * indentation via `indent.includes("\t")` — where `"\t"` is a literal + * tab byte in the bundled source. A blanket tab→spaces rewrite turned + * this into `indent.includes(" ")`, so every 2-space indent matched + * and the tab-appending branch always ran, producing tab-indented + * snapshots in 2-space files. + * + * See: https://github.com/voidzero-dev/vite-plus/issues/1553 + */ + describe('snapshot indent check (regression test for #1553)', () => { + const snapshotIndexPath = path.join(distDir, '@vitest/snapshot/index.js'); + + it('preserves the literal tab byte inside the indent.includes string', () => { + const content = fs.readFileSync(snapshotIndexPath, 'utf-8'); + // The bundled snapshot logic must still test for a tab character. + // If the string argument is " " instead of "\t", every 2-space + // indent matches and snapshots get tab-indented. + expect(content).toContain('indent.includes("\t")'); + expect(content).not.toMatch(/indent\.includes\(" "\)/); + }); + }); + /** * Third-party packages that call `expect.extend()` internally * (e.g., @testing-library/jest-dom) break under npm override because diff --git a/packages/test/build.ts b/packages/test/build.ts index 444963eec7..4712d6a7fb 100644 --- a/packages/test/build.ts +++ b/packages/test/build.ts @@ -1365,18 +1365,29 @@ async function patchVitestCoreResolver() { } /** - * Convert tabs to spaces in all JS files in dist/ for consistent formatting. - * This allows our patching code to use space-based patterns instead of tabs. + * Convert leading tabs to spaces in all JS files in dist/ for consistent + * formatting. This allows our patching code to use space-based patterns + * instead of tabs. + * + * Only leading whitespace is rewritten — tabs inside string or template + * literals are semantically meaningful (e.g. `indent.includes("\t")` in + * @vitest/snapshot picks the snapshot indent style by checking for a + * literal tab byte) and must be preserved. + * + * See: https://github.com/voidzero-dev/vite-plus/issues/1553 */ async function convertTabsToSpaces() { - console.log('\nConverting tabs to spaces in dist/...'); + console.log('\nConverting leading tabs to spaces in dist/...'); let convertedCount = 0; for await (const file of fsGlob(resolve(distDir, '**/*.js'))) { const content = await readFile(file, 'utf-8'); - if (content.includes('\t')) { - const converted = content.replace(/\t/g, ' '); + if (!/^\t/m.test(content)) { + continue; + } + const converted = content.replace(/^\t+/gm, (match) => ' '.repeat(match.length)); + if (converted !== content) { await writeFile(file, converted); convertedCount++; } From 294417e8b9b374c70b09e42b3e67cd5ee5325cfa Mon Sep 17 00:00:00 2001 From: MK Date: Tue, 12 May 2026 16:16:41 +0800 Subject: [PATCH 2/3] refactor(test): drop redundant guard and duplicate comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pre-check `if (!/^\t/m.test(content)) continue` in convertTabsToSpaces duplicates the work that `replace()` already performs, and the subsequent `if (converted !== content)` guard already short-circuits no-op writes. Drop the pre-check. The inline comment inside the #1553 regression test restated the same reasoning as the describe-level JSDoc — drop the inline copy. --- packages/test/__tests__/build-artifacts.spec.ts | 3 --- packages/test/build.ts | 3 --- 2 files changed, 6 deletions(-) diff --git a/packages/test/__tests__/build-artifacts.spec.ts b/packages/test/__tests__/build-artifacts.spec.ts index e14e02a783..641b98260c 100644 --- a/packages/test/__tests__/build-artifacts.spec.ts +++ b/packages/test/__tests__/build-artifacts.spec.ts @@ -81,9 +81,6 @@ describe('build artifacts', () => { it('preserves the literal tab byte inside the indent.includes string', () => { const content = fs.readFileSync(snapshotIndexPath, 'utf-8'); - // The bundled snapshot logic must still test for a tab character. - // If the string argument is " " instead of "\t", every 2-space - // indent matches and snapshots get tab-indented. expect(content).toContain('indent.includes("\t")'); expect(content).not.toMatch(/indent\.includes\(" "\)/); }); diff --git a/packages/test/build.ts b/packages/test/build.ts index 4712d6a7fb..dd313749cf 100644 --- a/packages/test/build.ts +++ b/packages/test/build.ts @@ -1383,9 +1383,6 @@ async function convertTabsToSpaces() { for await (const file of fsGlob(resolve(distDir, '**/*.js'))) { const content = await readFile(file, 'utf-8'); - if (!/^\t/m.test(content)) { - continue; - } const converted = content.replace(/^\t+/gm, (match) => ' '.repeat(match.length)); if (converted !== content) { await writeFile(file, converted); From 8395fe0d15082fbefa4196adcf4e4b45b2fb899b Mon Sep 17 00:00:00 2001 From: MK Date: Tue, 12 May 2026 16:18:06 +0800 Subject: [PATCH 3/3] test(snap): skip test-inline-snapshot-indent on Windows --- packages/cli/snap-tests/test-inline-snapshot-indent/steps.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/snap-tests/test-inline-snapshot-indent/steps.json b/packages/cli/snap-tests/test-inline-snapshot-indent/steps.json index 95763b2aad..8e3ce00a6c 100644 --- a/packages/cli/snap-tests/test-inline-snapshot-indent/steps.json +++ b/packages/cli/snap-tests/test-inline-snapshot-indent/steps.json @@ -1,4 +1,5 @@ { + "ignoredPlatforms": ["win32"], "commands": [ "vp test run -u src/inline-snapshot.test.ts # write inline snapshot via --update (regression test for #1553)", "cat src/inline-snapshot.test.ts # snapshot must use 2-space indentation, not tabs"