Skip to content

Commit 47459d1

Browse files
committed
fix: simplify formatting commands in snap scripts and update steps to link checkout packages
1 parent 273e4fb commit 47459d1

5 files changed

Lines changed: 176 additions & 17 deletions

File tree

packages/cli/snap-tests-global/create-framework-shim-astro/snap.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
/// <reference types="astro/client" />
44

55
> cd my-astro-app && vp install -- --no-frozen-lockfile # install dependencies
6-
> cd my-astro-app && sed -i.bak -e '/jsPlugins/d' -e '/rules:/d' -e '/options:/d' vite.config.ts && vp check --fix # fix generated formatting and ensure no errors
6+
> cd my-astro-app && vp check --fix # fix generated formatting and ensure no errors
77
pass: Formatting completed for checked files (<variable>ms)
8-
pass: Found no warnings or lint errors in 6 files (<variable>ms, <variable> threads)
8+
pass: Found no warnings, lint errors, or type errors in 6 files (<variable>ms, <variable> threads)
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"ignoredPlatforms": ["win32"],
3+
"linkCheckoutPackages": true,
34
"commands": [
45
{
56
"command": "vp create astro --no-interactive -- my-astro-app --yes --skip-houston --template basics # create Astro app",
@@ -10,8 +11,6 @@
1011
"command": "cd my-astro-app && vp install -- --no-frozen-lockfile # install dependencies",
1112
"ignoreOutput": true
1213
},
13-
{
14-
"command": "cd my-astro-app && sed -i.bak -e '/jsPlugins/d' -e '/rules:/d' -e '/options:/d' vite.config.ts && vp check --fix # fix generated formatting and ensure no errors"
15-
}
14+
"cd my-astro-app && vp check --fix # fix generated formatting and ensure no errors"
1615
]
1716
}

packages/cli/snap-tests-global/create-framework-shim-vue/snap.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ declare module '*.vue' {
77
}
88

99
> cd vite-plus-application && vp install # install dependencies
10-
> cd vite-plus-application && sed -i.bak -e '/jsPlugins/d' -e '/rules:/d' -e '/options:/d' vite.config.ts && vp check --fix # fix generated formatting and ensure no errors
10+
> cd vite-plus-application && vp check --fix # fix generated formatting and ensure no errors
1111
pass: Formatting completed for checked files (<variable>ms)
12-
pass: Found no warnings or lint errors in 5 files (<variable>ms, <variable> threads)
12+
pass: Found no warnings, lint errors, or type errors in 5 files (<variable>ms, <variable> threads)
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"ignoredPlatforms": ["win32"],
3+
"linkCheckoutPackages": true,
34
"commands": [
45
{
56
"command": "vp create vite:application --no-interactive -- --template vue-ts # create Vue+TS app",
@@ -10,8 +11,6 @@
1011
"command": "cd vite-plus-application && vp install # install dependencies",
1112
"ignoreOutput": true
1213
},
13-
{
14-
"command": "cd vite-plus-application && sed -i.bak -e '/jsPlugins/d' -e '/rules:/d' -e '/options:/d' vite.config.ts && vp check --fix # fix generated formatting and ensure no errors"
15-
}
14+
"cd vite-plus-application && vp check --fix # fix generated formatting and ensure no errors"
1615
]
1716
}

packages/tools/src/snap-test.ts

Lines changed: 168 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { randomUUID } from 'node:crypto';
2-
import fs, { readFileSync } from 'node:fs';
2+
import fs from 'node:fs';
33
import fsPromises from 'node:fs/promises';
4-
import { open } from 'node:fs/promises';
54
import { cpus, homedir, tmpdir } from 'node:os';
65
import path from 'node:path';
76
import { setTimeout } from 'node:timers/promises';
@@ -92,6 +91,146 @@ function selectShard<T>(items: T[], index: number, total: number): T[] {
9291

9392
const NPM_GLOBAL_PREFIX_DIR = 'npm-global-lib-for-snap-tests';
9493

94+
function resolveGlobalCliScriptsDir(casesDir: string): string {
95+
const candidates = [
96+
// `packages/cli/snap-tests-global` -> `packages/cli/dist`
97+
path.join(path.dirname(casesDir), 'dist'),
98+
// Fallback for the common `pnpm -F vite-plus snap-test-global` cwd.
99+
path.resolve('dist'),
100+
];
101+
102+
const scriptsDir = candidates.find((dir) => fs.existsSync(path.join(dir, 'bin.js')));
103+
if (!scriptsDir) {
104+
throw new Error(
105+
`Unable to find built Vite+ CLI scripts for global snap tests. Tried:\n${candidates
106+
.map((dir) => `- ${dir}`)
107+
.join('\n')}`,
108+
);
109+
}
110+
111+
return scriptsDir;
112+
}
113+
114+
function resolveRepoRoot(casesDir: string): string {
115+
return path.resolve(path.dirname(casesDir), '..', '..');
116+
}
117+
118+
function resolveGlobalCliBinary(binDir: string): string {
119+
const binaryName = process.platform === 'win32' ? 'vp.exe' : 'vp';
120+
const binaryPath = path.join(path.resolve(expandHome(binDir)), binaryName);
121+
if (!fs.existsSync(binaryPath)) {
122+
throw new Error(`Unable to find global snap test vp binary at ${binaryPath}`);
123+
}
124+
125+
return fs.realpathSync(binaryPath);
126+
}
127+
128+
function resolveBuiltGlobalCliBinary(casesDir: string): string {
129+
const binaryName = process.platform === 'win32' ? 'vp.exe' : 'vp';
130+
const repoRoot = resolveRepoRoot(casesDir);
131+
const candidates = [
132+
path.join(repoRoot, 'target', 'release', binaryName),
133+
path.join(repoRoot, 'target', 'debug', binaryName),
134+
];
135+
const binaryPath = candidates.find((candidate) => fs.existsSync(candidate));
136+
if (!binaryPath) {
137+
throw new Error(
138+
`Unable to find built Vite+ global CLI binary for global snap tests. Tried:\n${candidates
139+
.map((candidate) => `- ${candidate}`)
140+
.join('\n')}\nRun \`cargo build -p vite_global_cli --release\` before snap-test-global.`,
141+
);
142+
}
143+
144+
return fs.realpathSync(binaryPath);
145+
}
146+
147+
function newestMtimeMs(filePath: string): number {
148+
const stats = fs.statSync(filePath);
149+
if (!stats.isDirectory()) {
150+
return stats.mtimeMs;
151+
}
152+
153+
return fs
154+
.readdirSync(filePath)
155+
.reduce(
156+
(newest, entry) => Math.max(newest, newestMtimeMs(path.join(filePath, entry))),
157+
stats.mtimeMs,
158+
);
159+
}
160+
161+
function fileContentsEqual(a: string, b: string): boolean {
162+
return fs.readFileSync(a).equals(fs.readFileSync(b));
163+
}
164+
165+
function assertGlobalCliBinaryMatchesCheckout(binDir: string, casesDir: string): void {
166+
const repoRoot = resolveRepoRoot(casesDir);
167+
const builtBinary = resolveBuiltGlobalCliBinary(casesDir);
168+
const sourcePaths = [
169+
path.join(repoRoot, 'Cargo.toml'),
170+
path.join(repoRoot, 'Cargo.lock'),
171+
path.join(repoRoot, 'crates', 'vite_global_cli', 'src'),
172+
path.join(repoRoot, 'crates', 'vite_shared', 'src'),
173+
];
174+
const newestSourceMtime = Math.max(...sourcePaths.map(newestMtimeMs));
175+
if (fs.statSync(builtBinary).mtimeMs + 1000 < newestSourceMtime) {
176+
throw new Error(
177+
`Built Vite+ global CLI binary is older than the current checkout: ${builtBinary}\n` +
178+
'Run `cargo build -p vite_global_cli --release` before snap-test-global.',
179+
);
180+
}
181+
182+
const globalBinary = resolveGlobalCliBinary(binDir);
183+
if (fileContentsEqual(globalBinary, builtBinary)) {
184+
return;
185+
}
186+
187+
throw new Error(
188+
`Global snap tests would use a stale vp binary from ${globalBinary}.\n` +
189+
`Expected it to match the current checkout build at ${builtBinary}.\n` +
190+
'Run `pnpm bootstrap-cli` or `pnpm bootstrap-cli:ci` before snap-test-global.',
191+
);
192+
}
193+
194+
function replaceInstalledCheckoutPackages(rootDir: string, repoRoot: string): void {
195+
const stack = [rootDir];
196+
const symlinkType = process.platform === 'win32' ? 'junction' : 'dir';
197+
const replacements = new Map([
198+
['node_modules/vite-plus', path.join(repoRoot, 'packages', 'cli')],
199+
['node_modules/vite', path.join(repoRoot, 'packages', 'core')],
200+
['node_modules/vitest', path.join(repoRoot, 'packages', 'test')],
201+
['node_modules/@voidzero-dev/vite-plus-core', path.join(repoRoot, 'packages', 'core')],
202+
['node_modules/@voidzero-dev/vite-plus-test', path.join(repoRoot, 'packages', 'test')],
203+
]);
204+
205+
while (stack.length > 0) {
206+
const dir = stack.pop()!;
207+
for (const [relativePackagePath, checkoutPackageDir] of replacements) {
208+
const candidate = path.join(dir, relativePackagePath);
209+
if (fs.existsSync(candidate) && fs.realpathSync(candidate) !== checkoutPackageDir) {
210+
fs.rmSync(candidate, { recursive: true, force: true });
211+
fs.symlinkSync(checkoutPackageDir, candidate, symlinkType);
212+
}
213+
}
214+
215+
const isNodeModulesPath = dir.split(path.sep).includes('node_modules');
216+
const isPnpmStorePath = dir.split(path.sep).includes('.pnpm');
217+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
218+
if (!entry.isDirectory() || entry.name === '.git' || entry.name === '.bin') {
219+
continue;
220+
}
221+
if (
222+
isNodeModulesPath &&
223+
!isPnpmStorePath &&
224+
entry.name !== '.pnpm' &&
225+
entry.name !== '@voidzero-dev'
226+
) {
227+
continue;
228+
}
229+
stack.push(path.join(dir, entry.name));
230+
}
231+
}
232+
}
233+
95234
export async function snapTest() {
96235
const { positionals, values } = parseArgs({
97236
allowPositionals: true,
@@ -203,13 +342,18 @@ export async function snapTest() {
203342
const selectedCases = shard
204343
? selectShard(validCaseNames, shard.index, shard.total)
205344
: validCaseNames;
345+
const globalCliScriptsDir = values['bin-dir'] ? resolveGlobalCliScriptsDir(casesDir) : undefined;
346+
if (values['bin-dir']) {
347+
assertGlobalCliBinaryMatchesCheckout(values['bin-dir'], casesDir);
348+
}
206349

207350
const serialTasks: (() => Promise<void>)[] = [];
208351
const parallelTasks: (() => Promise<void>)[] = [];
209352
for (const caseName of selectedCases) {
210353
const stepsPath = path.join(casesDir, caseName, 'steps.json');
211-
const steps: Steps = JSON.parse(readFileSync(stepsPath, 'utf-8'));
212-
const task = () => runTestCase(caseName, tempTmpDir, casesDir, values['bin-dir']);
354+
const steps: Steps = JSON.parse(fs.readFileSync(stepsPath, 'utf-8'));
355+
const task = () =>
356+
runTestCase(caseName, tempTmpDir, casesDir, values['bin-dir'], globalCliScriptsDir);
213357
if (steps.serial) {
214358
serialTasks.push(task);
215359
} else {
@@ -258,6 +402,11 @@ interface Steps {
258402
ignoredPlatforms?: (string | PlatformFilter)[];
259403
env: Record<string, string>;
260404
commands: (string | Command)[];
405+
/**
406+
* If true, installed Vite+ packages in the test project are relinked to the
407+
* current checkout after each successful command.
408+
*/
409+
linkCheckoutPackages?: boolean;
261410
/**
262411
* Commands to run after the test completes, regardless of success or failure.
263412
* Useful for cleanup tasks like killing background processes.
@@ -322,7 +471,13 @@ function shouldSkipPlatform(ignoredPlatforms: (string | PlatformFilter)[]): bool
322471
return false;
323472
}
324473

325-
async function runTestCase(name: string, tempTmpDir: string, casesDir: string, binDir?: string) {
474+
async function runTestCase(
475+
name: string,
476+
tempTmpDir: string,
477+
casesDir: string,
478+
binDir?: string,
479+
globalCliScriptsDir?: string,
480+
) {
326481
const steps: Steps = JSON.parse(
327482
await fsPromises.readFile(`${casesDir}/${name}/steps.json`, 'utf-8'),
328483
);
@@ -362,6 +517,9 @@ async function runTestCase(name: string, tempTmpDir: string, casesDir: string, b
362517
VP_SKIP_INSTALL: '1',
363518
// make sure npm install global packages to the temporary directory
364519
NPM_CONFIG_PREFIX: path.join(tempTmpDir, NPM_GLOBAL_PREFIX_DIR),
520+
// Global CLI snap tests execute the Rust binary from --bin-dir, but the JS
521+
// entry should come from this checkout instead of a stale ~/.vite-plus install.
522+
...(globalCliScriptsDir ? { VITE_GLOBAL_CLI_JS_SCRIPTS_DIR: globalCliScriptsDir } : {}),
365523

366524
// A test case can override/unset environment variables above.
367525
// For example, VP_CLI_TEST/CI can be unset to test the real-world outputs.
@@ -409,7 +567,7 @@ async function runTestCase(name: string, tempTmpDir: string, casesDir: string, b
409567
// it seems not to have stable ordering of stdout/stderr chunks.
410568
// To ensure stable ordering, we redirect outputs to a file instead.
411569
const outputStreamPath = path.join(caseTmpDir, 'output.log');
412-
const outputStream = await open(outputStreamPath, 'w');
570+
const outputStream = await fsPromises.open(outputStreamPath, 'w');
413571

414572
const exitCode = await Promise.race([
415573
execute(stripComments(cmd.command), [], {
@@ -431,8 +589,11 @@ async function runTestCase(name: string, tempTmpDir: string, casesDir: string, b
431589
]);
432590

433591
await outputStream.close();
592+
if (exitCode === 0 && globalCliScriptsDir && steps.linkCheckoutPackages) {
593+
replaceInstalledCheckoutPackages(caseTmpDir, resolveRepoRoot(casesDir));
594+
}
434595

435-
let output = readFileSync(outputStreamPath, 'utf-8');
596+
let output = fs.readFileSync(outputStreamPath, 'utf-8');
436597

437598
let commandLine = `> ${cmd.command}`;
438599
if (exitCode !== 0) {

0 commit comments

Comments
 (0)