From bcfdb84e77f4ac0f20173bc610ab112ed21e608c Mon Sep 17 00:00:00 2001 From: MK Date: Wed, 4 Nov 2026 22:40:48 +0800 Subject: [PATCH 01/25] perf(lint): benchmark the experimental tsrs-lint backend --- bench/tsrs-lint.md | 26 +++++++++++++++ bench/tsrs-lint.ts | 81 ++++++++++++++++++++++++++++++++++++++++++++++ docs/guide/lint.md | 11 +++++++ 3 files changed, 118 insertions(+) create mode 100644 bench/tsrs-lint.md create mode 100644 bench/tsrs-lint.ts diff --git a/bench/tsrs-lint.md b/bench/tsrs-lint.md new file mode 100644 index 0000000000..9c643ebf3d --- /dev/null +++ b/bench/tsrs-lint.md @@ -0,0 +1,26 @@ +# Type-aware linting with tsrs-lint + +On the Vite+ workspace at `a32a8cec2` with the benchmark script added, the experimental backend saved 0.51 seconds per lint run with full type checking. Both backends analyzed 385 files with 174 rules, reported zero diagnostics, and exited successfully on every run. + +| `vp lint` workload | Bundled tsgolint | tsrs-lint | Wall time saved | +| ------------------------------------------- | ---------------: | --------: | --------------: | +| Type-aware rules | 1.089 s | 0.822 s | 24.5% | +| Type-aware rules and TypeScript diagnostics | 1.340 s | 0.830 s | 38.1% | + +For the full `vp check` command, the median fell from 1.852 s to 1.335 s (27.9%). This command also checked formatting on 632 files and used its default 16 threads. + +Measured on an Apple M4 Max with macOS 27.0.1, Node.js 24.22.2, Vite+ 1.5.0, Oxlint 1.91.0, oxlint-tsgolint 7.0.2005, and tsrs 0.21.0-ts7.1.0-beta. The benchmark used the published Vite+ 1.5.0 CLI artifacts and the workspace's locked dependencies. Each sample started a fresh local CLI process and included config loading, JS plugins, the backend launcher, and diagnostic reporting. The `vp lint` runs used four Oxlint threads. Both backends used their default settings. + +The table shows the median of ten runs per backend after one warm-up each. The runner reverses the backend order between pairs and compares the exit code, file and rule counts, and sorted JSON diagnostics for every run. In a separate failing fixture, both backends reported `typescript/no-floating-promises` and `TS2322` at the same locations through `vp lint` and `vp check`. + +## Reproduce + +Start in a built Vite+ checkout. Install the experimental backend under the ignored `tmp` directory to keep it separate from workspace dependencies. + +```sh +npm install --prefix ./tmp/tsrs-lint --no-save --package-lock=false tsrs@0.21.0-ts7.1.0-beta +export OXLINT_TSGOLINT_PATH="$PWD/tmp/tsrs-lint/node_modules/.bin/tsrs-lint" +node bench/tsrs-lint.ts --type-check +``` + +For the rules-only row, temporarily set `lint.options.typeCheck` to `false` in the root `vite.config.ts`, run `node bench/tsrs-lint.ts` without `--type-check`, then restore the setting. The benchmark always enables `--type-aware` and prints the individual samples in seconds. diff --git a/bench/tsrs-lint.ts b/bench/tsrs-lint.ts new file mode 100644 index 0000000000..7c9088d805 --- /dev/null +++ b/bench/tsrs-lint.ts @@ -0,0 +1,81 @@ +// Run from the workspace root with OXLINT_TSGOLINT_PATH pointing to tsrs-lint. +// Additional arguments (e.g. --type-check or file paths) go to both vp lint runs. +import assert from 'node:assert/strict'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; + +const { OXLINT_TSGOLINT_PATH: tsrs, ...baselineEnv } = process.env; +if (!tsrs) { + throw new Error('Set OXLINT_TSGOLINT_PATH to the tsrs-lint executable.'); +} +const vp = fileURLToPath(new URL('../packages/cli/bin/vp', import.meta.url)); +const args = [ + vp, + 'lint', + '--type-aware', + '--threads', + '4', + '--format', + 'json', + ...process.argv.slice(2), +]; + +function run(env: NodeJS.ProcessEnv) { + const start = performance.now(); + const { status, stdout, stderr, error } = spawnSync(process.execPath, args, { + env, + encoding: 'utf8', + maxBuffer: 64 * 1024 * 1024, + }); + const seconds = (performance.now() - start) / 1000; + if (error) { + throw error; + } + if ((status !== 0 && status !== 1) || !stdout.trimStart().startsWith('{')) { + throw new Error(`vp lint exited with ${status}\n${stderr}\n${stdout}`); + } + const report = JSON.parse(stdout) as { + diagnostics: unknown[]; + number_of_files: number; + number_of_rules: number; + }; + assert.ok(report.number_of_files > 0, 'Expected vp lint to analyze files'); + return { + seconds, + result: { + status, + files: report.number_of_files, + rules: report.number_of_rules, + diagnostics: report.diagnostics.map((d) => JSON.stringify(d)).toSorted(), + }, + }; +} + +const backends = [ + { name: 'tsgolint', env: baselineEnv, samples: [] as number[] }, + { + name: 'tsrs-lint', + env: { ...baselineEnv, OXLINT_TSGOLINT_PATH: tsrs }, + samples: [] as number[], + }, +]; +const [expected, candidate] = backends.map(({ env }) => run(env).result); +assert.deepEqual(candidate, expected); +const pairs = 10; +for (let pair = 0; pair < pairs; pair++) { + for (const backend of pair % 2 === 0 ? backends : backends.toReversed()) { + const { result, seconds } = run(backend.env); + assert.deepEqual(result, expected); + backend.samples.push(seconds); + } +} +console.log( + `${expected.files} files, ${expected.rules} rules, ${expected.diagnostics.length} diagnostics, exit ${expected.status}`, +); +for (const { name, samples } of backends) { + const sorted = samples.toSorted((a, b) => a - b); + const median = (sorted[pairs / 2 - 1] + sorted[pairs / 2]) / 2; + console.log( + `${name} median ${median.toFixed(3)} s [${samples.map((s) => s.toFixed(3)).join(', ')}]`, + ); +} diff --git a/docs/guide/lint.md b/docs/guide/lint.md index 211008821e..d2f7444545 100644 --- a/docs/guide/lint.md +++ b/docs/guide/lint.md @@ -51,6 +51,17 @@ We recommend enabling both `typeAware` and `typeCheck` in the `lint` block: This path is powered by [tsgolint](https://github.com/oxc-project/tsgolint) on top of the TypeScript 7 (aka TypeScript Go) toolchain. It gives Oxlint access to type information and allows type checking directly via `vp lint` and `vp check`. +### Try the experimental Rust backend + +To try the Rust backend, install `tsrs` in your project and point Oxlint at the `tsrs-lint` executable. Run these commands from the project root. + +```sh +vp add -D tsrs +OXLINT_TSGOLINT_PATH="$PWD/node_modules/.bin/tsrs-lint" vp lint --type-aware --type-check +``` + +The same environment variable works with `vp check` when type-aware linting is enabled. Omit it to use the bundled tsgolint again. + ## JS Plugins If you are migrating from ESLint and still depend on a few critical JavaScript-based ESLint plugins, Oxlint has [JS plugin support](https://oxc.rs/docs/guide/usage/linter/js-plugins) that can help you keep those plugins running while you complete the migration. From 6355fd9d369de09e932887c07e6549ab1de80782 Mon Sep 17 00:00:00 2001 From: MK Date: Thu, 5 Nov 2026 15:12:44 +0800 Subject: [PATCH 02/25] perf(lint): use tsrs as the experimental default backend --- bench/tsrs-lint.md | 2 +- bench/tsrs-lint.ts | 18 +++- crates/vp_global_cli/src/commands/version.rs | 43 ++++++++- docs/guide/check.md | 4 +- docs/guide/lint.md | 14 +-- packages/cli/package.json | 3 +- .../cli/src/__tests__/resolve-lint.spec.ts | 27 +++--- packages/cli/src/__tests__/toolchain.spec.ts | 2 +- packages/cli/src/__tests__/versions.spec.ts | 2 +- packages/cli/src/resolve-lint.ts | 13 +-- ...{tsgolint-path.ts => lint-backend-path.ts} | 38 ++++---- packages/cli/src/version.ts | 2 +- packages/cli/toolchain.config.json | 20 ++-- pnpm-lock.yaml | 94 ++++++++++++++++++- pnpm-workspace.yaml | 3 + 15 files changed, 215 insertions(+), 70 deletions(-) rename packages/cli/src/utils/{tsgolint-path.ts => lint-backend-path.ts} (50%) diff --git a/bench/tsrs-lint.md b/bench/tsrs-lint.md index 9c643ebf3d..6543ba2418 100644 --- a/bench/tsrs-lint.md +++ b/bench/tsrs-lint.md @@ -15,7 +15,7 @@ The table shows the median of ten runs per backend after one warm-up each. The r ## Reproduce -Start in a built Vite+ checkout. Install the experimental backend under the ignored `tmp` directory to keep it separate from workspace dependencies. +Start in a built Vite+ checkout. The runner explicitly selects the `oxlint-tsgolint` development dependency for the Go baseline, even when the CLI defaults to tsrs. Install the candidate backend under the ignored `tmp` directory to compare a specific version. ```sh npm install --prefix ./tmp/tsrs-lint --no-save --package-lock=false tsrs@0.21.0-ts7.1.0-beta diff --git a/bench/tsrs-lint.ts b/bench/tsrs-lint.ts index 7c9088d805..5dfb3a42da 100644 --- a/bench/tsrs-lint.ts +++ b/bench/tsrs-lint.ts @@ -2,13 +2,29 @@ // Additional arguments (e.g. --type-check or file paths) go to both vp lint runs. import assert from 'node:assert/strict'; import { spawnSync } from 'node:child_process'; +import { existsSync } from 'node:fs'; +import { createRequire } from 'node:module'; import { fileURLToPath } from 'node:url'; -const { OXLINT_TSGOLINT_PATH: tsrs, ...baselineEnv } = process.env; +const { OXLINT_TSGOLINT_PATH: tsrs, ...env } = process.env; if (!tsrs) { throw new Error('Set OXLINT_TSGOLINT_PATH to the tsrs-lint executable.'); } const vp = fileURLToPath(new URL('../packages/cli/bin/vp', import.meta.url)); +const require = createRequire(new URL('../packages/cli/package.json', import.meta.url)); +const tsgolint = + process.platform === 'win32' + ? ['tsgolint.exe', 'tsgolint.cmd'] + .map((bin) => + fileURLToPath(new URL(`../packages/cli/node_modules/.bin/${bin}`, import.meta.url)), + ) + .find(existsSync) + : require.resolve('oxlint-tsgolint/bin/tsgolint'); +assert.ok(tsgolint, 'Install workspace development dependencies to get the Go baseline.'); +const baselineEnv = { + ...env, + OXLINT_TSGOLINT_PATH: tsgolint, +}; const args = [ vp, 'lint', diff --git a/crates/vp_global_cli/src/commands/version.rs b/crates/vp_global_cli/src/commands/version.rs index c35976392e..6ecb78969b 100644 --- a/crates/vp_global_cli/src/commands/version.rs +++ b/crates/vp_global_cli/src/commands/version.rs @@ -123,6 +123,16 @@ fn resolve_tool_version( } } +fn tool_spec_for_manifest(tool: ToolSpec, manifest: Option<&vp_toolchain::Manifest>) -> ToolSpec { + if tool.id == "oxlint-tsgolint" + && manifest.is_some_and(|manifest| vp_toolchain::node_by_id(manifest, "tsrs").is_some()) + { + ToolSpec { id: "tsrs", package_name: "tsrs", bundled_version_key: None } + } else { + tool + } +} + fn print_stdout(message: &str) { vp_shared::output::print_and_flush(&mut io::stdout().lock(), message); } @@ -189,7 +199,8 @@ pub async fn execute(cwd: AbsolutePathBuf) -> Result { let tool_rows = TOOL_SPECS .iter() .map(|tool| { - let version = resolve_tool_version(local.as_ref(), manifest.as_ref(), *tool); + let tool = tool_spec_for_manifest(*tool, manifest.as_ref()); + let version = resolve_tool_version(local.as_ref(), manifest.as_ref(), tool); (tool.id, format_version(version)) }) .collect::>(); @@ -233,9 +244,12 @@ mod tests { #[cfg(unix)] use std::{fs, path::Path}; + use super::{ + TOOL_SPECS, detect_system_node_version, format_version, resolve_tool_version, + tool_spec_for_manifest, + }; #[cfg(unix)] - use super::{TOOL_SPECS, find_local_vite_plus, read_toolchain_manifest, resolve_tool_version}; - use super::{detect_system_node_version, format_version}; + use super::{find_local_vite_plus, read_toolchain_manifest}; #[cfg(unix)] fn symlink_dir(src: &Path, dst: &Path) { @@ -248,6 +262,29 @@ mod tests { assert_eq!(format_version(None), "Not found"); } + #[test] + fn reports_the_type_aware_backend_from_the_local_manifest() { + for (id, version) in [("tsrs", "0.21.0-ts7.1.0-beta"), ("oxlint-tsgolint", "7.0.2005")] { + let manifest = serde_json::from_value::(serde_json::json!({ + "schemaVersion": 1, + "nodes": [{ + "id": id, + "name": id, + "version": version, + "kind": "tool", + "delivery": ["dependency"], + "aliases": [] + }], + "edges": [] + })) + .unwrap(); + let tool = tool_spec_for_manifest(TOOL_SPECS[5], Some(&manifest)); + assert_eq!(tool.id, id); + assert_eq!(resolve_tool_version(None, Some(&manifest), tool).as_deref(), Some(version)); + } + assert_eq!(tool_spec_for_manifest(TOOL_SPECS[5], None).id, "oxlint-tsgolint"); + } + // The spawned `node` inherits this process's environment, so hold // temp_env's lock with PATH/VP_HOME re-pinned to their current values: // no concurrent with_vars scope can swap them mid-test and make a vp shim diff --git a/docs/guide/check.md b/docs/guide/check.md index 02ea53d077..6cebe2f077 100644 --- a/docs/guide/check.md +++ b/docs/guide/check.md @@ -4,9 +4,9 @@ ## Overview -`vp check` is the default command for fast static checks in Vite+. It brings together formatting through [Oxfmt](https://oxc.rs/docs/guide/usage/formatter.html), linting through [Oxlint](https://oxc.rs/docs/guide/usage/linter.html), and TypeScript type checks through [tsgolint](https://github.com/oxc-project/tsgolint). By merging all of these tasks into a single command, `vp check` is faster than running formatting, linting, and type checking as separate tools in separate commands. +`vp check` is the default command for fast static checks in Vite+. It brings together formatting through [Oxfmt](https://oxc.rs/docs/guide/usage/formatter.html), linting through [Oxlint](https://oxc.rs/docs/guide/usage/linter.html), and TypeScript type checks through [tsrs](https://github.com/oxc-project/tsrs) in this experimental build. By merging all of these tasks into a single command, `vp check` is faster than running formatting, linting, and type checking as separate tools in separate commands. -When both `typeAware` and `typeCheck` are enabled in the `lint.options` block in `vite.config.ts`, `vp check` also runs TypeScript type checks through the Oxlint type-aware path powered by the TypeScript Go toolchain and [tsgolint](https://github.com/oxc-project/tsgolint). `vp create` and `vp migrate` enable both `typeAware` and `typeCheck` by default. +When both `typeAware` and `typeCheck` are enabled in the `lint.options` block in `vite.config.ts`, `vp check` also runs TypeScript type checks through the Oxlint type-aware path powered by `tsrs-lint`. `vp create` and `vp migrate` enable both `typeAware` and `typeCheck` by default. We recommend turning both `typeAware` and `typeCheck` on so `vp check` becomes the single command for static checks during development. diff --git a/docs/guide/lint.md b/docs/guide/lint.md index d2f7444545..69538b86d3 100644 --- a/docs/guide/lint.md +++ b/docs/guide/lint.md @@ -49,18 +49,20 @@ We recommend enabling both `typeAware` and `typeCheck` in the `lint` block: - `typeAware: true` enables rules that require TypeScript type information - `typeCheck: true` enables full type checking during linting -This path is powered by [tsgolint](https://github.com/oxc-project/tsgolint) on top of the TypeScript 7 (aka TypeScript Go) toolchain. It gives Oxlint access to type information and allows type checking directly via `vp lint` and `vp check`. +This experimental build uses [tsrs](https://github.com/oxc-project/tsrs), a Rust port of the TypeScript 7 compiler. Its `tsrs-lint` executable gives Oxlint access to type information and allows type checking directly via `vp lint` and `vp check`. -### Try the experimental Rust backend +### Select another backend -To try the Rust backend, install `tsrs` in your project and point Oxlint at the `tsrs-lint` executable. Run these commands from the project root. +The bundled `tsrs-lint` runs by default. To compare it with [tsgolint](https://github.com/oxc-project/tsgolint), install the Go backend and set `OXLINT_TSGOLINT_PATH`. Run these commands from the project root. ```sh -vp add -D tsrs -OXLINT_TSGOLINT_PATH="$PWD/node_modules/.bin/tsrs-lint" vp lint --type-aware --type-check +vp add -D oxlint-tsgolint +OXLINT_TSGOLINT_PATH="$PWD/node_modules/.bin/tsgolint" vp lint --type-aware --type-check ``` -The same environment variable works with `vp check` when type-aware linting is enabled. Omit it to use the bundled tsgolint again. +The same environment variable works with `vp check` when type-aware linting is enabled. Omit it to use the bundled tsrs again. You can also point it at another `tsrs-lint` installation. + +The pinned `tsrs` release does not provide a Windows ARM64 binary. This experimental replacement does not support that platform yet. ## JS Plugins diff --git a/packages/cli/package.json b/packages/cli/package.json index bbefe97f75..1f7950851e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -364,7 +364,7 @@ "@vitest/utils": "catalog:", "oxfmt": "catalog:", "oxlint": "catalog:", - "oxlint-tsgolint": "catalog:", + "tsrs": "catalog:", "vite": "workspace:@voidzero-dev/vite-plus-core@*", "vitest": "catalog:" }, @@ -388,6 +388,7 @@ "lint-staged": "catalog:", "minimatch": "catalog:", "nanotar": "catalog:", + "oxlint-tsgolint": "catalog:", "picocolors": "catalog:", "pirates": "catalog:", "tinyglobby": "catalog:", diff --git a/packages/cli/src/__tests__/resolve-lint.spec.ts b/packages/cli/src/__tests__/resolve-lint.spec.ts index c8220e9200..c5734d7895 100644 --- a/packages/cli/src/__tests__/resolve-lint.spec.ts +++ b/packages/cli/src/__tests__/resolve-lint.spec.ts @@ -2,7 +2,7 @@ import { existsSync } from 'node:fs'; import { describe, expect, it } from 'vitest'; -import { lint, resolveWindowsTsgolintExecutable } from '../resolve-lint.js'; +import { lint, resolveWindowsLintBackendExecutable } from '../resolve-lint.js'; describe('resolve-lint', () => { it('should resolve binPath and OXLINT_TSGOLINT_PATH to existing files', async () => { @@ -14,28 +14,29 @@ describe('resolve-lint', () => { `oxlint binPath should point to an existing file, got: ${result.binPath}`, ).toBe(true); - const tsgolintPath = result.envs.OXLINT_TSGOLINT_PATH; - expect(tsgolintPath).toBeTruthy(); + const backendPath = result.envs.OXLINT_TSGOLINT_PATH; + expect(backendPath).toBeTruthy(); + expect(backendPath).toMatch(/[/\\]tsrs-lint(?:\.(?:cmd|exe))?$/); expect( - existsSync(tsgolintPath), - `OXLINT_TSGOLINT_PATH should point to an existing file, got: ${tsgolintPath}`, + existsSync(backendPath), + `OXLINT_TSGOLINT_PATH should point to an existing file, got: ${backendPath}`, ).toBe(true); }); - it('should keep the Windows tsgolint executable path absolute', () => { - const tsgolintPath = - 'C:\\repo\\node_modules\\.pnpm\\vite-plus@0.1.24\\node_modules\\vite-plus\\node_modules\\.bin\\tsgolint.cmd'; - const result = resolveWindowsTsgolintExecutable( + it('should keep the Windows tsrs-lint executable path absolute', () => { + const backendPath = + 'C:\\repo\\node_modules\\.pnpm\\vite-plus@0.1.24\\node_modules\\vite-plus\\node_modules\\.bin\\tsrs-lint.cmd'; + const result = resolveWindowsLintBackendExecutable( [ - 'C:\\repo\\node_modules\\.pnpm\\vite-plus@0.1.24\\node_modules\\vite-plus\\node_modules\\.bin\\tsgolint.exe', - tsgolintPath, + 'C:\\repo\\node_modules\\.pnpm\\vite-plus@0.1.24\\node_modules\\vite-plus\\node_modules\\.bin\\tsrs-lint.exe', + backendPath, ], { - exists: (path) => path === tsgolintPath, + exists: (path) => path === backendPath, }, ); - expect(result).toBe(tsgolintPath); + expect(result).toBe(backendPath); expect(result).not.toMatch(/^\.\\/); }); }); diff --git a/packages/cli/src/__tests__/toolchain.spec.ts b/packages/cli/src/__tests__/toolchain.spec.ts index 036d1e4a63..c1dc6ab535 100644 --- a/packages/cli/src/__tests__/toolchain.spec.ts +++ b/packages/cli/src/__tests__/toolchain.spec.ts @@ -32,7 +32,7 @@ describe('toolchain export', () => { 'vitest', 'oxlint', 'oxfmt', - 'oxlint-tsgolint', + 'tsrs', 'tsdown', 'vite-task', 'oxc', diff --git a/packages/cli/src/__tests__/versions.spec.ts b/packages/cli/src/__tests__/versions.spec.ts index 646a608397..223f70785c 100644 --- a/packages/cli/src/__tests__/versions.spec.ts +++ b/packages/cli/src/__tests__/versions.spec.ts @@ -115,7 +115,7 @@ describe('versions export', () => { expect(versions.vitest).toBeTypeOf('string'); expect(versions.oxlint).toBeTypeOf('string'); expect(versions.oxfmt).toBeTypeOf('string'); - expect(versions['oxlint-tsgolint']).toBeTypeOf('string'); + expect(versions.tsrs).toBeTypeOf('string'); }); }); }); diff --git a/packages/cli/src/resolve-lint.ts b/packages/cli/src/resolve-lint.ts index f552962c4b..9b0ef98d25 100644 --- a/packages/cli/src/resolve-lint.ts +++ b/packages/cli/src/resolve-lint.ts @@ -13,10 +13,10 @@ import { dirname, join } from 'node:path'; -import { CONFIG_METADATA_ENV, DEFAULT_ENVS, resolve } from './utils/constants.ts'; -import { resolveTsgolintExecutable } from './utils/tsgolint-path.ts'; +import { CONFIG_METADATA_ENV, DEFAULT_ENVS, resolve, resolveBundled } from './utils/constants.ts'; +import { resolveLintBackendExecutable } from './utils/lint-backend-path.ts'; -export { resolveWindowsTsgolintExecutable } from './utils/tsgolint-path.ts'; +export { resolveWindowsLintBackendExecutable } from './utils/lint-backend-path.ts'; /** * Resolves the oxlint binary path and environment variables. @@ -40,16 +40,17 @@ export async function lint(): Promise<{ const oxlintMainPath = resolve('oxlint'); const oxlintPackageRoot = dirname(dirname(oxlintMainPath)); const binPath = join(oxlintPackageRoot, 'bin', 'oxlint'); - const oxlintTsgolintPath = resolveTsgolintExecutable( - resolve('oxlint-tsgolint/bin/tsgolint'), + const tsrsLintPath = resolveLintBackendExecutable( + join(dirname(resolveBundled('tsrs/package.json')), 'bin', 'tsrs-lint'), import.meta.url, + 'tsrs-lint', ); const result = { binPath, // TODO: provide envs inference API envs: { ...DEFAULT_ENVS, - OXLINT_TSGOLINT_PATH: oxlintTsgolintPath, + OXLINT_TSGOLINT_PATH: tsrsLintPath, // oxlint loads vite.config.ts only to read the `lint` block, so skip the // user's Vite plugin factory (lazyPlugins) while it evaluates the config. [CONFIG_METADATA_ENV]: '1', diff --git a/packages/cli/src/utils/tsgolint-path.ts b/packages/cli/src/utils/lint-backend-path.ts similarity index 50% rename from packages/cli/src/utils/tsgolint-path.ts rename to packages/cli/src/utils/lint-backend-path.ts index e98f1d41ca..5b7ac60571 100644 --- a/packages/cli/src/utils/tsgolint-path.ts +++ b/packages/cli/src/utils/lint-backend-path.ts @@ -2,54 +2,58 @@ import { existsSync, realpathSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; -export function resolveWindowsTsgolintExecutable( +export function resolveWindowsLintBackendExecutable( pathCandidates: string[], options: { exists: (path: string) => boolean; getRealpathCandidates?: () => string[]; }, ): string { - let oxlintTsgolintPath = pathCandidates.find((p) => options.exists(p)) ?? ''; - if (!oxlintTsgolintPath && options.getRealpathCandidates) { + let backendPath = pathCandidates.find((p) => options.exists(p)) ?? ''; + if (!backendPath && options.getRealpathCandidates) { try { - oxlintTsgolintPath = options.getRealpathCandidates().find((p) => options.exists(p)) ?? ''; + backendPath = options.getRealpathCandidates().find((p) => options.exists(p)) ?? ''; } catch { // realpath failed, fall through to default } } - if (!oxlintTsgolintPath) { + if (!backendPath) { throw new Error( - 'Unable to resolve oxlint-tsgolint executable, tried:\n' + + 'Unable to resolve type-aware lint executable, tried:\n' + pathCandidates.map((path) => `- ${path}`).join('\n'), ); } - return oxlintTsgolintPath; + return backendPath; } -export function resolveTsgolintExecutable(tsgolintBinPath: string, scriptUrl: string): string { +export function resolveLintBackendExecutable( + backendBinPath: string, + scriptUrl: string, + executableName: string, +): string { if (process.platform !== 'win32') { - return tsgolintBinPath; + return backendBinPath; } // On Windows, try .exe first (bun creates .exe), then .cmd (npm/pnpm/yarn create .cmd) const scriptDir = dirname(fileURLToPath(scriptUrl)); const localBinDir = join(scriptDir, '..', 'node_modules', '.bin'); - const oxlintTsgolintPackagePath = dirname(dirname(tsgolintBinPath)); - const projectBinDir = join(oxlintTsgolintPackagePath, '..', '.bin'); + const backendPackagePath = dirname(dirname(backendBinPath)); + const projectBinDir = join(backendPackagePath, '..', '.bin'); const pathCandidates = [ - join(localBinDir, 'tsgolint.exe'), - join(localBinDir, 'tsgolint.cmd'), - join(projectBinDir, 'tsgolint.exe'), - join(projectBinDir, 'tsgolint.cmd'), + join(localBinDir, `${executableName}.exe`), + join(localBinDir, `${executableName}.cmd`), + join(projectBinDir, `${executableName}.exe`), + join(projectBinDir, `${executableName}.cmd`), ]; - return resolveWindowsTsgolintExecutable(pathCandidates, { + return resolveWindowsLintBackendExecutable(pathCandidates, { exists: existsSync, // Bun stores packages in .bun/ cache dirs where the symlinked paths above won't match. getRealpathCandidates: () => { const realPkgDir = realpathSync(join(scriptDir, '..')); const realBinDir = join(dirname(realPkgDir), '.bin'); - return [join(realBinDir, 'tsgolint.exe'), join(realBinDir, 'tsgolint.cmd')]; + return [join(realBinDir, `${executableName}.exe`), join(realBinDir, `${executableName}.cmd`)]; }, }); } diff --git a/packages/cli/src/version.ts b/packages/cli/src/version.ts index 2a62af26b9..ac5d3f7f22 100644 --- a/packages/cli/src/version.ts +++ b/packages/cli/src/version.ts @@ -15,7 +15,7 @@ const TOOL_DISPLAY_ORDER = [ 'vitest', 'oxfmt', 'oxlint', - 'oxlint-tsgolint', + 'tsrs', 'tsdown', ] as const; diff --git a/packages/cli/toolchain.config.json b/packages/cli/toolchain.config.json index d71c335087..1a15f0148f 100644 --- a/packages/cli/toolchain.config.json +++ b/packages/cli/toolchain.config.json @@ -1,14 +1,6 @@ { "schemaVersion": 1, - "versionExportIds": [ - "vite", - "rolldown", - "vitest", - "oxfmt", - "oxlint", - "oxlint-tsgolint", - "tsdown" - ], + "versionExportIds": ["vite", "rolldown", "vitest", "oxfmt", "oxlint", "tsrs", "tsdown"], "nodes": [ { "id": "vite-plus", @@ -86,14 +78,14 @@ } }, { - "id": "oxlint-tsgolint", - "name": "oxlint-tsgolint", + "id": "tsrs", + "name": "tsrs", "kind": "tool", "delivery": ["dependency"], - "aliases": ["tsgolint"], + "aliases": ["tsrs-lint", "tsgolint", "oxlint-tsgolint"], "versionSource": { "type": "npm-dependency", - "package": "oxlint-tsgolint" + "package": "tsrs" } }, { @@ -203,7 +195,7 @@ }, { "from": "vite-plus", - "to": "oxlint-tsgolint", + "to": "tsrs", "relationship": "depends-on" }, { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 023b9c79c7..3c0d7904e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -344,6 +344,9 @@ catalogs: tsdown: specifier: ^0.23.4 version: 0.23.4 + tsrs: + specifier: '=0.21.0-ts7.1.0-beta' + version: 0.21.0-ts7.1.0-beta typescript: specifier: ~7.0.2 version: 7.0.2 @@ -463,12 +466,12 @@ importers: oxlint: specifier: 'catalog:' version: 1.93.0(oxlint-tsgolint@7.0.2006)(vite-plus@packages+cli) - oxlint-tsgolint: - specifier: 'catalog:' - version: 7.0.2006 playwright: specifier: '*' version: 1.62.1 + tsrs: + specifier: 'catalog:' + version: 0.21.0-ts7.1.0-beta vite: specifier: workspace:@voidzero-dev/vite-plus-core@* version: link:../core @@ -533,6 +536,9 @@ importers: nanotar: specifier: 'catalog:' version: 0.3.0 + oxlint-tsgolint: + specifier: 'catalog:' + version: 7.0.2005 picocolors: specifier: 'catalog:' version: 1.1.1 @@ -3576,6 +3582,52 @@ packages: peerDependencies: tsdown: 0.23.4 + '@tsrs/darwin-arm64@0.21.0-ts7.1.0-beta': + resolution: {integrity: sha512-qBHxa+LmrNu/XYZjqLXV4qQPsHzxSI+dQTISxXuxyBaxtJVDwCwGAq9rVHKgaUX3U0bsyb8FyC1JIZFu4+EqZQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] + + '@tsrs/darwin-x64@0.21.0-ts7.1.0-beta': + resolution: {integrity: sha512-7WUGrCYqz94dRpSTungOh90LL/5NhhnLQrMQqRYvmirHcp1sRlcVziJ2w+cynKwQ81/I2UajsNiKho/YSObcyA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] + + '@tsrs/linux-arm64-gnu@0.21.0-ts7.1.0-beta': + resolution: {integrity: sha512-bNWkz4lu+bOzl+sl9btE4pQ0RbLkv0Zm4FE2oKvBrt56vCnl7khcAcsqpSxBXApOkp5OwvyyY+XuOGuROBt7yg==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tsrs/linux-arm64-musl@0.21.0-ts7.1.0-beta': + resolution: {integrity: sha512-TwvKTiOD8Fwb3HB+CrhnpYgKf7He1BobtjefTja0iGAzDtfaiGwoF06zLUdmey27kmCmxjBPybS45VRyZBkQnQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tsrs/linux-x64-gnu@0.21.0-ts7.1.0-beta': + resolution: {integrity: sha512-0OAYWOp33xHJbY/zwowbq4IKg1aCzD98sb2ksZb/1um2l28HYhJyhMEEsNczLpUFipQDE+zVBNkFoBML/KRNvA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tsrs/linux-x64-musl@0.21.0-ts7.1.0-beta': + resolution: {integrity: sha512-OtYmWa1tzjSt/bXxotPBrPrzlfZqz+azkkfotmuYMQfxiOgmRRm7cl022iV4lE21EOUicqp0NAnuDGDLfDlNBg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tsrs/win32-x64@0.21.0-ts7.1.0-beta': + resolution: {integrity: sha512-ITwz3wOx161DNwEC4qYrrRwWgQyZQR4Os/GA3g67dKml0CQ4LPWHXniCzXrMagwYyH2vBBhPYT+7o+bKDj0qtA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [win32] + '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} @@ -6663,6 +6715,11 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsrs@0.21.0-ts7.1.0-beta: + resolution: {integrity: sha512-4aafVQhCMlJ9Qhjm3wT/a0o21HO7ajTDA6cSCnP4PVNU1fhQ2rTXLxJxEbhonz969Hh8s+dYOlFpaO4GoJMzDA==} + engines: {node: '>=16.20.0'} + hasBin: true + tsx@4.23.15: resolution: {integrity: sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==} engines: {node: '>=18.0.0'} @@ -8955,6 +9012,27 @@ snapshots: tsdown: 0.23.4(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.23.4)(@tsdown/exe@0.23.4)(@typescript/native-preview@7.0.0-dev.20260707.2)(@vitejs/devtools@0.8.2(crossws@0.4.17(srvx@1.0.6))(srvx@1.0.6)(vite@packages+core))(@volar/typescript@2.4.28)(@vue/language-core@3.3.12)(oxc-resolver@11.24.2)(publint@0.3.24)(tsx@4.23.15)(typescript@7.0.2)(unplugin-unused@0.5.6)(unrun@0.3.1)(vue-tsc@3.3.12(typescript@7.0.2)) verkit: 0.5.0 + '@tsrs/darwin-arm64@0.21.0-ts7.1.0-beta': + optional: true + + '@tsrs/darwin-x64@0.21.0-ts7.1.0-beta': + optional: true + + '@tsrs/linux-arm64-gnu@0.21.0-ts7.1.0-beta': + optional: true + + '@tsrs/linux-arm64-musl@0.21.0-ts7.1.0-beta': + optional: true + + '@tsrs/linux-x64-gnu@0.21.0-ts7.1.0-beta': + optional: true + + '@tsrs/linux-x64-musl@0.21.0-ts7.1.0-beta': + optional: true + + '@tsrs/win32-x64@0.21.0-ts7.1.0-beta': + optional: true + '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 @@ -12125,6 +12203,16 @@ snapshots: tslib@2.8.1: {} + tsrs@0.21.0-ts7.1.0-beta: + optionalDependencies: + '@tsrs/darwin-arm64': 0.21.0-ts7.1.0-beta + '@tsrs/darwin-x64': 0.21.0-ts7.1.0-beta + '@tsrs/linux-arm64-gnu': 0.21.0-ts7.1.0-beta + '@tsrs/linux-arm64-musl': 0.21.0-ts7.1.0-beta + '@tsrs/linux-x64-gnu': 0.21.0-ts7.1.0-beta + '@tsrs/linux-x64-musl': 0.21.0-ts7.1.0-beta + '@tsrs/win32-x64': 0.21.0-ts7.1.0-beta + tsx@4.23.15: dependencies: esbuild: 0.28.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a8d8f5840a..718c74cb81 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -67,6 +67,7 @@ catalog: source-map: 0.8.0 tinyglobby: ^0.2.15 tsdown: ^0.23.4 + tsrs: =0.21.0-ts7.1.0-beta typescript: ~7.0.2 valibot: 1.5.0 validate-npm-package-name: ^8.0.0 @@ -94,6 +95,7 @@ minimumReleaseAgeExclude: - '@oxlint/*' - '@rolldown/*' - '@tsdown/*' + - '@tsrs/*' - '@types/*' - '@vitejs/*' - '@vitest/*' @@ -119,6 +121,7 @@ minimumReleaseAgeExclude: - rolldown-plugin-dts - tinyexec - tsdown + - tsrs - tsx - unrun - verkit From 947811c08e02680b4b3a24b60a17897f1d379a98 Mon Sep 17 00:00:00 2001 From: MK Date: Thu, 5 Nov 2026 15:21:11 +0800 Subject: [PATCH 03/25] chore(deps): update tsrs to 0.22.0-ts7.1.0-beta --- pnpm-lock.yaml | 68 ++++++++++++++++++++++----------------------- pnpm-workspace.yaml | 2 +- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3c0d7904e4..a9f83a7d97 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -345,8 +345,8 @@ catalogs: specifier: ^0.23.4 version: 0.23.4 tsrs: - specifier: '=0.21.0-ts7.1.0-beta' - version: 0.21.0-ts7.1.0-beta + specifier: '=0.22.0-ts7.1.0-beta' + version: 0.22.0-ts7.1.0-beta typescript: specifier: ~7.0.2 version: 7.0.2 @@ -471,7 +471,7 @@ importers: version: 1.62.1 tsrs: specifier: 'catalog:' - version: 0.21.0-ts7.1.0-beta + version: 0.22.0-ts7.1.0-beta vite: specifier: workspace:@voidzero-dev/vite-plus-core@* version: link:../core @@ -3582,48 +3582,48 @@ packages: peerDependencies: tsdown: 0.23.4 - '@tsrs/darwin-arm64@0.21.0-ts7.1.0-beta': - resolution: {integrity: sha512-qBHxa+LmrNu/XYZjqLXV4qQPsHzxSI+dQTISxXuxyBaxtJVDwCwGAq9rVHKgaUX3U0bsyb8FyC1JIZFu4+EqZQ==} + '@tsrs/darwin-arm64@0.22.0-ts7.1.0-beta': + resolution: {integrity: sha512-mY0dMEtFxig4h5At3nByjZe8E4DTDPqqz73kkxQ6gqW5K9ym+u/VHQhtnpTF6ZOTkYyjP3bTaj5T8HcyoGVddA==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [darwin] - '@tsrs/darwin-x64@0.21.0-ts7.1.0-beta': - resolution: {integrity: sha512-7WUGrCYqz94dRpSTungOh90LL/5NhhnLQrMQqRYvmirHcp1sRlcVziJ2w+cynKwQ81/I2UajsNiKho/YSObcyA==} + '@tsrs/darwin-x64@0.22.0-ts7.1.0-beta': + resolution: {integrity: sha512-dvsSnyU8rpI3OqTIH2xxjt9ThvZC52BsL6/LS69eWg7Ieq2oni1PnLels5gGTFqvHFoRbezi5Y0KY7PQ1OmK5w==} engines: {node: '>=16.20.0'} cpu: [x64] os: [darwin] - '@tsrs/linux-arm64-gnu@0.21.0-ts7.1.0-beta': - resolution: {integrity: sha512-bNWkz4lu+bOzl+sl9btE4pQ0RbLkv0Zm4FE2oKvBrt56vCnl7khcAcsqpSxBXApOkp5OwvyyY+XuOGuROBt7yg==} + '@tsrs/linux-arm64-gnu@0.22.0-ts7.1.0-beta': + resolution: {integrity: sha512-igg42LxSN/DHFqafvDqZW2JNBMH8WIwc1JE/IY4QUDOXNhZnUfDgEu9xo1QYwbgryeo5llmhRwM4CI/BQUWF1Q==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [linux] libc: [glibc] - '@tsrs/linux-arm64-musl@0.21.0-ts7.1.0-beta': - resolution: {integrity: sha512-TwvKTiOD8Fwb3HB+CrhnpYgKf7He1BobtjefTja0iGAzDtfaiGwoF06zLUdmey27kmCmxjBPybS45VRyZBkQnQ==} + '@tsrs/linux-arm64-musl@0.22.0-ts7.1.0-beta': + resolution: {integrity: sha512-f0zCqMkxPof7QVwjdgA8Fb0n6BtX5MOdILH/xfS+He6gRLCoECky0ejbsgLWCpB06GMlsL1gCbDr0/tmc91APg==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [linux] libc: [musl] - '@tsrs/linux-x64-gnu@0.21.0-ts7.1.0-beta': - resolution: {integrity: sha512-0OAYWOp33xHJbY/zwowbq4IKg1aCzD98sb2ksZb/1um2l28HYhJyhMEEsNczLpUFipQDE+zVBNkFoBML/KRNvA==} + '@tsrs/linux-x64-gnu@0.22.0-ts7.1.0-beta': + resolution: {integrity: sha512-/e511w7xXmxBf14paz8eMLUbmlIc33Sy1E00y1FqmnUK04FTotWl+80mNvIVX5NrlOOJgrh+B125t1CHWu/8vQ==} engines: {node: '>=16.20.0'} cpu: [x64] os: [linux] libc: [glibc] - '@tsrs/linux-x64-musl@0.21.0-ts7.1.0-beta': - resolution: {integrity: sha512-OtYmWa1tzjSt/bXxotPBrPrzlfZqz+azkkfotmuYMQfxiOgmRRm7cl022iV4lE21EOUicqp0NAnuDGDLfDlNBg==} + '@tsrs/linux-x64-musl@0.22.0-ts7.1.0-beta': + resolution: {integrity: sha512-B3aDOdS5+h77HcFep+KtOI+XmLm2G+FQPQC3KPSDjS/IG0tY9GPSc14mYnLwgO++dXluukO/oufk3J4Lo0CFjg==} engines: {node: '>=16.20.0'} cpu: [x64] os: [linux] libc: [musl] - '@tsrs/win32-x64@0.21.0-ts7.1.0-beta': - resolution: {integrity: sha512-ITwz3wOx161DNwEC4qYrrRwWgQyZQR4Os/GA3g67dKml0CQ4LPWHXniCzXrMagwYyH2vBBhPYT+7o+bKDj0qtA==} + '@tsrs/win32-x64@0.22.0-ts7.1.0-beta': + resolution: {integrity: sha512-cZ69w+wOECbU6YF5UWi15TK73eX1pgoMEHKSmUF4K8PbS7DPIh/nWMsdb3rM8Ee1SUZ9ONCx+S6myh64SRi3Bg==} engines: {node: '>=16.20.0'} cpu: [x64] os: [win32] @@ -6715,8 +6715,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsrs@0.21.0-ts7.1.0-beta: - resolution: {integrity: sha512-4aafVQhCMlJ9Qhjm3wT/a0o21HO7ajTDA6cSCnP4PVNU1fhQ2rTXLxJxEbhonz969Hh8s+dYOlFpaO4GoJMzDA==} + tsrs@0.22.0-ts7.1.0-beta: + resolution: {integrity: sha512-DEnEqQZRM3sHCV7MnQKVeTh61U2SX+0WksVlWp4eVayy0NffQ2oiCOjgH/Eye3sMPYkGwpM3tht3HtVI9NdURg==} engines: {node: '>=16.20.0'} hasBin: true @@ -9012,25 +9012,25 @@ snapshots: tsdown: 0.23.4(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.23.4)(@tsdown/exe@0.23.4)(@typescript/native-preview@7.0.0-dev.20260707.2)(@vitejs/devtools@0.8.2(crossws@0.4.17(srvx@1.0.6))(srvx@1.0.6)(vite@packages+core))(@volar/typescript@2.4.28)(@vue/language-core@3.3.12)(oxc-resolver@11.24.2)(publint@0.3.24)(tsx@4.23.15)(typescript@7.0.2)(unplugin-unused@0.5.6)(unrun@0.3.1)(vue-tsc@3.3.12(typescript@7.0.2)) verkit: 0.5.0 - '@tsrs/darwin-arm64@0.21.0-ts7.1.0-beta': + '@tsrs/darwin-arm64@0.22.0-ts7.1.0-beta': optional: true - '@tsrs/darwin-x64@0.21.0-ts7.1.0-beta': + '@tsrs/darwin-x64@0.22.0-ts7.1.0-beta': optional: true - '@tsrs/linux-arm64-gnu@0.21.0-ts7.1.0-beta': + '@tsrs/linux-arm64-gnu@0.22.0-ts7.1.0-beta': optional: true - '@tsrs/linux-arm64-musl@0.21.0-ts7.1.0-beta': + '@tsrs/linux-arm64-musl@0.22.0-ts7.1.0-beta': optional: true - '@tsrs/linux-x64-gnu@0.21.0-ts7.1.0-beta': + '@tsrs/linux-x64-gnu@0.22.0-ts7.1.0-beta': optional: true - '@tsrs/linux-x64-musl@0.21.0-ts7.1.0-beta': + '@tsrs/linux-x64-musl@0.22.0-ts7.1.0-beta': optional: true - '@tsrs/win32-x64@0.21.0-ts7.1.0-beta': + '@tsrs/win32-x64@0.22.0-ts7.1.0-beta': optional: true '@tybys/wasm-util@0.10.3': @@ -12203,15 +12203,15 @@ snapshots: tslib@2.8.1: {} - tsrs@0.21.0-ts7.1.0-beta: + tsrs@0.22.0-ts7.1.0-beta: optionalDependencies: - '@tsrs/darwin-arm64': 0.21.0-ts7.1.0-beta - '@tsrs/darwin-x64': 0.21.0-ts7.1.0-beta - '@tsrs/linux-arm64-gnu': 0.21.0-ts7.1.0-beta - '@tsrs/linux-arm64-musl': 0.21.0-ts7.1.0-beta - '@tsrs/linux-x64-gnu': 0.21.0-ts7.1.0-beta - '@tsrs/linux-x64-musl': 0.21.0-ts7.1.0-beta - '@tsrs/win32-x64': 0.21.0-ts7.1.0-beta + '@tsrs/darwin-arm64': 0.22.0-ts7.1.0-beta + '@tsrs/darwin-x64': 0.22.0-ts7.1.0-beta + '@tsrs/linux-arm64-gnu': 0.22.0-ts7.1.0-beta + '@tsrs/linux-arm64-musl': 0.22.0-ts7.1.0-beta + '@tsrs/linux-x64-gnu': 0.22.0-ts7.1.0-beta + '@tsrs/linux-x64-musl': 0.22.0-ts7.1.0-beta + '@tsrs/win32-x64': 0.22.0-ts7.1.0-beta tsx@4.23.15: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 718c74cb81..4919dabbe8 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -67,7 +67,7 @@ catalog: source-map: 0.8.0 tinyglobby: ^0.2.15 tsdown: ^0.23.4 - tsrs: =0.21.0-ts7.1.0-beta + tsrs: =0.22.0-ts7.1.0-beta typescript: ~7.0.2 valibot: 1.5.0 validate-npm-package-name: ^8.0.0 From 2a152a99387d7938421c989f1ec62deae7d242de Mon Sep 17 00:00:00 2001 From: MK Date: Thu, 5 Nov 2026 16:08:34 +0800 Subject: [PATCH 04/25] docs(rfc): describe the default tsrs backend --- rfcs/check-command.md | 148 +++++++++++++++++++++++--------------- rfcs/migration-command.md | 2 +- rfcs/toolchain-command.md | 67 ++++++++++------- 3 files changed, 133 insertions(+), 84 deletions(-) diff --git a/rfcs/check-command.md b/rfcs/check-command.md index dedcdf8c1b..69f83c3912 100644 --- a/rfcs/check-command.md +++ b/rfcs/check-command.md @@ -35,33 +35,42 @@ vp check # Auto-fix format and lint issues vp check --fix -vp check --fix --no-lint # Only fix formatting +vp check --fix --no-lint # Skip lint rules; configured type checks still run # Disable specific checks vp check --no-fmt vp check --no-lint -vp check --no-type-aware -vp check --no-type-check ``` ### Options -| Flag | Default | Description | -| ---------------------------------- | ------- | ------------------------------------------------------- | -| `--fix` | OFF | Auto-fix format and lint issues | -| `--fmt` / `--no-fmt` | ON | Run format check (`vp fmt --check`) | -| `--lint` / `--no-lint` | ON | Run lint check (`vp lint`) | -| `--type-aware` / `--no-type-aware` | ON | Enable type-aware lint rules (oxlint `--type-aware`) | -| `--type-check` / `--no-type-check` | ON | Enable TypeScript type checking (oxlint `--type-check`) | -| `--no-error-on-unmatched-pattern` | OFF | Do not exit with error when pattern is unmatched | +| Flag | Default | Description | +| --------------------------------- | ------- | --------------------------------------------------------- | +| `--fix` | OFF | Auto-fix formatting and lint issues | +| `--no-fmt` | OFF | Skip formatting | +| `--no-lint` | OFF | Skip lint rules; retain configured TypeScript diagnostics | +| `--quiet` | OFF | Report errors without warnings | +| `--deny-warnings` | OFF | Treat lint warnings as errors | +| `--no-error-on-unmatched-pattern` | OFF | Do not fail when no files match | -**Flag dependency:** `--type-check` requires `--type-aware` as a prerequisite. +Type-aware rules and TypeScript diagnostics follow `lint.options` in +`vite.config.ts`. `vp create` and `vp migrate` enable both settings by default: -- `--type-aware` enables lint rules that use type information (e.g., `no-floating-promises`) -- `--type-check` enables experimental TypeScript compiler-level type checking (requires type-aware) -- If `--no-type-aware` is set, `--type-check` is also implicitly disabled +```ts +export default { + lint: { + options: { + typeAware: true, + typeCheck: true, + }, + }, +}; +``` -Both are enabled by default in `vp check` to provide comprehensive static analysis. +`typeAware` enables rules that need type information, such as +`typescript/no-floating-promises`. `typeCheck` also enables TypeScript compiler +diagnostics. Set either option to `false` in the config to disable it. +`vp check` does not expose `--no-type-aware` or `--no-type-check` flags. ### File Path Arguments @@ -94,7 +103,7 @@ Commands run **sequentially** with fail-fast semantics: ``` 1. vp fmt --check (verify formatting, don't auto-fix) -2. vp lint --type-aware --type-check (lint + type checking) +2. vp lint (lint + configured type checking) ``` If any step fails, `vp check` exits immediately with a non-zero exit code. @@ -112,9 +121,9 @@ Output rules: - Do not print delegated commands such as `vp fmt --check` or `vp lint --type-aware --type-check` - Print one `pass:` line only after a phase completes successfully -- Mention type checks in the lint success line only when `--type-check` is enabled +- Mention type checks in the lint success line only when `lint.options.typeCheck` is enabled - On failure, print a human-readable `error:` line, then raw diagnostics, then a blank line and a final summary sentence -- Treat `vp check --no-fmt --no-lint` as an error instead of silent success +- With `--no-fmt --no-lint`, run only configured TypeScript diagnostics. Report an error if no checks remain enabled. Representative failure output: @@ -180,21 +189,54 @@ Commands::Check { args } => commands::delegate::execute(cwd, "check", &args).awa ### NAPI Binding -The `Check` variant is defined in `SynthesizableSubcommand` in `packages/cli/binding/src/cli.rs`. The check command's orchestration logic lives in its own module at `packages/cli/binding/src/check/`, following the same directory-per-command pattern as `exec/`: +The `Check` variant is defined in `SynthesizableSubcommand` in `packages/cli/binding/src/cli/types.rs`. The check command's orchestration logic lives in its own module at `packages/cli/binding/src/check/`, following the same directory-per-command pattern as `exec/`: - `check/mod.rs` — `execute_check()` orchestration (runs fmt + lint sequentially, handles `--fix` re-formatting) - `check/analysis.rs` — Output analysis types (`CheckSummary`, `LintMessageKind`, etc.), parsers, and formatting helpers -The check module reuses `SubcommandResolver` and `resolve_and_capture_output` from `cli.rs` to resolve and run the underlying fmt/lint commands. +The check module reuses `SubcommandResolver` and `resolve_and_capture_output` from `cli/resolver.rs` and `cli/execution.rs` to resolve and run the underlying fmt/lint commands. ### TypeScript Side -No new resolver needed — `vp check` reuses existing `resolve-lint.ts` and `resolve-fmt.ts`. +`vp check` reuses `resolve-lint.ts` and `resolve-fmt.ts`. + +### Experimental type-aware backend + +This draft replaces the bundled Go backend with `tsrs@0.22.0-ts7.1.0-beta`, +a Rust port of the TypeScript 7 compiler. `resolve-lint.ts` selects the bundled +`tsrs` package and passes its `bin/tsrs-lint` launcher to Oxlint through +`OXLINT_TSGOLINT_PATH`. The same resolver serves `vp lint` and `vp check`. +Users do not need to set an environment variable for the default backend. + +An explicit `OXLINT_TSGOLINT_PATH` takes precedence over the resolver default. +The environment variable keeps its existing name because Oxlint uses it to +select the type-aware backend. A project can still select Go explicitly: + +```sh +vp add -D oxlint-tsgolint +OXLINT_TSGOLINT_PATH="$PWD/node_modules/.bin/tsgolint" vp check +``` + +`tsrs` is a production dependency. `oxlint-tsgolint` remains a development +dependency for comparisons. The benchmark selects the Go executable explicitly, +so a change to the default cannot make it compare tsrs against itself. +See the [performance report](../bench/tsrs-lint.md) for elapsed time, peak memory, +and measurement conditions. + +The pinned tsrs release has no Windows ARM64 binary. This draft adds no automatic +Go fallback. A release for all supported Vite+ platforms needs an upstream +Windows ARM64 binary or an explicit fallback. Runtime smoke tests currently +cover macOS ARM64; other platforms still need runtime validation. + +The backend switch preserves the existing formatting phase, lint configuration, +and failure handling. Smoke fixtures compare diagnostics, suppression comments, +and automatic fixes against Go. These checks do not establish complete +compiler parity. ### Key Files 1. `crates/vp_global_cli/src/cli.rs` — `Check` command variant and routing -2. `packages/cli/binding/src/cli.rs` — `SynthesizableSubcommand::Check` definition, delegates to `check` module +2. `packages/cli/binding/src/cli/types.rs` — `SynthesizableSubcommand::Check` definition, delegates to `check` module 3. `packages/cli/binding/src/check/mod.rs` — Check command orchestration (`execute_check`) 4. `packages/cli/binding/src/check/analysis.rs` — Output parsing and analysis types @@ -203,13 +245,14 @@ No new resolver needed — `vp check` reuses existing `resolve-lint.ts` and `res ``` Run format, lint, and type checks -Usage: vp check [OPTIONS] +Usage: vp check [OPTIONS] [PATHS]... Options: - --fmt Run format check [default: true] - --lint Run lint check [default: true] - --type-aware Enable type-aware linting [default: true] - --type-check Enable TypeScript type checking [default: true] + --fix Auto-fix format and lint issues + --quiet Report errors without warnings + --deny-warnings Treat lint warnings as errors + --no-fmt Skip format check + --no-lint Skip lint rules; retain configured type checks --no-error-on-unmatched-pattern Do not exit with error when no files match -h, --help Print help ``` @@ -261,16 +304,15 @@ pass: Found no warnings or lint errors in 2 files (452ms, 16 threads) The check command's cache fingerprint includes: -- **Environment variable:** `OXLINT_TSGOLINT_PATH` (affects lint behavior) +- **Environment variable:** `OXLINT_TSGOLINT_PATH` (an explicit Go or tsrs backend override affects lint behavior) - **Input files:** Auto-tracked via fspy, excluding: - - `node_modules/.vite-temp/**` — config compilation cache (read+written by the vp CLI subprocess) - `node_modules/.vite/task-cache/**` — task runner state files that change after each run -These exclusions are defined by `check_cache_inputs()` in `cli.rs`. +These exclusions are defined by `check_cache_inputs()` in `packages/cli/binding/src/cli/resolver.rs`. ### How it differs from `vp fmt` / `vp lint` -When `vp fmt` or `vp lint` appear in task scripts, the command handler resolves them to their underlying binaries (e.g., `node path/to/oxfmt.mjs`). The `vp check` command is different — it runs as a full `vp check` subprocess because it's a composite command that orchestrates both fmt and lint internally. This means the `vp` CLI process itself is tracked by fspy, which is why the `.vite-temp` and `.vite/task-cache` exclusions are necessary. +When `vp fmt` or `vp lint` appear in task scripts, the command handler resolves them to their underlying binaries (e.g., `node path/to/oxfmt.mjs`). The `vp check` command is different — it runs as a full `vp check` subprocess because it's a composite command that orchestrates both fmt and lint internally. This means the `vp` CLI process itself is tracked by fspy, which is why the `.vite/task-cache` exclusions are necessary. ## Comparison with Other Tools @@ -281,30 +323,20 @@ When `vp fmt` or `vp lint` appear in task scripts, the command handler resolves | **`biome check`** | **Format + lint (closest analog)** | | `deno check` | Type checking only | -## Snap Tests +## Snapshot Tests -``` -packages/cli/snap-tests/check-basic/ - package.json - steps.json # { "steps": [{ "command": "vp check" }] } - src/index.ts # Clean file that passes all checks - snap.txt - -packages/cli/snap-tests/check-fmt-fail/ - package.json - steps.json # { "steps": [{ "command": "vp check" }] } - src/index.ts # Badly formatted file - snap.txt # Shows fmt --check failure, lint doesn't run (fail-fast) - -packages/cli/snap-tests/check-no-fmt/ - package.json - steps.json # { "steps": [{ "command": "vp check --no-fmt" }] } - snap.txt # Only lint runs - -packages/cli/snap-tests/check-cache-enabled/ - package.json # { "scripts": { "check": "vp check" } } - vite.config.ts # { run: { cache: true } } - steps.json # Runs vp run check twice, expects cache hit on second run - src/index.js - snap.txt -``` +CLI cases live in `crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/`. +Each case uses `snapshots.toml` and a recorded Markdown snapshot. + +| Fixture | Coverage | +| ------------------------- | ------------------------------------------------------ | +| `check_pass_typecheck` | Formatting, linting, and configured type checks | +| `check_fmt_fail` | Formatting failure stops the command | +| `check_no_fmt` | Linting without formatting | +| `check_no_lint_typecheck` | Type checks without lint rules | +| `check_oxlint_env` | An explicit backend path overrides the bundled default | +| `check_cache_enabled` | A second `vp run check` replays cached output | +| `oxlint_typeaware` | Type-aware task caching tracks imported type files | + +Use the [snapshot runner instructions](../crates/vp_cli_snapshots/tests/cli_snapshots/README.md) +to run or update these cases. diff --git a/rfcs/migration-command.md b/rfcs/migration-command.md index 2d82152eb7..82cec43d34 100644 --- a/rfcs/migration-command.md +++ b/rfcs/migration-command.md @@ -40,7 +40,7 @@ When transitioning to Vite+, projects typically use standalone tools like vite, - ✅ **Configuration files**: - .oxlintrc → vite.config.ts (lint section) - .oxfmtrc → vite.config.ts (format section) -- ✅ **tsconfig.json cleanup**: Removes deprecated `esModuleInterop: false` (causes oxlint tsgolint errors) +- ✅ **tsconfig.json cleanup**: Removes deprecated `esModuleInterop: false` (causes Oxlint type-aware backend errors) **What this command optionally migrates** (prompted): diff --git a/rfcs/toolchain-command.md b/rfcs/toolchain-command.md index 7a407cccbc..5f94f55812 100644 --- a/rfcs/toolchain-command.md +++ b/rfcs/toolchain-command.md @@ -81,7 +81,7 @@ compatibility: 1. Vite+ distribution packages, including `vite-plus` and `@voidzero-dev/vite-plus-core`. 2. User-facing tools invoked or composed by Vite+, including Vite, Rolldown, - Vitest, Oxlint, Oxfmt, oxlint-tsgolint, tsdown, and Vite Task. + Vitest, Oxlint, Oxfmt, tsrs, tsdown, and Vite Task. 3. Bundled or compiled engines whose versions affect tool behavior. Version 1 includes Oxc and Oxc Resolver. @@ -164,13 +164,13 @@ vite-plus@0.2.4 │ └── bundles tsdown@0.22.3 ├── depends on vitest@4.1.10 ├── depends on oxlint@1.72.0 -├── depends on oxlint-tsgolint@0.24.0 +├── depends on tsrs@0.22.0-ts7.1.0-beta ├── depends on oxfmt@0.57.0 └── compiles vite-task (built 2026-08-06T09:30:00Z, revision ) ``` -These versions show the repository state when this RFC was written. They are -not part of the command contract. +These versions illustrate the output format. They are not part of the command +contract. The tsrs row reflects the experimental default backend. The readable tree can repeat a shared node to show two relationships. JSON has one entry for each node ID. @@ -209,12 +209,19 @@ Filters match a node's: Initial aliases include: -| Query | Node | -| ---------------- | ------------------------------ | -| `vite-plus-core` | `@voidzero-dev/vite-plus-core` | -| `tsgolint` | `oxlint-tsgolint` | -| `vite-task` | Vite Task | -| `oxc-resolver` | Oxc Resolver | +| Query | Node | +| ----------------- | ------------------------------ | +| `vite-plus-core` | `@voidzero-dev/vite-plus-core` | +| `tsrs-lint` | `tsrs` | +| `tsgolint` | `tsrs` | +| `oxlint-tsgolint` | `tsrs` | +| `vite-task` | Vite Task | +| `oxc-resolver` | Oxc Resolver | + +The experimental manifest uses `tsrs` as the backend node ID and package name. +It retains `tsgolint` and `oxlint-tsgolint` as aliases for existing queries. +These aliases select the bundled tsrs node; they do not report a separate Go +dependency. Older manifests retain their original `oxlint-tsgolint` node. Package and tool names are case-sensitive. This matches npm and Cargo names. @@ -412,6 +419,16 @@ the local Vite+ release to use the command. To show the global release, run It reads tool rows from the manifest. Use `vp toolchain` to select tools and show relationships or engine details. +For this experimental backend, the local version output and `vite-plus/versions` +report the `tsrs` version. The generated export uses the `tsrs` key in place of +`oxlint-tsgolint`. The global version command selects the tsrs row when the local +manifest contains it. It retains the Go row for older local manifests. + +The manifest describes the bundled backend. An `OXLINT_TSGOLINT_PATH` override +does not change its nodes or the version exports. The manifest also does not +certify platform support: the pinned tsrs release lacks a Windows ARM64 binary. +See the [backend design](./check-command.md#experimental-type-aware-backend). + ## Relationship to `vp why` `vp why` sends the command to the detected package manager. It keeps the @@ -486,21 +503,21 @@ The documentation states that `vp why` is a package-manager operation. ### CLI snapshot tests -New cases belong in `crates/vite_cli_snapshots/tests/cli_snapshots/`: - -| Scenario | Expected coverage | -| --------------------------------- | -------------------------------------------------------- | -| Full local manifest | Complete tree and local source | -| `vp toolchain vite` | Core, Vite, Rolldown, Oxc, and Oxc Resolver chain | -| Multiple filters | Stable union of branches | -| Alias filter | `vite-plus-core`, `vite-task`, and `tsgolint` resolution | -| `--json` | Valid JSON without header, styling, or trailing text | -| No local package | Global source selection | -| `--global` inside a local project | Global source forced | -| Old local Vite+ package | Unknown-command failure from the local CLI | -| Unknown tool | Status 1 and `vp why` hint | -| `vp why vite` | Package-manager output followed by toolchain hint | -| `vp why vite --json` | Unmodified JSON package-manager output | +New cases belong in `crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/`: + +| Scenario | Expected coverage | +| --------------------------------- | -------------------------------------------------------------------------- | +| Full local manifest | Complete tree and local source | +| `vp toolchain vite` | Core, Vite, Rolldown, Oxc, and Oxc Resolver chain | +| Multiple filters | Stable union of branches | +| Alias filter | `vite-plus-core`, `vite-task`, `tsrs-lint`, and legacy Go alias resolution | +| `--json` | Valid JSON without header, styling, or trailing text | +| No local package | Global source selection | +| `--global` inside a local project | Global source forced | +| Old local Vite+ package | Unknown-command failure from the local CLI | +| Unknown tool | Status 1 and `vp why` hint | +| `vp why vite` | Package-manager output followed by toolchain hint | +| `vp why vite --json` | Unmodified JSON package-manager output | Release artifact tests load the same manifest with each platform binding. The tests compare native versions with the compiled release inputs. From aaa2a6fae4ee2474135c0194d7fdde3e8b40cbae Mon Sep 17 00:00:00 2001 From: MK Date: Thu, 5 Nov 2026 16:22:24 +0800 Subject: [PATCH 05/25] bench(lint): add reproducible Codespaces resource comparison --- bench/tsrs-lint-codespaces/README.md | 95 +++++++ bench/tsrs-lint-codespaces/inputs/bench.py | 236 ++++++++++++++++++ bench/tsrs-lint-codespaces/inputs/docs.py | 11 + bench/tsrs-lint-codespaces/inputs/docs.sh | 1 + bench/tsrs-lint-codespaces/inputs/run.sh | 1 + bench/tsrs-lint-codespaces/inputs/setup.py | 150 +++++++++++ bench/tsrs-lint-codespaces/inputs/setup.sh | 1 + bench/tsrs-lint-codespaces/inputs/validate.sh | 1 + .../inputs/verify-setup.py | 6 + .../inputs/verify-setup.sh | 1 + bench/tsrs-lint-codespaces/stages.json | 46 ++++ rfcs/check-command.md | 5 +- 12 files changed, 552 insertions(+), 2 deletions(-) create mode 100644 bench/tsrs-lint-codespaces/README.md create mode 100644 bench/tsrs-lint-codespaces/inputs/bench.py create mode 100644 bench/tsrs-lint-codespaces/inputs/docs.py create mode 100644 bench/tsrs-lint-codespaces/inputs/docs.sh create mode 100644 bench/tsrs-lint-codespaces/inputs/run.sh create mode 100644 bench/tsrs-lint-codespaces/inputs/setup.py create mode 100644 bench/tsrs-lint-codespaces/inputs/setup.sh create mode 100644 bench/tsrs-lint-codespaces/inputs/validate.sh create mode 100644 bench/tsrs-lint-codespaces/inputs/verify-setup.py create mode 100644 bench/tsrs-lint-codespaces/inputs/verify-setup.sh create mode 100644 bench/tsrs-lint-codespaces/stages.json diff --git a/bench/tsrs-lint-codespaces/README.md b/bench/tsrs-lint-codespaces/README.md new file mode 100644 index 0000000000..a43b01cc7c --- /dev/null +++ b/bench/tsrs-lint-codespaces/README.md @@ -0,0 +1,95 @@ +# Codespaces comparison of lint backends + +This recipe compares the explicit Go override with the default tsrs backend in +the same built CLI. It runs three workloads on both Vite+ and vinext: type-aware +rules, rules with TypeScript diagnostics, and the full `vp check` command. + +Use the [codespaces-run skill](https://github.com/Dunqing/skills/tree/main/skills/codespaces-run). +The recipe uses one `standardLinux32gb` instance with four CPUs and 16 GB of RAM. +The skill retains the environment and stops the instance after artifact collection. +Do not run other builds or benchmarks on the instance during measurement. + +## Source and setup + +The measured CLI source is `22b07bd150aa57b3e04cc52e0b81d43294ffe982`. +The RFC patch from `da260f86fce1d43810c62e4e6aad011066c88cb9` preserves the +documentation edits present when this comparison started. The vinext corpus is +`249088af307c0a852fa6dc59c08fc6ec80e1620e` from `fengmk2/vinext`. + +Setup installs Node.js `24.22.2` for Vite+ and `26.13.0` for vinext. It installs +the package managers declared by those revisions: pnpm `12.11.2` and `11.1.1`. +Both projects and Vite+'s separate documentation workspace use frozen lockfiles. +The Python environment pins `psutil==7.0.0`. +Node downloads are checked against the official SHA256 files. + +The recipe builds the PR's CLI JavaScript. It uses the published Vite+ `1.5.0` +native binding, core, and prompts packages, plus the matching Rolldown binding. +It does not compile a complete native release. The setup receipt records hashes +of the CLI entry point, native binding, Node executables, and benchmark config. + +The repository's Ubuntu devcontainer can lack an SSH server. If `gh codespace ssh` +reports that problem, open the existing Codespace's web terminal and install +`openssh-server` with `sudo apt-get update` and +`sudo apt-get install -y --no-install-recommends openssh-server`. +Keep the repository's devcontainer configuration unchanged. Setup installs +`python3-venv` only if Python cannot import `ensurepip`. + +## Run + +Run these commands from this checkout. The temporary input directory stays +outside the source tree so it cannot change the measured file set. + +```sh +tsrs_skill_tool="${CODEX_HOME:-$HOME/.codex}/skills/codespaces-run/scripts/sandbox.py" +tsrs_bench_inputs="$(mktemp -d)" +cp bench/tsrs-lint-codespaces/inputs/* "$tsrs_bench_inputs/" +git diff 22b07bd150aa57b3e04cc52e0b81d43294ffe982 da260f86fce1d43810c62e4e6aad011066c88cb9 \ + -- rfcs/check-command.md rfcs/toolchain-command.md rfcs/migration-command.md \ + > "$tsrs_bench_inputs/rfcs.patch" + +python3 "$tsrs_skill_tool" submit \ + --repo voidzero-dev/vite-plus \ + --git-url https://github.com/voidzero-dev/vite-plus.git \ + --git-commit 22b07bd150aa57b3e04cc52e0b81d43294ffe982 \ + --stages "$PWD/bench/tsrs-lint-codespaces/stages.json" \ + --inputs "$tsrs_bench_inputs" \ + --machine standardLinux32gb --cpus 4 --min-memory-mib 14000 \ + --timeout 7200 --profile default \ + --environment-key tsrs-022-linux-x64-node24.22.2-26.13.0-pnpm12.11.2-11.1.1 + +python3 "$tsrs_skill_tool" events JOB_ID --follow +``` + +Use the returned job ID in the last command. Check the final receipt for +`collected: true`, a shutdown timestamp, and no cleanup error. Logs, raw samples, +memory traces, and results are retained under the skill's local job directory. +If a later stage fails, use the skill's stage recovery procedure to retain a +verified setup. Do not start another job while the previous job holds the lease. + +## Method + +Validation runs each backend once for each workload before measurement. Every +run must exit successfully with no diagnostics and the expected file and rule +counts. Full checks must also report successful formatting. Memory runs check +the native executable identity to detect accidental comparisons of one backend +against itself. + +Timing uses ten pairs after one warm-up per backend. Each pair reverses the +previous pair's order. Memory uses a separate five-pair phase with its own +warm-ups. Each invocation starts a fresh CLI process. Installation, builds, +validation, and memory sampling are excluded from the timing table. + +The memory sampler records the simultaneous sum of resident set size (RSS) for +the CLI and its descendants. It sleeps for 20 ms between samples and records the +actual intervals. This includes Node, Oxlint, and the native type-aware backend. +Shared pages can count more than once, and sampling can miss short peaks. The +result is a sampled process-tree peak, not total allocation or container memory. + +Lint uses four threads. Vinext lint uses `OXLINT_TSGOLINT_CHECKER_SCHEDULING=sorted`, +as in the earlier comparison. Full checks use the machine's default thread count +and backend scheduling. The rules-only configuration disables `typeCheck` while +preserving the project's original lint rules and plugins. + +The summary reports medians and ranges. Warm-up samples have `pair=-1` and do +not contribute to the medians. Each full run produces 204 records, including +warm-ups, plus the separate 12-command validation phase. diff --git a/bench/tsrs-lint-codespaces/inputs/bench.py b/bench/tsrs-lint-codespaces/inputs/bench.py new file mode 100644 index 0000000000..bae07e9059 --- /dev/null +++ b/bench/tsrs-lint-codespaces/inputs/bench.py @@ -0,0 +1,236 @@ +import argparse +import csv +import hashlib +import json +import os +import pathlib +import platform +import re +import statistics +import subprocess +import tempfile +import time + +import psutil + +ENV = pathlib.Path(os.environ['JOB_ENV_DIR']) +OUT = pathlib.Path(os.environ['ARTIFACTS_DIR']) +CONFIG = json.loads((ENV / 'bench-config.json').read_text()) +BASE_ENV = {key: value for key, value in os.environ.items() + if not key.startswith(('OXLINT_TSGOLINT', 'TSRS_')) + and key not in ('NODE_OPTIONS', 'NODE_PATH', 'GITHUB_ACTIONS')} +EXPECTED = {} +RECORDS = [] +SCENARIOS = ('rules', 'full', 'check') +TIMING_PAIRS = 10 +MEMORY_PAIRS = 5 + + +def progress(phase, message, completed=0, total=0): + value = {'phase': phase, 'message': message, 'completed': completed, 'total': total} + subprocess.run(['python3', os.environ['JOB_WORKER'], 'progress', os.environ['JOB_DIR'], json.dumps(value)], check=True) + print(json.dumps(value), flush=True) + + +def environment(project, scenario, backend): + env = BASE_ENV | { + 'PATH': str(pathlib.Path(project['node']).parent) + ':' + BASE_ENV['PATH'], + 'VP_HOME': str(ENV / 'vp-home'), 'VP_SELF_SETUP_NO_MODIFY_PATH': '1', + } + if scenario != 'check' and project['scheduling']: + env['OXLINT_TSGOLINT_CHECKER_SCHEDULING'] = project['scheduling'] + if backend == 'go': + env['OXLINT_TSGOLINT_PATH'] = CONFIG['go'] + return env + + +def lint_config(project): + code = ''' +import { createRequire } from 'node:module'; +import { pathToFileURL } from 'node:url'; +const require = createRequire(process.argv[1]); +const { loadConfigFromFile } = await import(pathToFileURL(require.resolve('vite')).href); +const result = await loadConfigFromFile({ command: 'serve', mode: 'development' }, undefined, process.cwd(), 'silent'); +if (!result?.config?.lint) throw new Error('Missing lint configuration'); +process.stdout.write(JSON.stringify(result.config.lint)); +''' + cli_package = str(pathlib.Path(CONFIG['cli']).parent.parent / 'package.json') + env = environment(project, 'rules', 'rust') | {'VP_RESOLVING_CONFIG_METADATA': '1'} + output = subprocess.check_output([project['node'], '--input-type=module', '-e', code, cli_package], cwd=project['root'], env=env, text=True) + original = json.loads(output) + (OUT / (project['name'] + '-lint-config.json')).write_text(json.dumps(original, indent=2) + '\n') + config = json.loads(output) + config.setdefault('options', {})['typeAware'] = True + config['options']['typeCheck'] = False + return config + + +def signature(project, scenario, stdout, stderr): + if scenario == 'check': + combined = stdout + stderr + formatted = re.search(r'pass: All ([\d,]+) files are correctly formatted', combined) + linted = re.search(r'pass: Found no warnings, lint errors, or type errors in ([\d,]+) files', combined) + assert formatted and linted, combined + files = int(linted[1].replace(',', '')) + result = {'files': files, 'format_files': int(formatted[1].replace(',', ''))} + else: + data = json.loads(stdout) + assert not data['diagnostics'], data['diagnostics'][:5] + files = data['number_of_files'] + assert data['number_of_rules'] == project['rules'], data + result = {key: data[key] for key in ('number_of_files', 'number_of_rules', 'threads_count', 'diagnostics')} + assert files == project['files'], (project['name'], files, project['files']) + key = (project['name'], scenario) + if key in EXPECTED: + assert result == EXPECTED[key], (key, result, EXPECTED[key]) + else: + EXPECTED[key] = result + return result + + +def sample(child, start): + root = psutil.Process(child.pid) + seen = {child.pid: root.create_time()} + trace = [] + processes = {} + while child.poll() is None: + try: + for process in root.children(recursive=True): + seen[process.pid] = process.create_time() + except psutil.Error: + pass + alive = [] + for pid, born in list(seen.items()): + try: + process = psutil.Process(pid) + with process.oneshot(): + if process.create_time() != born or process.status() == psutil.STATUS_ZOMBIE: + continue + rss = process.memory_info().rss + exe = process.exe() + elapsed = time.perf_counter() - start + key = f'{pid}:{exe}' + record = processes.setdefault(key, {'pid': pid, 'exe': exe, 'first': elapsed, 'peak_rss': 0}) + record['last'] = elapsed + record['peak_rss'] = max(record['peak_rss'], rss) + alive.append({'pid': pid, 'exe': exe, 'rss': rss}) + except psutil.Error: + pass + trace.append({'seconds': time.perf_counter() - start, 'rss': sum(p['rss'] for p in alive), 'processes': alive}) + time.sleep(0.02) + if time.perf_counter() - start > 300: + child.kill() + raise TimeoutError('Benchmark command exceeded 300 seconds') + return trace, list(processes.values()) + + +def run(project, scenario, backend, pair, phase, config=None): + args = ['check'] if scenario == 'check' else ['lint', '--type-aware', '--threads', '4', '--format', 'json'] + if scenario == 'full': + args += ['--type-check'] + if config: + args += ['-c', str(config)] + command = [project['node'], CONFIG['cli'], *args] + directory = OUT / phase / project['name'] + directory.mkdir(parents=True, exist_ok=True) + stem = directory / f'{scenario}-{backend}-{pair:02d}' + with stem.with_suffix('.stdout').open('w') as stdout, stem.with_suffix('.stderr').open('w') as stderr: + start = time.perf_counter() + child = subprocess.Popen(command, cwd=project['root'], env=environment(project, scenario, backend), stdout=stdout, stderr=stderr) + trace, processes = sample(child, start) if phase in ('validate', 'memory') else ([], []) + # Blocking wait avoids the polling latency of wait(timeout=...). + # The Codespaces worker enforces the overall task deadline. + status = child.wait() + seconds = time.perf_counter() - start + stdout = stem.with_suffix('.stdout').read_text() + stderr = stem.with_suffix('.stderr').read_text() + assert status == 0, (project['name'], scenario, backend, status, stdout[-5000:], stderr[-5000:]) + result = signature(project, scenario, stdout, stderr) + record = {'project': project['name'], 'scenario': scenario, 'backend': backend, + 'phase': phase, 'pair': pair, 'seconds': seconds, 'exit': status, + 'peak_rss': max((x['rss'] for x in trace), default=None), 'signature': result} + if trace: + native = [p for p in processes if pathlib.Path(p['exe']).name in ('tsgolint', 'tsrs')] + assert len(native) == 1, native + assert pathlib.Path(native[0]['exe']).name == ('tsgolint' if backend == 'go' else 'tsrs') + intervals = [b['seconds'] - a['seconds'] for a, b in zip(trace, trace[1:])] + record.update({'native': native[0], 'median_sample_interval_ms': statistics.median(intervals) * 1000}) + stem.with_suffix('.trace.json').write_text(json.dumps(trace)) + stem.with_suffix('.processes.json').write_text(json.dumps(processes, indent=2)) + stem.with_suffix('.json').write_text(json.dumps(record, indent=2) + '\n') + RECORDS.append(record) + (OUT / 'samples.json').write_text(json.dumps(RECORDS, indent=2) + '\n') + print(project['name'], scenario, backend, phase, pair, f'{seconds:.3f}s', record['peak_rss'], flush=True) + return record + + +def measure(project, scenario, phase): + handle = None + if scenario == 'rules': + handle = tempfile.NamedTemporaryFile(mode='w', suffix='.json', prefix='.tsrs-bench-', dir=project['root']) + json.dump(lint_config(project), handle) + handle.flush() + try: + pairs = 0 if phase == 'validate' else TIMING_PAIRS if phase == 'time' else MEMORY_PAIRS + for pair in range(-1, pairs): + backends = ('go', 'rust') if pair < 0 or pair % 2 == 0 else ('rust', 'go') + for backend in backends: + run(project, scenario, backend, pair, phase, handle.name if handle else None) + finally: + if handle: + handle.close() + + +def metadata(): + info = {'vp_commit': CONFIG['vp_commit'], 'python': platform.python_version(), 'psutil': psutil.__version__, + 'platform': platform.platform(), 'cpu_affinity': psutil.Process().cpu_affinity(), + 'memory_bytes': psutil.virtual_memory().total, 'timing_pairs': TIMING_PAIRS, + 'memory_pairs': MEMORY_PAIRS, 'sample_sleep_ms': 20, 'projects': CONFIG['projects'], + 'versions': json.loads((ENV / 'setup-receipt.json').read_text())['versions']} + for name, args in [('cpu', ['lscpu']), ('git_diff', ['git', 'diff', '--stat']), ('source', ['git', 'rev-parse', 'HEAD'])]: + info[name] = subprocess.check_output(args, text=True) + info['node_versions'] = {p['name']: subprocess.check_output([p['node'], '--version'], text=True).strip() for p in CONFIG['projects']} + info['cpu_percent_before'] = psutil.cpu_percent(interval=1) + (OUT / 'metadata.json').write_text(json.dumps(info, indent=2) + '\n') + + +def summarize(): + rows = [] + for project in CONFIG['projects']: + for scenario in SCENARIOS: + row = {'project': project['name'], 'scenario': scenario} + for backend in ('go', 'rust'): + for phase, metric in [('time', 'seconds'), ('memory', 'peak_rss')]: + values = [r[metric] for r in RECORDS if r['project'] == project['name'] and r['scenario'] == scenario and r['backend'] == backend and r['phase'] == phase and r['pair'] >= 0] + row[f'{backend}_{metric}'] = statistics.median(values) + row[f'{backend}_{metric}_range'] = [min(values), max(values)] + row['time_saved_percent'] = 100 * (1 - row['rust_seconds'] / row['go_seconds']) + row['rss_saved_percent'] = 100 * (1 - row['rust_peak_rss'] / row['go_peak_rss']) + rows.append(row) + (OUT / 'summary.json').write_text(json.dumps(rows, indent=2) + '\n') + with (OUT / 'samples.csv').open('w') as file: + keys = ['project', 'scenario', 'backend', 'phase', 'pair', 'seconds', 'peak_rss', 'exit'] + writer = csv.DictWriter(file, fieldnames=keys, extrasaction='ignore') + writer.writeheader() + writer.writerows(RECORDS) + print('RESULTS', json.dumps(rows), flush=True) + + +parser = argparse.ArgumentParser() +parser.add_argument('--validate-only', action='store_true') +options = parser.parse_args() +metadata() +phases = ('validate',) if options.validate_only else ('time', 'memory') +total = len(phases) * len(CONFIG['projects']) * len(SCENARIOS) +completed = 0 +for phase in phases: + for project in CONFIG['projects']: + for scenario in SCENARIOS: + progress(phase, f"{project['name']}: {scenario}", completed, total) + measure(project, scenario, phase) + completed += 1 + progress(phase, f"Completed {project['name']}: {scenario}", completed, total) +if not options.validate_only: + summarize() +assert len(RECORDS) == (12 if options.validate_only else 204), len(RECORDS) +progress('validated' if options.validate_only else 'measured', 'All output assertions passed', total, total) diff --git a/bench/tsrs-lint-codespaces/inputs/docs.py b/bench/tsrs-lint-codespaces/inputs/docs.py new file mode 100644 index 0000000000..cc1cef3543 --- /dev/null +++ b/bench/tsrs-lint-codespaces/inputs/docs.py @@ -0,0 +1,11 @@ +import os +import pathlib +import subprocess + +root = pathlib.Path.cwd() +env_root = pathlib.Path(os.environ['JOB_ENV_DIR']) +env = os.environ | {'PATH': str(env_root / 'node-v24.22.2-linux-x64/bin') + ':' + os.environ['PATH']} +subprocess.run([ + env_root / 'pnpm12/node_modules/.bin/pnpm', 'install', '--frozen-lockfile', + '--ignore-scripts', '--store-dir', env_root / 'pnpm-store', +], cwd=root / 'docs', env=env, check=True) diff --git a/bench/tsrs-lint-codespaces/inputs/docs.sh b/bench/tsrs-lint-codespaces/inputs/docs.sh new file mode 100644 index 0000000000..0ff5cba507 --- /dev/null +++ b/bench/tsrs-lint-codespaces/inputs/docs.sh @@ -0,0 +1 @@ +python3 "$JOB_INPUT_DIR/docs.py" diff --git a/bench/tsrs-lint-codespaces/inputs/run.sh b/bench/tsrs-lint-codespaces/inputs/run.sh new file mode 100644 index 0000000000..68be2b7d7b --- /dev/null +++ b/bench/tsrs-lint-codespaces/inputs/run.sh @@ -0,0 +1 @@ +"$JOB_ENV_DIR/venv/bin/python" "$JOB_INPUT_DIR/bench.py" diff --git a/bench/tsrs-lint-codespaces/inputs/setup.py b/bench/tsrs-lint-codespaces/inputs/setup.py new file mode 100644 index 0000000000..32fa921a55 --- /dev/null +++ b/bench/tsrs-lint-codespaces/inputs/setup.py @@ -0,0 +1,150 @@ +import hashlib +import json +import os +import pathlib +import shutil +import subprocess +import urllib.request + +ROOT = pathlib.Path.cwd() +ENV = pathlib.Path(os.environ['JOB_ENV_DIR']) +ENV.mkdir(parents=True, exist_ok=True) +INPUT = pathlib.Path(os.environ['JOB_INPUT_DIR']) +OUT = pathlib.Path(os.environ['ARTIFACTS_DIR']) +VP_COMMIT = '22b07bd150aa57b3e04cc52e0b81d43294ffe982' +VINEXT_COMMIT = '249088af307c0a852fa6dc59c08fc6ec80e1620e' + + +def run(args, cwd=ROOT, env=None): + print('+', ' '.join(map(str, args)), flush=True) + subprocess.run(list(map(str, args)), cwd=cwd, env=env, check=True) + + +def progress(message): + run(['python3', os.environ['JOB_WORKER'], 'progress', os.environ['JOB_DIR'], + json.dumps({'phase': 'setup', 'message': message})]) + + +def fetch_git(url, revision, destination): + if not (destination / '.git').exists(): + destination.mkdir(parents=True, exist_ok=True) + run(['git', 'init', destination]) + run(['git', 'remote', 'add', 'origin', url], cwd=destination) + run(['git', 'fetch', '--depth', '1', 'origin', revision], cwd=destination) + run(['git', 'checkout', '--detach', 'FETCH_HEAD'], cwd=destination) + actual = subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=destination, text=True).strip() + assert actual == revision, (destination, actual, revision) + + +def node(version): + target = ENV / f'node-v{version}-linux-x64' + if not (target / 'bin/node').exists(): + filename = target.name + '.tar.xz' + base = f'https://nodejs.org/dist/v{version}/' + with urllib.request.urlopen(base + 'SHASUMS256.txt') as response: + checksums = response.read().decode() + (OUT / f'node-{version}-SHASUMS256.txt').write_text(checksums) + expected = next(line.split()[0] for line in checksums.splitlines() if line.split()[-1] == filename) + archive = ENV / filename + urllib.request.urlretrieve(base + filename, archive) + assert hashlib.sha256(archive.read_bytes()).hexdigest() == expected + run(['tar', '-xJf', archive, '-C', ENV]) + assert subprocess.check_output([target / 'bin/node', '--version'], text=True).strip() == 'v' + version + return target + + +def npm(prefix, packages, runtime): + prefix.mkdir(parents=True, exist_ok=True) + env = os.environ | {'PATH': str(runtime / 'bin') + ':' + os.environ['PATH']} + run([runtime / 'bin/npm', 'install', '--prefix', prefix, '--no-audit', '--no-fund', + '--ignore-scripts', '--package-lock=false', *packages], env=env) + + +def copy_dir(source, destination): + if destination.is_symlink(): + destination.unlink() + shutil.copytree(source, destination, dirs_exist_ok=True) + + +progress('Installing pinned Node and package-manager versions') +assert subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip() == VP_COMMIT +patch = INPUT / 'rfcs.patch' +if patch.stat().st_size: + reverse = subprocess.run(['git', 'apply', '--reverse', '--check', str(patch)], capture_output=True) + if reverse.returncode: + run(['git', 'apply', patch]) +node24 = node('24.22.2') +node26 = node('26.13.0') +npm(ENV / 'pnpm12', ['pnpm@12.11.2'], node24) +# pnpm 12 ships a shebang-less placeholder until its native installer runs. +run([node24 / 'bin/node', ENV / 'pnpm12/node_modules/pnpm/install.js']) +npm(ENV / 'pnpm11', ['pnpm@11.1.1'], node26) +for prefix, version in [('pnpm12', '12.11.2'), ('pnpm11', '11.1.1')]: + pm_env = os.environ | {'PATH': str(node26 / 'bin') + ':' + os.environ['PATH']} + assert subprocess.check_output([ENV / prefix / 'node_modules/.bin/pnpm', '--version'], cwd=ENV, env=pm_env, text=True).strip() == version +if not (ENV / 'venv/bin/python').exists(): + # The repository's minimal Ubuntu image can omit ensurepip. + check = subprocess.run(['python3', '-c', 'import ensurepip'], capture_output=True, text=True) + if check.returncode: + if 'No module named' not in check.stderr or 'ensurepip' not in check.stderr: + raise RuntimeError(check.stderr) + progress('Installing python3-venv because the container lacks ensurepip') + run(['sudo', 'apt-get', 'update']) + run(['sudo', 'env', 'DEBIAN_FRONTEND=noninteractive', 'apt-get', 'install', '-y', 'python3-venv']) + run(['python3', '-m', 'venv', ENV / 'venv']) +run([ENV / 'venv/bin/python', '-m', 'pip', 'install', 'psutil==7.0.0']) +base_env = os.environ | { + 'PATH': str(node24 / 'bin') + ':' + str(ENV / 'pnpm12/node_modules/.bin') + ':' + os.environ['PATH'], + 'VP_HOME': str(ENV / 'vp-home'), 'VP_SELF_SETUP_NO_MODIFY_PATH': '1', + 'CARGO_HOME': str(ENV / 'cargo'), 'CARGO_TARGET_DIR': str(ENV / 'cargo-target'), +} +progress('Fetching exact source dependencies and published native artifacts') +for name, spec in json.loads((ROOT / 'packages/tools/.upstream-versions.json').read_text()).items(): + fetch_git(spec['repo'], spec['hash'], ROOT / name) +run([node24 / 'bin/node', 'packages/tools/src/vendored-vitest.ts'], env=base_env) +npm(ENV / 'published', [ + 'vite-plus@1.5.0', '@voidzero-dev/vite-plus-core@1.5.0', + '@voidzero-dev/vite-plus-linux-x64-gnu@1.5.0', + '@voidzero-dev/vite-plus-prompts@1.5.0', 'rolldown@1.2.16', +], node24) +run([ENV / 'pnpm12/node_modules/.bin/pnpm', 'install', '--frozen-lockfile', '--ignore-scripts', + '--store-dir', ENV / 'pnpm-store'], env=base_env) +modules = ENV / 'published/node_modules' +for source, destination in [ + (modules / 'vite-plus/dist', ROOT / 'packages/cli/dist'), + (modules / '@voidzero-dev/vite-plus-core/dist', ROOT / 'packages/core/dist'), + (modules / '@voidzero-dev/vite-plus-prompts/dist', ROOT / 'packages/prompts/dist'), + (modules / 'rolldown/dist', ROOT / 'rolldown/packages/rolldown/dist'), + (modules / '@rolldown/binding-linux-x64-gnu', ROOT / 'rolldown/packages/rolldown/node_modules/@rolldown/binding-linux-x64-gnu'), + (modules / '@voidzero-dev/vite-plus-linux-x64-gnu', ROOT / 'packages/core/node_modules/@voidzero-dev/vite-plus-linux-x64-gnu'), +]: + copy_dir(source, destination) +native = modules / '@voidzero-dev/vite-plus-linux-x64-gnu/vite-plus.linux-x64-gnu.node' +shutil.copy2(native, ROOT / 'packages/cli/binding' / native.name) +progress('Building CLI JavaScript; using the published 1.5.0 Linux native binding and core') +run([ENV / 'pnpm12/node_modules/.bin/pnpm', '--filter', 'vite-plus', 'build-ts'], env=base_env) +progress('Preparing the pinned vinext benchmark corpus and generated types') +vinext = ENV / 'vinext' +fetch_git('https://github.com/fengmk2/vinext.git', VINEXT_COMMIT, vinext) +vinext_env = base_env | {'PATH': str(node26 / 'bin') + ':' + str(ENV / 'pnpm11/node_modules/.bin') + ':' + os.environ['PATH']} +run([ENV / 'pnpm11/node_modules/.bin/pnpm', 'install', '--frozen-lockfile', '--ignore-scripts', + '--store-dir', ENV / 'pnpm-store-vinext'], cwd=vinext, env=vinext_env) +run([ENV / 'pnpm11/node_modules/.bin/pnpm', 'run', 'postinstall'], cwd=vinext, env=vinext_env) +config = { + 'vp_commit': VP_COMMIT, 'cli': str(ROOT / 'packages/cli/bin/vp'), + 'go': str(ROOT / 'packages/cli/node_modules/.bin/tsgolint'), + 'tsrs': str(ROOT / 'packages/cli/node_modules/.bin/tsrs-lint'), + 'projects': [ + {'name': 'vite-plus', 'root': str(ROOT), 'node': str(node24 / 'bin/node'), 'files': 385, 'rules': 174, 'scheduling': None}, + {'name': 'vinext', 'root': str(vinext), 'node': str(node26 / 'bin/node'), 'files': 1828, 'rules': 151, 'scheduling': 'sorted', 'commit': VINEXT_COMMIT}, + ], +} +(ENV / 'bench-config.json').write_text(json.dumps(config, indent=2) + '\n') +versions = {} +for package in ['tsrs', 'oxlint', 'oxlint-tsgolint']: + versions[package] = json.loads((ROOT / 'packages/cli/node_modules' / package / 'package.json').read_text())['version'] +assert versions == {'tsrs': '0.22.0-ts7.1.0-beta', 'oxlint': '1.91.0', 'oxlint-tsgolint': '7.0.2005'}, versions +files = [ROOT / 'packages/cli/dist/bin.js', native, node24 / 'bin/node', node26 / 'bin/node', ENV / 'bench-config.json'] +receipt = {'versions': versions, 'hashes': {str(p): hashlib.sha256(p.read_bytes()).hexdigest() for p in files}} +(ENV / 'setup-receipt.json').write_text(json.dumps(receipt, indent=2) + '\n') +print('SETUP_COMPLETE', json.dumps(versions), flush=True) diff --git a/bench/tsrs-lint-codespaces/inputs/setup.sh b/bench/tsrs-lint-codespaces/inputs/setup.sh new file mode 100644 index 0000000000..b2ff6948e5 --- /dev/null +++ b/bench/tsrs-lint-codespaces/inputs/setup.sh @@ -0,0 +1 @@ +python3 "$JOB_INPUT_DIR/setup.py" diff --git a/bench/tsrs-lint-codespaces/inputs/validate.sh b/bench/tsrs-lint-codespaces/inputs/validate.sh new file mode 100644 index 0000000000..1f8e6ed6c9 --- /dev/null +++ b/bench/tsrs-lint-codespaces/inputs/validate.sh @@ -0,0 +1 @@ +"$JOB_ENV_DIR/venv/bin/python" "$JOB_INPUT_DIR/bench.py" --validate-only diff --git a/bench/tsrs-lint-codespaces/inputs/verify-setup.py b/bench/tsrs-lint-codespaces/inputs/verify-setup.py new file mode 100644 index 0000000000..e14726debc --- /dev/null +++ b/bench/tsrs-lint-codespaces/inputs/verify-setup.py @@ -0,0 +1,6 @@ +import hashlib,json,os,pathlib +root=pathlib.Path(os.environ['JOB_ENV_DIR']) +receipt=json.loads((root/'setup-receipt.json').read_text()) +for name,expected in receipt['hashes'].items(): + assert hashlib.sha256(pathlib.Path(name).read_bytes()).hexdigest()==expected,name +print('Setup artifacts verified') diff --git a/bench/tsrs-lint-codespaces/inputs/verify-setup.sh b/bench/tsrs-lint-codespaces/inputs/verify-setup.sh new file mode 100644 index 0000000000..3968785a51 --- /dev/null +++ b/bench/tsrs-lint-codespaces/inputs/verify-setup.sh @@ -0,0 +1 @@ +python3 "$JOB_INPUT_DIR/verify-setup.py" diff --git a/bench/tsrs-lint-codespaces/stages.json b/bench/tsrs-lint-codespaces/stages.json new file mode 100644 index 0000000000..5506f61409 --- /dev/null +++ b/bench/tsrs-lint-codespaces/stages.json @@ -0,0 +1,46 @@ +{ + "stages": [ + { + "name": "setup", + "script": "setup.sh", + "inputs": { + "workspace": [ + "pnpm-lock.yaml", + "pnpm-workspace.yaml", + "packages/cli/package.json", + "packages/tools/.upstream-versions.json" + ], + "inputs": ["setup.py", "verify-setup.py", "rfcs.patch"] + }, + "outputs": { + "env": ["setup-receipt.json", "bench-config.json"] + }, + "verify": "verify-setup.sh" + }, + { + "name": "docs", + "script": "docs.sh", + "inputs": { + "workspace": ["docs/package.json", "docs/pnpm-lock.yaml", "docs/pnpm-workspace.yaml"], + "inputs": ["docs.py"], + "env": ["setup-receipt.json"] + } + }, + { + "name": "validate", + "script": "validate.sh", + "inputs": { + "inputs": ["bench.py"], + "env": ["setup-receipt.json", "bench-config.json"] + } + }, + { + "name": "benchmark", + "script": "run.sh", + "inputs": { + "inputs": ["bench.py"], + "env": ["setup-receipt.json", "bench-config.json"] + } + } + ] +} diff --git a/rfcs/check-command.md b/rfcs/check-command.md index 69f83c3912..dff09b084b 100644 --- a/rfcs/check-command.md +++ b/rfcs/check-command.md @@ -225,8 +225,9 @@ and measurement conditions. The pinned tsrs release has no Windows ARM64 binary. This draft adds no automatic Go fallback. A release for all supported Vite+ platforms needs an upstream -Windows ARM64 binary or an explicit fallback. Runtime smoke tests currently -cover macOS ARM64; other platforms still need runtime validation. +Windows ARM64 binary or an explicit fallback. Runtime smoke tests cover macOS +ARM64. The Codespaces comparison also validates both backends on Linux x64 +with Vite+ and vinext. Other platforms still need runtime validation. The backend switch preserves the existing formatting phase, lint configuration, and failure handling. Smoke fixtures compare diagnostics, suppression comments, From 94c63a8f7d5cba776b4396c2302de1b55e463a86 Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 6 Nov 2026 00:09:53 +0800 Subject: [PATCH 06/25] bench(lint): report tsrs 0.22 time and memory results --- bench/tsrs-lint-codespaces/README.md | 16 ++ bench/tsrs-lint-codespaces/inputs/bench.py | 33 ++- .../results/main-metadata.json | 52 +++++ .../results/main-samples.csv | 205 ++++++++++++++++++ .../tsrs-lint-codespaces/results/summary.json | 98 +++++++++ .../results/timing-rerun-metadata.json | 36 +++ .../results/timing-rerun-samples.csv | 23 ++ bench/tsrs-lint.md | 107 +++++++-- 8 files changed, 544 insertions(+), 26 deletions(-) create mode 100644 bench/tsrs-lint-codespaces/results/main-metadata.json create mode 100644 bench/tsrs-lint-codespaces/results/main-samples.csv create mode 100644 bench/tsrs-lint-codespaces/results/summary.json create mode 100644 bench/tsrs-lint-codespaces/results/timing-rerun-metadata.json create mode 100644 bench/tsrs-lint-codespaces/results/timing-rerun-samples.csv diff --git a/bench/tsrs-lint-codespaces/README.md b/bench/tsrs-lint-codespaces/README.md index a43b01cc7c..6676e5c19d 100644 --- a/bench/tsrs-lint-codespaces/README.md +++ b/bench/tsrs-lint-codespaces/README.md @@ -8,6 +8,9 @@ Use the [codespaces-run skill](https://github.com/Dunqing/skills/tree/main/skill The recipe uses one `standardLinux32gb` instance with four CPUs and 16 GB of RAM. The skill retains the environment and stops the instance after artifact collection. Do not run other builds or benchmarks on the instance during measurement. +The CPU model can change when the same Codespace restarts. Inspect each job's +metadata before combining results. Keep both backends of a comparison in the +same job, and identify any hardware change in the report. ## Source and setup @@ -66,6 +69,19 @@ memory traces, and results are retained under the skill's local job directory. If a later stage fails, use the skill's stage recovery procedure to retain a verified setup. Do not start another job while the previous job holds the lease. +The runner also accepts `--project`, `--scenario`, and `--phase` filters. For a +focused repetition, submit a continuation after the prior job stops. Its script +can run the following commands with the recipe's input files: + +```sh +python3 "$JOB_INPUT_DIR/verify-setup.py" +"$JOB_ENV_DIR/venv/bin/python" "$JOB_INPUT_DIR/bench.py" \ + --project vinext --scenario full --phase time +``` + +This selection produces 22 timing records: ten pairs and two warm-ups. Keep the +new job's metadata and raw records when it replaces a previous timing set. + ## Method Validation runs each backend once for each workload before measurement. Every diff --git a/bench/tsrs-lint-codespaces/inputs/bench.py b/bench/tsrs-lint-codespaces/inputs/bench.py index bae07e9059..846ba77bc2 100644 --- a/bench/tsrs-lint-codespaces/inputs/bench.py +++ b/bench/tsrs-lint-codespaces/inputs/bench.py @@ -186,7 +186,8 @@ def metadata(): 'platform': platform.platform(), 'cpu_affinity': psutil.Process().cpu_affinity(), 'memory_bytes': psutil.virtual_memory().total, 'timing_pairs': TIMING_PAIRS, 'memory_pairs': MEMORY_PAIRS, 'sample_sleep_ms': 20, 'projects': CONFIG['projects'], - 'versions': json.loads((ENV / 'setup-receipt.json').read_text())['versions']} + 'versions': json.loads((ENV / 'setup-receipt.json').read_text())['versions'], + 'selection': vars(options)} for name, args in [('cpu', ['lscpu']), ('git_diff', ['git', 'diff', '--stat']), ('source', ['git', 'rev-parse', 'HEAD'])]: info[name] = subprocess.check_output(args, text=True) info['node_versions'] = {p['name']: subprocess.check_output([p['node'], '--version'], text=True).strip() for p in CONFIG['projects']} @@ -196,16 +197,20 @@ def metadata(): def summarize(): rows = [] - for project in CONFIG['projects']: - for scenario in SCENARIOS: + for project in selected_projects: + for scenario in selected_scenarios: row = {'project': project['name'], 'scenario': scenario} for backend in ('go', 'rust'): for phase, metric in [('time', 'seconds'), ('memory', 'peak_rss')]: + if phase not in phases: + continue values = [r[metric] for r in RECORDS if r['project'] == project['name'] and r['scenario'] == scenario and r['backend'] == backend and r['phase'] == phase and r['pair'] >= 0] row[f'{backend}_{metric}'] = statistics.median(values) row[f'{backend}_{metric}_range'] = [min(values), max(values)] - row['time_saved_percent'] = 100 * (1 - row['rust_seconds'] / row['go_seconds']) - row['rss_saved_percent'] = 100 * (1 - row['rust_peak_rss'] / row['go_peak_rss']) + if 'time' in phases: + row['time_saved_percent'] = 100 * (1 - row['rust_seconds'] / row['go_seconds']) + if 'memory' in phases: + row['rss_saved_percent'] = 100 * (1 - row['rust_peak_rss'] / row['go_peak_rss']) rows.append(row) (OUT / 'summary.json').write_text(json.dumps(rows, indent=2) + '\n') with (OUT / 'samples.csv').open('w') as file: @@ -218,19 +223,27 @@ def summarize(): parser = argparse.ArgumentParser() parser.add_argument('--validate-only', action='store_true') +parser.add_argument('--project', choices=[p['name'] for p in CONFIG['projects']]) +parser.add_argument('--scenario', choices=SCENARIOS) +parser.add_argument('--phase', choices=('time', 'memory')) options = parser.parse_args() +selected_projects = [p for p in CONFIG['projects'] if not options.project or p['name'] == options.project] +selected_scenarios = (options.scenario,) if options.scenario else SCENARIOS metadata() -phases = ('validate',) if options.validate_only else ('time', 'memory') -total = len(phases) * len(CONFIG['projects']) * len(SCENARIOS) +phases = ('validate',) if options.validate_only else (options.phase,) if options.phase else ('time', 'memory') +total = len(phases) * len(selected_projects) * len(selected_scenarios) completed = 0 for phase in phases: - for project in CONFIG['projects']: - for scenario in SCENARIOS: + for project in selected_projects: + for scenario in selected_scenarios: progress(phase, f"{project['name']}: {scenario}", completed, total) measure(project, scenario, phase) completed += 1 progress(phase, f"Completed {project['name']}: {scenario}", completed, total) if not options.validate_only: summarize() -assert len(RECORDS) == (12 if options.validate_only else 204), len(RECORDS) +expected_runs = len(selected_projects) * len(selected_scenarios) * sum( + 2 * (1 + (0 if phase == 'validate' else TIMING_PAIRS if phase == 'time' else MEMORY_PAIRS)) + for phase in phases) +assert len(RECORDS) == expected_runs, (len(RECORDS), expected_runs) progress('validated' if options.validate_only else 'measured', 'All output assertions passed', total, total) diff --git a/bench/tsrs-lint-codespaces/results/main-metadata.json b/bench/tsrs-lint-codespaces/results/main-metadata.json new file mode 100644 index 0000000000..84ee55482f --- /dev/null +++ b/bench/tsrs-lint-codespaces/results/main-metadata.json @@ -0,0 +1,52 @@ +{ + "vp_commit": "22b07bd150aa57b3e04cc52e0b81d43294ffe982", + "python": "3.10.12", + "psutil": "7.0.0", + "platform": "Linux-6.8.0-1064-azure-x86_64-with-glibc2.35", + "cpu_affinity": [0, 1, 2, 3], + "memory_bytes": 16770494464, + "timing_pairs": 10, + "memory_pairs": 5, + "sample_sleep_ms": 20, + "versions": { + "tsrs": "0.22.0-ts7.1.0-beta", + "oxlint": "1.91.0", + "oxlint-tsgolint": "7.0.2005" + }, + "node_versions": { + "vite-plus": "v24.22.2", + "vinext": "v26.13.0" + }, + "cpu_model": "AMD EPYC 9V74 80-Core Processor", + "rfcs_commit": "da260f86fce1d43810c62e4e6aad011066c88cb9", + "vinext_commit": "249088af307c0a852fa6dc59c08fc6ec80e1620e", + "job_id": "job-3dbd90be3d4f4aa3", + "validation_commands": 12, + "benchmark_records": 204, + "median_sampling_interval_ms": 21.781716749956104, + "sampling_interval_median_range_ms": [21.504334000383096, 24.079619999611168], + "full_check_signatures": { + "vite-plus": { + "files": 385, + "format_files": 632 + }, + "vinext": { + "files": 1828, + "format_files": 4673 + } + }, + "worker_result": { + "status": "succeeded", + "exit_code": 0, + "started_at": 1793866444.5705564, + "finished_at": 1793872607.508668 + }, + "excluded_timing_set": { + "project": "vinext", + "scenario": "full", + "phase": "time", + "reason": "Browser troubleshooting started editor extension activity during this timing set. A clean repetition replaces it." + }, + "harness_sha256": "5ae0087c81988829d74777717f4e06561aeb883c33017478f4ef33f2da85a42d", + "collected": true +} diff --git a/bench/tsrs-lint-codespaces/results/main-samples.csv b/bench/tsrs-lint-codespaces/results/main-samples.csv new file mode 100644 index 0000000000..394d9e3859 --- /dev/null +++ b/bench/tsrs-lint-codespaces/results/main-samples.csv @@ -0,0 +1,205 @@ +project,scenario,backend,phase,pair,seconds,peak_rss,exit +vite-plus,rules,go,time,-1,3.6880124389999764,,0 +vite-plus,rules,rust,time,-1,2.191973147999988,,0 +vite-plus,rules,go,time,0,3.713578874000177,,0 +vite-plus,rules,rust,time,0,2.2759946600001513,,0 +vite-plus,rules,rust,time,1,2.4920109750000847,,0 +vite-plus,rules,go,time,1,3.6422700799998893,,0 +vite-plus,rules,go,time,2,3.5738986520000253,,0 +vite-plus,rules,rust,time,2,2.242526223999903,,0 +vite-plus,rules,rust,time,3,2.223109660000091,,0 +vite-plus,rules,go,time,3,3.483162299000014,,0 +vite-plus,rules,go,time,4,3.7109736130000783,,0 +vite-plus,rules,rust,time,4,2.2263701169999877,,0 +vite-plus,rules,rust,time,5,2.2016010700001516,,0 +vite-plus,rules,go,time,5,3.9088208739999573,,0 +vite-plus,rules,go,time,6,3.736302202999923,,0 +vite-plus,rules,rust,time,6,2.187699441999939,,0 +vite-plus,rules,rust,time,7,2.1890677399999277,,0 +vite-plus,rules,go,time,7,3.5215749090000372,,0 +vite-plus,rules,go,time,8,3.7870844199999283,,0 +vite-plus,rules,rust,time,8,2.17189058200006,,0 +vite-plus,rules,rust,time,9,2.187169283000003,,0 +vite-plus,rules,go,time,9,3.839309356000058,,0 +vite-plus,full,go,time,-1,4.654679539999961,,0 +vite-plus,full,rust,time,-1,2.3562958489999346,,0 +vite-plus,full,go,time,0,4.566960510999934,,0 +vite-plus,full,rust,time,0,2.3931662680001864,,0 +vite-plus,full,rust,time,1,2.3698954330000106,,0 +vite-plus,full,go,time,1,4.634962337999923,,0 +vite-plus,full,go,time,2,4.632292471000028,,0 +vite-plus,full,rust,time,2,2.412981780000109,,0 +vite-plus,full,rust,time,3,2.430237735999981,,0 +vite-plus,full,go,time,3,4.53366976999996,,0 +vite-plus,full,go,time,4,4.307837832000132,,0 +vite-plus,full,rust,time,4,2.493581193999944,,0 +vite-plus,full,rust,time,5,2.3662074239998674,,0 +vite-plus,full,go,time,5,4.511783857999944,,0 +vite-plus,full,go,time,6,5.0457770020000225,,0 +vite-plus,full,rust,time,6,2.3568083630000274,,0 +vite-plus,full,rust,time,7,2.3296725099999094,,0 +vite-plus,full,go,time,7,4.698553215999937,,0 +vite-plus,full,go,time,8,4.591066730999955,,0 +vite-plus,full,rust,time,8,2.364594460999797,,0 +vite-plus,full,rust,time,9,2.456160010000076,,0 +vite-plus,full,go,time,9,4.855081535999943,,0 +vite-plus,check,go,time,-1,5.750060867999991,,0 +vite-plus,check,rust,time,-1,3.431430506999959,,0 +vite-plus,check,go,time,0,5.560635703999878,,0 +vite-plus,check,rust,time,0,3.5964835449999555,,0 +vite-plus,check,rust,time,1,3.4927115529999355,,0 +vite-plus,check,go,time,1,6.118377254000052,,0 +vite-plus,check,go,time,2,5.643318074000035,,0 +vite-plus,check,rust,time,2,3.5944409420001193,,0 +vite-plus,check,rust,time,3,3.4559823089998645,,0 +vite-plus,check,go,time,3,5.5692066710000745,,0 +vite-plus,check,go,time,4,5.942744820999906,,0 +vite-plus,check,rust,time,4,3.629871501000025,,0 +vite-plus,check,rust,time,5,3.5768438600000536,,0 +vite-plus,check,go,time,5,5.60959895100018,,0 +vite-plus,check,go,time,6,5.8363495700000385,,0 +vite-plus,check,rust,time,6,3.4873933159999524,,0 +vite-plus,check,rust,time,7,3.661024717999908,,0 +vite-plus,check,go,time,7,5.935824476999869,,0 +vite-plus,check,go,time,8,5.847994853000046,,0 +vite-plus,check,rust,time,8,3.4268209399999705,,0 +vite-plus,check,rust,time,9,3.530400184999962,,0 +vite-plus,check,go,time,9,6.113309405000109,,0 +vinext,rules,go,time,-1,52.948004241000035,,0 +vinext,rules,rust,time,-1,46.124062173000084,,0 +vinext,rules,go,time,0,52.21461774299996,,0 +vinext,rules,rust,time,0,46.608522696000136,,0 +vinext,rules,rust,time,1,46.39468327899999,,0 +vinext,rules,go,time,1,51.841172385999926,,0 +vinext,rules,go,time,2,52.348372229000006,,0 +vinext,rules,rust,time,2,43.62988959800009,,0 +vinext,rules,rust,time,3,44.699639799999886,,0 +vinext,rules,go,time,3,49.70923635600002,,0 +vinext,rules,go,time,4,53.68720330700012,,0 +vinext,rules,rust,time,4,43.043341374999954,,0 +vinext,rules,rust,time,5,46.44647001299995,,0 +vinext,rules,go,time,5,56.2561799030002,,0 +vinext,rules,go,time,6,55.63514441699999,,0 +vinext,rules,rust,time,6,42.57279852900001,,0 +vinext,rules,rust,time,7,45.58332635399984,,0 +vinext,rules,go,time,7,49.66620632200011,,0 +vinext,rules,go,time,8,51.518826092999916,,0 +vinext,rules,rust,time,8,45.142249451000225,,0 +vinext,rules,rust,time,9,46.268095335,,0 +vinext,rules,go,time,9,51.84008323399985,,0 +vinext,full,go,time,-1,54.97031217999984,,0 +vinext,full,rust,time,-1,49.50701832100003,,0 +vinext,full,go,time,0,54.54909113500025,,0 +vinext,full,rust,time,0,48.777478402999805,,0 +vinext,full,rust,time,1,50.565860768999755,,0 +vinext,full,go,time,1,54.497046376999606,,0 +vinext,full,go,time,2,55.068685018999986,,0 +vinext,full,rust,time,2,49.378554876999715,,0 +vinext,full,rust,time,3,46.8017662240004,,0 +vinext,full,go,time,3,54.68703606200006,,0 +vinext,full,go,time,4,56.505208954999944,,0 +vinext,full,rust,time,4,49.47823296100023,,0 +vinext,full,rust,time,5,46.74362833999976,,0 +vinext,full,go,time,5,55.63798557900009,,0 +vinext,full,go,time,6,54.669546999999966,,0 +vinext,full,rust,time,6,49.366139873000066,,0 +vinext,full,rust,time,7,48.29169880900008,,0 +vinext,full,go,time,7,60.90340383700004,,0 +vinext,full,go,time,8,58.05361229900018,,0 +vinext,full,rust,time,8,49.801143192999916,,0 +vinext,full,rust,time,9,48.88615762499967,,0 +vinext,full,go,time,9,56.70309664500019,,0 +vinext,check,go,time,-1,61.557492223999816,,0 +vinext,check,rust,time,-1,50.663133098000344,,0 +vinext,check,go,time,0,62.98640043899968,,0 +vinext,check,rust,time,0,48.491100012999595,,0 +vinext,check,rust,time,1,49.07317851800008,,0 +vinext,check,go,time,1,62.14544949099991,,0 +vinext,check,go,time,2,60.34417550099988,,0 +vinext,check,rust,time,2,50.885471571999915,,0 +vinext,check,rust,time,3,50.162232835000395,,0 +vinext,check,go,time,3,62.48697384699972,,0 +vinext,check,go,time,4,63.368093478000446,,0 +vinext,check,rust,time,4,50.91846492900004,,0 +vinext,check,rust,time,5,53.96345718400062,,0 +vinext,check,go,time,5,68.245969263,,0 +vinext,check,go,time,6,64.2242278700005,,0 +vinext,check,rust,time,6,52.53176643899951,,0 +vinext,check,rust,time,7,50.54870111699984,,0 +vinext,check,go,time,7,61.418192543999794,,0 +vinext,check,go,time,8,62.163307601999804,,0 +vinext,check,rust,time,8,47.784832813999856,,0 +vinext,check,rust,time,9,48.62056124399987,,0 +vinext,check,go,time,9,61.23984774499968,,0 +vite-plus,rules,go,memory,-1,3.940764267999839,1458286592,0 +vite-plus,rules,rust,memory,-1,2.329289043999779,1018667008,0 +vite-plus,rules,go,memory,0,3.857643793000534,1215549440,0 +vite-plus,rules,rust,memory,0,2.352922975000183,976420864,0 +vite-plus,rules,rust,memory,1,2.5503717050005434,1004322816,0 +vite-plus,rules,go,memory,1,3.7755272800004605,1337851904,0 +vite-plus,rules,go,memory,2,3.9427604910006266,1220345856,0 +vite-plus,rules,rust,memory,2,2.2911063749997993,1000955904,0 +vite-plus,rules,rust,memory,3,2.3516781999996965,998109184,0 +vite-plus,rules,go,memory,3,3.8952087559991924,1343397888,0 +vite-plus,rules,go,memory,4,3.78354734200002,1261670400,0 +vite-plus,rules,rust,memory,4,2.3114647920001516,964509696,0 +vite-plus,full,go,memory,-1,5.309363115000451,1652715520,0 +vite-plus,full,rust,memory,-1,2.525028889999703,1011937280,0 +vite-plus,full,go,memory,0,4.856635449999885,1443962880,0 +vite-plus,full,rust,memory,0,2.658091739000156,1061445632,0 +vite-plus,full,rust,memory,1,2.5839939859997685,1056391168,0 +vite-plus,full,go,memory,1,4.8830151010006375,1477206016,0 +vite-plus,full,go,memory,2,5.28511940000044,1383268352,0 +vite-plus,full,rust,memory,2,2.5511508569998114,1054629888,0 +vite-plus,full,rust,memory,3,2.5333675159999984,1069342720,0 +vite-plus,full,go,memory,3,5.299707000000126,1786830848,0 +vite-plus,full,go,memory,4,4.7314949120000165,1393152000,0 +vite-plus,full,rust,memory,4,2.576505080000061,1045348352,0 +vite-plus,check,go,memory,-1,5.992466878999949,1424461824,0 +vite-plus,check,rust,memory,-1,3.699139065000054,1118486528,0 +vite-plus,check,go,memory,0,5.935010570000486,1546260480,0 +vite-plus,check,rust,memory,0,3.6238119339996047,1078734848,0 +vite-plus,check,rust,memory,1,3.7297287820001657,1094008832,0 +vite-plus,check,go,memory,1,5.904669201999241,1786224640,0 +vite-plus,check,go,memory,2,6.3074621320001825,1534910464,0 +vite-plus,check,rust,memory,2,3.609159493000334,1111625728,0 +vite-plus,check,rust,memory,3,3.7218976559997827,1092087808,0 +vite-plus,check,go,memory,3,5.842184367999835,1543946240,0 +vite-plus,check,go,memory,4,6.1350453840004775,1427783680,0 +vite-plus,check,rust,memory,4,3.898626783000509,1090322432,0 +vinext,rules,go,memory,-1,54.02692431700052,4816293888,0 +vinext,rules,rust,memory,-1,46.12704977300018,3161968640,0 +vinext,rules,go,memory,0,53.78586441100015,4626669568,0 +vinext,rules,rust,memory,0,48.66075261300011,3163717632,0 +vinext,rules,rust,memory,1,48.704375655999684,3253788672,0 +vinext,rules,go,memory,1,53.11478282000007,4844425216,0 +vinext,rules,go,memory,2,50.72010573700027,5035069440,0 +vinext,rules,rust,memory,2,46.750776367999606,3171049472,0 +vinext,rules,rust,memory,3,43.352124744999855,3257614336,0 +vinext,rules,go,memory,3,55.42714346699995,4705017856,0 +vinext,rules,go,memory,4,50.09278948500014,4499263488,0 +vinext,rules,rust,memory,4,44.060889810000845,3327598592,0 +vinext,full,go,memory,-1,56.74663075999979,5482045440,0 +vinext,full,rust,memory,-1,49.11478131599961,3299753984,0 +vinext,full,go,memory,0,57.2608009810001,5504036864,0 +vinext,full,rust,memory,0,50.5766341210001,3399245824,0 +vinext,full,rust,memory,1,46.43882131400005,3354329088,0 +vinext,full,go,memory,1,56.82105735300047,5004701696,0 +vinext,full,go,memory,2,57.29950182000084,5265002496,0 +vinext,full,rust,memory,2,50.1835814720007,3529072640,0 +vinext,full,rust,memory,3,49.69551689400032,3426652160,0 +vinext,full,go,memory,3,57.23072275499999,4893655040,0 +vinext,full,go,memory,4,57.49116492400026,5388804096,0 +vinext,full,rust,memory,4,47.16479458400045,3357630464,0 +vinext,check,go,memory,-1,63.65577673000007,5786767360,0 +vinext,check,rust,memory,-1,50.898781433000295,3493175296,0 +vinext,check,go,memory,0,60.84386064899991,5591572480,0 +vinext,check,rust,memory,0,50.34470988399971,3572527104,0 +vinext,check,rust,memory,1,49.92382415800057,3534950400,0 +vinext,check,go,memory,1,61.628392914999495,6064586752,0 +vinext,check,go,memory,2,58.81841730999986,5774688256,0 +vinext,check,rust,memory,2,51.66727800700028,3535589376,0 +vinext,check,rust,memory,3,48.62187229600022,3533045760,0 +vinext,check,go,memory,3,63.150578589999895,6065008640,0 +vinext,check,go,memory,4,62.494274702999974,5812363264,0 +vinext,check,rust,memory,4,50.306470861999514,3523481600,0 diff --git a/bench/tsrs-lint-codespaces/results/summary.json b/bench/tsrs-lint-codespaces/results/summary.json new file mode 100644 index 0000000000..bdb9ccf395 --- /dev/null +++ b/bench/tsrs-lint-codespaces/results/summary.json @@ -0,0 +1,98 @@ +[ + { + "project": "vite-plus", + "scenario": "rules", + "go_seconds": 3.7122762435001277, + "go_seconds_range": [3.483162299000014, 3.9088208739999573], + "go_peak_rss": 1261670400, + "go_peak_rss_range": [1215549440, 1343397888], + "rust_seconds": 2.2123553650001213, + "rust_seconds_range": [2.17189058200006, 2.4920109750000847], + "rust_peak_rss": 998109184, + "rust_peak_rss_range": [964509696, 1004322816], + "time_saved_percent": 40.40434439991467, + "rss_saved_percent": 20.88986283580878, + "timing_job": "job-3dbd90be3d4f4aa3", + "memory_job": "job-3dbd90be3d4f4aa3" + }, + { + "project": "vite-plus", + "scenario": "full", + "go_seconds": 4.611679600999992, + "go_seconds_range": [4.307837832000132, 5.0457770020000225], + "go_peak_rss": 1443962880, + "go_peak_rss_range": [1383268352, 1786830848], + "rust_seconds": 2.3815308505000985, + "rust_seconds_range": [2.3296725099999094, 2.493581193999944], + "rust_peak_rss": 1056391168, + "rust_peak_rss_range": [1045348352, 1069342720], + "time_saved_percent": 48.35870969909336, + "rss_saved_percent": 26.8408362408873, + "timing_job": "job-3dbd90be3d4f4aa3", + "memory_job": "job-3dbd90be3d4f4aa3" + }, + { + "project": "vite-plus", + "scenario": "check", + "go_seconds": 5.842172211500042, + "go_seconds_range": [5.560635703999878, 6.118377254000052], + "go_peak_rss": 1543946240, + "go_peak_rss_range": [1427783680, 1786224640], + "rust_seconds": 3.553622022500008, + "rust_seconds_range": [3.4268209399999705, 3.661024717999908], + "rust_peak_rss": 1092087808, + "rust_peak_rss_range": [1078734848, 1111625728], + "time_saved_percent": 39.172932706350736, + "rss_saved_percent": 29.26646150580995, + "timing_job": "job-3dbd90be3d4f4aa3", + "memory_job": "job-3dbd90be3d4f4aa3" + }, + { + "project": "vinext", + "scenario": "rules", + "go_seconds": 52.027895064499944, + "go_seconds_range": [49.66620632200011, 56.2561799030002], + "go_peak_rss": 4705017856, + "go_peak_rss_range": [4499263488, 5035069440], + "rust_seconds": 45.36278790250003, + "rust_seconds_range": [42.57279852900001, 46.608522696000136], + "rust_peak_rss": 3253788672, + "rust_peak_rss_range": [3163717632, 3327598592], + "time_saved_percent": 12.810641587050675, + "rss_saved_percent": 30.84428642814485, + "timing_job": "job-3dbd90be3d4f4aa3", + "memory_job": "job-3dbd90be3d4f4aa3" + }, + { + "project": "vinext", + "scenario": "full", + "go_seconds": 54.19228503350001, + "go_seconds_range": [51.108869654000046, 55.4913207269999], + "go_peak_rss": 5265002496, + "go_peak_rss_range": [4893655040, 5504036864], + "rust_seconds": 44.79370169149996, + "rust_seconds_range": [42.42826828800003, 46.32845908899998], + "rust_peak_rss": 3399245824, + "rust_peak_rss_range": [3354329088, 3529072640], + "time_saved_percent": 17.343028322555753, + "rss_saved_percent": 35.436957027417904, + "timing_job": "job-4685d3c6e32f48bf", + "memory_job": "job-3dbd90be3d4f4aa3" + }, + { + "project": "vinext", + "scenario": "check", + "go_seconds": 62.32514072449976, + "go_seconds_range": [60.34417550099988, 68.245969263], + "go_peak_rss": 5812363264, + "go_peak_rss_range": [5591572480, 6065008640], + "rust_seconds": 50.355466976000116, + "rust_seconds_range": [47.784832813999856, 53.96345718400062], + "rust_peak_rss": 3534950400, + "rust_peak_rss_range": [3523481600, 3572527104], + "time_saved_percent": 19.205209341459884, + "rss_saved_percent": 39.18221832598796, + "timing_job": "job-3dbd90be3d4f4aa3", + "memory_job": "job-3dbd90be3d4f4aa3" + } +] diff --git a/bench/tsrs-lint-codespaces/results/timing-rerun-metadata.json b/bench/tsrs-lint-codespaces/results/timing-rerun-metadata.json new file mode 100644 index 0000000000..083a981844 --- /dev/null +++ b/bench/tsrs-lint-codespaces/results/timing-rerun-metadata.json @@ -0,0 +1,36 @@ +{ + "vp_commit": "22b07bd150aa57b3e04cc52e0b81d43294ffe982", + "python": "3.10.12", + "psutil": "7.0.0", + "platform": "Linux-6.8.0-1064-azure-x86_64-with-glibc2.35", + "cpu_affinity": [0, 1, 2, 3], + "memory_bytes": 16770494464, + "timing_pairs": 10, + "versions": { + "tsrs": "0.22.0-ts7.1.0-beta", + "oxlint": "1.91.0", + "oxlint-tsgolint": "7.0.2005" + }, + "node_versions": { + "vite-plus": "v24.22.2", + "vinext": "v26.13.0" + }, + "selection": { + "validate_only": false, + "project": "vinext", + "scenario": "full", + "phase": "time" + }, + "cpu_model": "AMD EPYC 7763 64-Core Processor", + "job_id": "job-4685d3c6e32f48bf", + "records": 22, + "worker_result": { + "status": "succeeded", + "exit_code": 0, + "started_at": 1793893477.6474826, + "finished_at": 1793894561.3346298 + }, + "harness_sha256": "734df51a34c3f202dad7e007648a1ca718daa3e9c2a26d5d0c9ba32a952992f8", + "collected": true, + "shutdown_confirmed_at": 1793894671.662227 +} diff --git a/bench/tsrs-lint-codespaces/results/timing-rerun-samples.csv b/bench/tsrs-lint-codespaces/results/timing-rerun-samples.csv new file mode 100644 index 0000000000..c82e7ce8c5 --- /dev/null +++ b/bench/tsrs-lint-codespaces/results/timing-rerun-samples.csv @@ -0,0 +1,23 @@ +project,scenario,backend,phase,pair,seconds,peak_rss,exit +vinext,full,go,time,-1,53.405041512000025,,0 +vinext,full,rust,time,-1,45.34521164800003,,0 +vinext,full,go,time,0,54.13607682600002,,0 +vinext,full,rust,time,0,45.48383163799997,,0 +vinext,full,rust,time,1,46.32845908899998,,0 +vinext,full,go,time,1,54.58117138299997,,0 +vinext,full,go,time,2,51.108869654000046,,0 +vinext,full,rust,time,2,42.42826828800003,,0 +vinext,full,rust,time,3,45.07651155499991,,0 +vinext,full,go,time,3,52.29078975899995,,0 +vinext,full,go,time,4,54.71693397199999,,0 +vinext,full,rust,time,4,44.51089182800001,,0 +vinext,full,rust,time,5,45.55147170500004,,0 +vinext,full,go,time,5,54.248493241000006,,0 +vinext,full,go,time,6,54.310657021,,0 +vinext,full,rust,time,6,42.593737007999835,,0 +vinext,full,rust,time,7,44.00615199700019,,0 +vinext,full,go,time,7,55.4913207269999,,0 +vinext,full,go,time,8,54.02196162600012,,0 +vinext,full,rust,time,8,43.040702052999904,,0 +vinext,full,rust,time,9,45.36723157000006,,0 +vinext,full,go,time,9,53.41371220799988,,0 diff --git a/bench/tsrs-lint.md b/bench/tsrs-lint.md index 6543ba2418..b91343953d 100644 --- a/bench/tsrs-lint.md +++ b/bench/tsrs-lint.md @@ -1,26 +1,101 @@ # Type-aware linting with tsrs-lint -On the Vite+ workspace at `a32a8cec2` with the benchmark script added, the experimental backend saved 0.51 seconds per lint run with full type checking. Both backends analyzed 385 files with 174 rules, reported zero diagnostics, and exited successfully on every run. +This comparison uses the default `tsrs@0.22.0-ts7.1.0-beta` integration and an explicit +`oxlint-tsgolint@7.0.2005` override. Both use Oxlint `1.91.0` and the same built CLI. -| `vp lint` workload | Bundled tsgolint | tsrs-lint | Wall time saved | -| ------------------------------------------- | ---------------: | --------: | --------------: | -| Type-aware rules | 1.089 s | 0.822 s | 24.5% | -| Type-aware rules and TypeScript diagnostics | 1.340 s | 0.830 s | 38.1% | +## Elapsed time -For the full `vp check` command, the median fell from 1.852 s to 1.335 s (27.9%). This command also checked formatting on 632 files and used its default 16 threads. +Each value is the median of ten fresh CLI processes after one warm-up per backend. +The runner alternates the order between pairs. Timing runs do not sample memory. -Measured on an Apple M4 Max with macOS 27.0.1, Node.js 24.22.2, Vite+ 1.5.0, Oxlint 1.91.0, oxlint-tsgolint 7.0.2005, and tsrs 0.21.0-ts7.1.0-beta. The benchmark used the published Vite+ 1.5.0 CLI artifacts and the workspace's locked dependencies. Each sample started a fresh local CLI process and included config loading, JS plugins, the backend launcher, and diagnostic reporting. The `vp lint` runs used four Oxlint threads. Both backends used their default settings. +| Project | Workload | Go (s) | tsrs (s) | Reduction | +| -------- | -------------------------------- | -----: | -------: | --------: | +| Vite+ | Type-aware rules | 3.712 | 2.212 | 40.4% | +| Vite+ | Rules and TypeScript diagnostics | 4.612 | 2.382 | 48.4% | +| Vite+ | Full `vp check` | 5.842 | 3.554 | 39.2% | +| vinext | Type-aware rules | 52.028 | 45.363 | 12.8% | +| vinext † | Rules and TypeScript diagnostics | 54.192 | 44.794 | 17.3% | +| vinext | Full `vp check` | 62.325 | 50.355 | 19.2% | -The table shows the median of ten runs per backend after one warm-up each. The runner reverses the backend order between pairs and compares the exit code, file and rule counts, and sorted JSON diagnostics for every run. In a separate failing fixture, both backends reported `typescript/no-floating-promises` and `TS2322` at the same locations through `vp lint` and `vp check`. +† The clean timing repetition used an EPYC 7763 after a Codespace restart. +All other timing rows used an EPYC 9V74. Both backends within each row used +the same CPU. Compare backend values within a row; the CPU change prevents +a strict comparison of absolute times across workloads. -## Reproduce +## Peak memory -Start in a built Vite+ checkout. The runner explicitly selects the `oxlint-tsgolint` development dependency for the Go baseline, even when the CLI defaults to tsrs. Install the candidate backend under the ignored `tmp` directory to compare a specific version. +Each value is the median of five separate runs after one warm-up per backend. +Memory is the sampled peak of the simultaneous sum of RSS for the CLI and its +descendants, including Node, Oxlint, and the native backend. One GiB is 1,073,741,824 bytes. -```sh -npm install --prefix ./tmp/tsrs-lint --no-save --package-lock=false tsrs@0.21.0-ts7.1.0-beta -export OXLINT_TSGOLINT_PATH="$PWD/tmp/tsrs-lint/node_modules/.bin/tsrs-lint" -node bench/tsrs-lint.ts --type-check -``` +| Project | Workload | Go (GiB) | tsrs (GiB) | Reduction | +| ------- | -------------------------------- | -------: | ---------: | --------: | +| Vite+ | Type-aware rules | 1.175 | 0.930 | 20.9% | +| Vite+ | Rules and TypeScript diagnostics | 1.345 | 0.984 | 26.8% | +| Vite+ | Full `vp check` | 1.438 | 1.017 | 29.3% | +| vinext | Type-aware rules | 4.382 | 3.030 | 30.8% | +| vinext | Rules and TypeScript diagnostics | 4.903 | 3.166 | 35.4% | +| vinext | Full `vp check` | 5.413 | 3.292 | 39.2% | -For the rules-only row, temporarily set `lint.options.typeCheck` to `false` in the root `vite.config.ts`, run `node bench/tsrs-lint.ts` without `--type-check`, then restore the setting. The benchmark always enables `--type-aware` and prints the individual samples in seconds. +The sampler sleeps for 20 ms. The median observed interval across runs was +21.8 ms. Shared pages can count more than once, +and sampling can miss short peaks. These values are process-tree RSS, not total +allocation or container memory. Per-case ranges are in the [summary](./tsrs-lint-codespaces/results/summary.json). + +## Conditions and interpretation + +The jobs used one GitHub Codespace with four vCPUs, 16 GB advertised RAM, and an +AMD EPYC 9V74 processor for the main run. The focused timing repetition used an +AMD EPYC 7763 after the instance restarted. Usable RAM was 15.62 GiB. +The container used Ubuntu 22.04 on Linux x64 with glibc. Vite+ used Node.js +`24.22.2`; vinext used Node.js `26.13.0`. + +The CLI JavaScript came from `22b07bd150aa57b3e04cc52e0b81d43294ffe982`. +Its native binding, core, and prompts artifacts came from the published Vite+ +`1.5.0` packages. The measured Vite+ source also includes the RFC patch at +`da260f86fce1d43810c62e4e6aad011066c88cb9`. Vinext is pinned to +`249088af307c0a852fa6dc59c08fc6ec80e1620e`, from the requested upgrade branch. +This is a comparison of backend routing in the built CLI, not a full native release build. + +Lint uses four threads. Vinext lint sets +`OXLINT_TSGOLINT_CHECKER_SCHEDULING=sorted`; Vite+ lint uses default scheduling. +Full checks use four threads and default backend scheduling. The rules-only +configuration disables TypeScript diagnostics while retaining the project's +original rules and plugins. Setup, builds, validation, and memory sampling are +excluded from the elapsed-time table. + +Both backends passed all 12 validation commands and every benchmark invocation. +Vite+ checked 385 source files with 174 rules and formatted 632 files. Vinext +checked 1,828 source files with 151 rules and formatted 4,673 files. These clean +corpora do not establish complete diagnostic or compiler parity. + +Vinext retains its original JS plugin rules, including +`vinext-utils/prefer-shared-utils`. The backend switch leaves that shared work +in place. This limits the elapsed-time improvement even though the memory +reduction is larger. The measurements do not include that rule's separate optimization. + +## Evidence and reproduction + +Use the [Codespaces recipe](./tsrs-lint-codespaces/README.md) to recreate the pinned +setup and all six comparisons. The recipe uses frozen lockfiles and retains +verified dependencies between jobs. + +The [main samples](./tsrs-lint-codespaces/results/main-samples.csv) contain all 204 +invocations, including warm-ups. A browser connection used to diagnose an SSH +outage started editor extension activity during vinext's type-checking timing +set. That entire timing set is excluded. The table instead uses the 22 records +from the [clean timing repetition](./tsrs-lint-codespaces/results/timing-rerun-samples.csv). +The repetition uses the same retained build and dependencies, with their hashes +checked before execution. All memory values come from the main job. + +Records with `pair=-1` are warm-ups and do not contribute to medians. For the +vinext `full`/`time` group, select the repetition CSV; select the main CSV for +all other groups. Take each backend's median `seconds` or `peak_rss` value and +calculate the reduction as `100 * (1 - rust / go)`. + +The [main metadata](./tsrs-lint-codespaces/results/main-metadata.json) and +[repetition metadata](./tsrs-lint-codespaces/results/timing-rerun-metadata.json) +record versions, hardware, source revisions, and successful worker results. +The main worker finished while the controller was disconnected; its complete +artifacts were recovered from the stopped Codespace. The jobs are `job-3dbd90be3d4f4aa3` and +`job-4685d3c6e32f48bf`. From 9df490e9cd5874697a5e396634c803390ed268d3 Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 6 Nov 2026 00:29:35 +0800 Subject: [PATCH 07/25] test(cli): update snapshots for the tsrs backend --- .../snapshots/cli_helper_message.md | 14 +++++++------- .../snapshots/toolchain_filters.md | 2 +- .../snapshots/toolchain_full_global.md | 2 +- .../snapshots/toolchain_full_local.md | 2 +- .../snapshots/toolchain_old_local_routing.md | 4 ++-- .../snapshots/command_version_local_tools.md | 14 +++++++------- .../snapshots/command_version_no_side_effects.md | 14 +++++++------- .../snapshots/command_version_with_env.md | 14 +++++++------- 8 files changed, 33 insertions(+), 33 deletions(-) diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message.md index 9fc35cf1f6..db73ff1f06 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message.md @@ -77,13 +77,13 @@ Local vite-plus: vite-plus Tools: - vite - rolldown - vitest - oxfmt - oxlint - oxlint-tsgolint - tsdown + vite + rolldown + vitest + oxfmt + oxlint + tsrs + tsdown Environment: Package manager Not found diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_filters.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_filters.md index c189dfbc67..f674bbb61f 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_filters.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_filters.md @@ -40,6 +40,6 @@ Vite+ toolchain (local) vite-plus@ ├── depends on @voidzero-dev/vite-plus-core@ -├── depends on oxlint-tsgolint@ +├── depends on tsrs@ └── compiles vite-task (built , revision ) ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_global.md index 209b74891e..860c74f1da 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_global.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_global.md @@ -19,7 +19,7 @@ vite-plus@ │ └── bundles tsdown@ ├── depends on vitest@ ├── depends on oxlint@ -├── depends on oxlint-tsgolint@ +├── depends on tsrs@ ├── depends on oxfmt@ ├── compiles vite-task (built , revision ) └── compiles oxc-node@ () diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_local.md index a983347b5a..e125bb8365 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_local.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_local.md @@ -19,7 +19,7 @@ vite-plus@ │ └── bundles tsdown@ ├── depends on vitest@ ├── depends on oxlint@ -├── depends on oxlint-tsgolint@ +├── depends on tsrs@ ├── depends on oxfmt@ ├── compiles vite-task (built , revision ) └── compiles oxc-node@ () diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_old_local_routing.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_old_local_routing.md index b98a86b6cb..727e750ae0 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_old_local_routing.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_old_local_routing.md @@ -48,7 +48,7 @@ vite-plus@ │ └── bundles tsdown@ ├── depends on vitest@ ├── depends on oxlint@ -├── depends on oxlint-tsgolint@ +├── depends on tsrs@ ├── depends on oxfmt@ ├── compiles vite-task (built , revision ) └── compiles oxc-node@ () @@ -85,7 +85,7 @@ vite-plus@ │ └── bundles tsdown@ ├── depends on vitest@ ├── depends on oxlint@ -├── depends on oxlint-tsgolint@ +├── depends on tsrs@ ├── depends on oxfmt@ ├── compiles vite-task (built , revision ) └── compiles oxc-node@ () diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_version_local_tools/snapshots/command_version_local_tools.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_version_local_tools/snapshots/command_version_local_tools.md index 3cd9eaedb6..bbb0a67908 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_version_local_tools/snapshots/command_version_local_tools.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_version_local_tools/snapshots/command_version_local_tools.md @@ -13,11 +13,11 @@ Local vite-plus: vite-plus Tools: - vite - rolldown - vitest - oxfmt - oxlint - oxlint-tsgolint - tsdown + vite + rolldown + vitest + oxfmt + oxlint + tsrs + tsdown ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_version_no_side_effects/snapshots/command_version_no_side_effects.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_version_no_side_effects/snapshots/command_version_no_side_effects.md index 8b90bb1474..74521cd9d4 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_version_no_side_effects/snapshots/command_version_no_side_effects.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_version_no_side_effects/snapshots/command_version_no_side_effects.md @@ -13,13 +13,13 @@ Local vite-plus: vite-plus Tools: - vite - rolldown - vitest - oxfmt - oxlint - oxlint-tsgolint - tsdown + vite + rolldown + vitest + oxfmt + oxlint + tsrs + tsdown Environment: Package manager Not found diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_version_with_env/snapshots/command_version_with_env.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_version_with_env/snapshots/command_version_with_env.md index e31aa008c4..6794e89a27 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_version_with_env/snapshots/command_version_with_env.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_version_with_env/snapshots/command_version_with_env.md @@ -11,13 +11,13 @@ Local vite-plus: vite-plus Tools: - vite - rolldown - vitest - oxfmt - oxlint - oxlint-tsgolint - tsdown + vite + rolldown + vitest + oxfmt + oxlint + tsrs + tsdown Environment: Package manager pnpm From 0e16dfbd15dc48ee104be2e670030f99292d20ba Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 6 Nov 2026 09:51:13 +0800 Subject: [PATCH 08/25] docs(lint): remove backend switching instructions --- docs/guide/lint.md | 11 ----------- rfcs/check-command.md | 7 +------ 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/docs/guide/lint.md b/docs/guide/lint.md index 69538b86d3..6e37103417 100644 --- a/docs/guide/lint.md +++ b/docs/guide/lint.md @@ -51,17 +51,6 @@ We recommend enabling both `typeAware` and `typeCheck` in the `lint` block: This experimental build uses [tsrs](https://github.com/oxc-project/tsrs), a Rust port of the TypeScript 7 compiler. Its `tsrs-lint` executable gives Oxlint access to type information and allows type checking directly via `vp lint` and `vp check`. -### Select another backend - -The bundled `tsrs-lint` runs by default. To compare it with [tsgolint](https://github.com/oxc-project/tsgolint), install the Go backend and set `OXLINT_TSGOLINT_PATH`. Run these commands from the project root. - -```sh -vp add -D oxlint-tsgolint -OXLINT_TSGOLINT_PATH="$PWD/node_modules/.bin/tsgolint" vp lint --type-aware --type-check -``` - -The same environment variable works with `vp check` when type-aware linting is enabled. Omit it to use the bundled tsrs again. You can also point it at another `tsrs-lint` installation. - The pinned `tsrs` release does not provide a Windows ARM64 binary. This experimental replacement does not support that platform yet. ## JS Plugins diff --git a/rfcs/check-command.md b/rfcs/check-command.md index dff09b084b..90bb4f4c44 100644 --- a/rfcs/check-command.md +++ b/rfcs/check-command.md @@ -210,12 +210,7 @@ Users do not need to set an environment variable for the default backend. An explicit `OXLINT_TSGOLINT_PATH` takes precedence over the resolver default. The environment variable keeps its existing name because Oxlint uses it to -select the type-aware backend. A project can still select Go explicitly: - -```sh -vp add -D oxlint-tsgolint -OXLINT_TSGOLINT_PATH="$PWD/node_modules/.bin/tsgolint" vp check -``` +select the type-aware backend. `tsrs` is a production dependency. `oxlint-tsgolint` remains a development dependency for comparisons. The benchmark selects the Go executable explicitly, From 7458845c3a98cc5531eb90201ae9cbb60f0b5627 Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 6 Nov 2026 09:58:50 +0800 Subject: [PATCH 09/25] chore(bench): keep Codespaces artifacts out of the PR --- bench/tsrs-lint-codespaces/README.md | 111 -------- bench/tsrs-lint-codespaces/inputs/bench.py | 249 ------------------ bench/tsrs-lint-codespaces/inputs/docs.py | 11 - bench/tsrs-lint-codespaces/inputs/docs.sh | 1 - bench/tsrs-lint-codespaces/inputs/run.sh | 1 - bench/tsrs-lint-codespaces/inputs/setup.py | 150 ----------- bench/tsrs-lint-codespaces/inputs/setup.sh | 1 - bench/tsrs-lint-codespaces/inputs/validate.sh | 1 - .../inputs/verify-setup.py | 6 - .../inputs/verify-setup.sh | 1 - .../results/main-metadata.json | 52 ---- .../results/main-samples.csv | 205 -------------- .../tsrs-lint-codespaces/results/summary.json | 98 ------- .../results/timing-rerun-metadata.json | 36 --- .../results/timing-rerun-samples.csv | 23 -- bench/tsrs-lint-codespaces/stages.json | 46 ---- bench/tsrs-lint.md | 27 +- 17 files changed, 7 insertions(+), 1012 deletions(-) delete mode 100644 bench/tsrs-lint-codespaces/README.md delete mode 100644 bench/tsrs-lint-codespaces/inputs/bench.py delete mode 100644 bench/tsrs-lint-codespaces/inputs/docs.py delete mode 100644 bench/tsrs-lint-codespaces/inputs/docs.sh delete mode 100644 bench/tsrs-lint-codespaces/inputs/run.sh delete mode 100644 bench/tsrs-lint-codespaces/inputs/setup.py delete mode 100644 bench/tsrs-lint-codespaces/inputs/setup.sh delete mode 100644 bench/tsrs-lint-codespaces/inputs/validate.sh delete mode 100644 bench/tsrs-lint-codespaces/inputs/verify-setup.py delete mode 100644 bench/tsrs-lint-codespaces/inputs/verify-setup.sh delete mode 100644 bench/tsrs-lint-codespaces/results/main-metadata.json delete mode 100644 bench/tsrs-lint-codespaces/results/main-samples.csv delete mode 100644 bench/tsrs-lint-codespaces/results/summary.json delete mode 100644 bench/tsrs-lint-codespaces/results/timing-rerun-metadata.json delete mode 100644 bench/tsrs-lint-codespaces/results/timing-rerun-samples.csv delete mode 100644 bench/tsrs-lint-codespaces/stages.json diff --git a/bench/tsrs-lint-codespaces/README.md b/bench/tsrs-lint-codespaces/README.md deleted file mode 100644 index 6676e5c19d..0000000000 --- a/bench/tsrs-lint-codespaces/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# Codespaces comparison of lint backends - -This recipe compares the explicit Go override with the default tsrs backend in -the same built CLI. It runs three workloads on both Vite+ and vinext: type-aware -rules, rules with TypeScript diagnostics, and the full `vp check` command. - -Use the [codespaces-run skill](https://github.com/Dunqing/skills/tree/main/skills/codespaces-run). -The recipe uses one `standardLinux32gb` instance with four CPUs and 16 GB of RAM. -The skill retains the environment and stops the instance after artifact collection. -Do not run other builds or benchmarks on the instance during measurement. -The CPU model can change when the same Codespace restarts. Inspect each job's -metadata before combining results. Keep both backends of a comparison in the -same job, and identify any hardware change in the report. - -## Source and setup - -The measured CLI source is `22b07bd150aa57b3e04cc52e0b81d43294ffe982`. -The RFC patch from `da260f86fce1d43810c62e4e6aad011066c88cb9` preserves the -documentation edits present when this comparison started. The vinext corpus is -`249088af307c0a852fa6dc59c08fc6ec80e1620e` from `fengmk2/vinext`. - -Setup installs Node.js `24.22.2` for Vite+ and `26.13.0` for vinext. It installs -the package managers declared by those revisions: pnpm `12.11.2` and `11.1.1`. -Both projects and Vite+'s separate documentation workspace use frozen lockfiles. -The Python environment pins `psutil==7.0.0`. -Node downloads are checked against the official SHA256 files. - -The recipe builds the PR's CLI JavaScript. It uses the published Vite+ `1.5.0` -native binding, core, and prompts packages, plus the matching Rolldown binding. -It does not compile a complete native release. The setup receipt records hashes -of the CLI entry point, native binding, Node executables, and benchmark config. - -The repository's Ubuntu devcontainer can lack an SSH server. If `gh codespace ssh` -reports that problem, open the existing Codespace's web terminal and install -`openssh-server` with `sudo apt-get update` and -`sudo apt-get install -y --no-install-recommends openssh-server`. -Keep the repository's devcontainer configuration unchanged. Setup installs -`python3-venv` only if Python cannot import `ensurepip`. - -## Run - -Run these commands from this checkout. The temporary input directory stays -outside the source tree so it cannot change the measured file set. - -```sh -tsrs_skill_tool="${CODEX_HOME:-$HOME/.codex}/skills/codespaces-run/scripts/sandbox.py" -tsrs_bench_inputs="$(mktemp -d)" -cp bench/tsrs-lint-codespaces/inputs/* "$tsrs_bench_inputs/" -git diff 22b07bd150aa57b3e04cc52e0b81d43294ffe982 da260f86fce1d43810c62e4e6aad011066c88cb9 \ - -- rfcs/check-command.md rfcs/toolchain-command.md rfcs/migration-command.md \ - > "$tsrs_bench_inputs/rfcs.patch" - -python3 "$tsrs_skill_tool" submit \ - --repo voidzero-dev/vite-plus \ - --git-url https://github.com/voidzero-dev/vite-plus.git \ - --git-commit 22b07bd150aa57b3e04cc52e0b81d43294ffe982 \ - --stages "$PWD/bench/tsrs-lint-codespaces/stages.json" \ - --inputs "$tsrs_bench_inputs" \ - --machine standardLinux32gb --cpus 4 --min-memory-mib 14000 \ - --timeout 7200 --profile default \ - --environment-key tsrs-022-linux-x64-node24.22.2-26.13.0-pnpm12.11.2-11.1.1 - -python3 "$tsrs_skill_tool" events JOB_ID --follow -``` - -Use the returned job ID in the last command. Check the final receipt for -`collected: true`, a shutdown timestamp, and no cleanup error. Logs, raw samples, -memory traces, and results are retained under the skill's local job directory. -If a later stage fails, use the skill's stage recovery procedure to retain a -verified setup. Do not start another job while the previous job holds the lease. - -The runner also accepts `--project`, `--scenario`, and `--phase` filters. For a -focused repetition, submit a continuation after the prior job stops. Its script -can run the following commands with the recipe's input files: - -```sh -python3 "$JOB_INPUT_DIR/verify-setup.py" -"$JOB_ENV_DIR/venv/bin/python" "$JOB_INPUT_DIR/bench.py" \ - --project vinext --scenario full --phase time -``` - -This selection produces 22 timing records: ten pairs and two warm-ups. Keep the -new job's metadata and raw records when it replaces a previous timing set. - -## Method - -Validation runs each backend once for each workload before measurement. Every -run must exit successfully with no diagnostics and the expected file and rule -counts. Full checks must also report successful formatting. Memory runs check -the native executable identity to detect accidental comparisons of one backend -against itself. - -Timing uses ten pairs after one warm-up per backend. Each pair reverses the -previous pair's order. Memory uses a separate five-pair phase with its own -warm-ups. Each invocation starts a fresh CLI process. Installation, builds, -validation, and memory sampling are excluded from the timing table. - -The memory sampler records the simultaneous sum of resident set size (RSS) for -the CLI and its descendants. It sleeps for 20 ms between samples and records the -actual intervals. This includes Node, Oxlint, and the native type-aware backend. -Shared pages can count more than once, and sampling can miss short peaks. The -result is a sampled process-tree peak, not total allocation or container memory. - -Lint uses four threads. Vinext lint uses `OXLINT_TSGOLINT_CHECKER_SCHEDULING=sorted`, -as in the earlier comparison. Full checks use the machine's default thread count -and backend scheduling. The rules-only configuration disables `typeCheck` while -preserving the project's original lint rules and plugins. - -The summary reports medians and ranges. Warm-up samples have `pair=-1` and do -not contribute to the medians. Each full run produces 204 records, including -warm-ups, plus the separate 12-command validation phase. diff --git a/bench/tsrs-lint-codespaces/inputs/bench.py b/bench/tsrs-lint-codespaces/inputs/bench.py deleted file mode 100644 index 846ba77bc2..0000000000 --- a/bench/tsrs-lint-codespaces/inputs/bench.py +++ /dev/null @@ -1,249 +0,0 @@ -import argparse -import csv -import hashlib -import json -import os -import pathlib -import platform -import re -import statistics -import subprocess -import tempfile -import time - -import psutil - -ENV = pathlib.Path(os.environ['JOB_ENV_DIR']) -OUT = pathlib.Path(os.environ['ARTIFACTS_DIR']) -CONFIG = json.loads((ENV / 'bench-config.json').read_text()) -BASE_ENV = {key: value for key, value in os.environ.items() - if not key.startswith(('OXLINT_TSGOLINT', 'TSRS_')) - and key not in ('NODE_OPTIONS', 'NODE_PATH', 'GITHUB_ACTIONS')} -EXPECTED = {} -RECORDS = [] -SCENARIOS = ('rules', 'full', 'check') -TIMING_PAIRS = 10 -MEMORY_PAIRS = 5 - - -def progress(phase, message, completed=0, total=0): - value = {'phase': phase, 'message': message, 'completed': completed, 'total': total} - subprocess.run(['python3', os.environ['JOB_WORKER'], 'progress', os.environ['JOB_DIR'], json.dumps(value)], check=True) - print(json.dumps(value), flush=True) - - -def environment(project, scenario, backend): - env = BASE_ENV | { - 'PATH': str(pathlib.Path(project['node']).parent) + ':' + BASE_ENV['PATH'], - 'VP_HOME': str(ENV / 'vp-home'), 'VP_SELF_SETUP_NO_MODIFY_PATH': '1', - } - if scenario != 'check' and project['scheduling']: - env['OXLINT_TSGOLINT_CHECKER_SCHEDULING'] = project['scheduling'] - if backend == 'go': - env['OXLINT_TSGOLINT_PATH'] = CONFIG['go'] - return env - - -def lint_config(project): - code = ''' -import { createRequire } from 'node:module'; -import { pathToFileURL } from 'node:url'; -const require = createRequire(process.argv[1]); -const { loadConfigFromFile } = await import(pathToFileURL(require.resolve('vite')).href); -const result = await loadConfigFromFile({ command: 'serve', mode: 'development' }, undefined, process.cwd(), 'silent'); -if (!result?.config?.lint) throw new Error('Missing lint configuration'); -process.stdout.write(JSON.stringify(result.config.lint)); -''' - cli_package = str(pathlib.Path(CONFIG['cli']).parent.parent / 'package.json') - env = environment(project, 'rules', 'rust') | {'VP_RESOLVING_CONFIG_METADATA': '1'} - output = subprocess.check_output([project['node'], '--input-type=module', '-e', code, cli_package], cwd=project['root'], env=env, text=True) - original = json.loads(output) - (OUT / (project['name'] + '-lint-config.json')).write_text(json.dumps(original, indent=2) + '\n') - config = json.loads(output) - config.setdefault('options', {})['typeAware'] = True - config['options']['typeCheck'] = False - return config - - -def signature(project, scenario, stdout, stderr): - if scenario == 'check': - combined = stdout + stderr - formatted = re.search(r'pass: All ([\d,]+) files are correctly formatted', combined) - linted = re.search(r'pass: Found no warnings, lint errors, or type errors in ([\d,]+) files', combined) - assert formatted and linted, combined - files = int(linted[1].replace(',', '')) - result = {'files': files, 'format_files': int(formatted[1].replace(',', ''))} - else: - data = json.loads(stdout) - assert not data['diagnostics'], data['diagnostics'][:5] - files = data['number_of_files'] - assert data['number_of_rules'] == project['rules'], data - result = {key: data[key] for key in ('number_of_files', 'number_of_rules', 'threads_count', 'diagnostics')} - assert files == project['files'], (project['name'], files, project['files']) - key = (project['name'], scenario) - if key in EXPECTED: - assert result == EXPECTED[key], (key, result, EXPECTED[key]) - else: - EXPECTED[key] = result - return result - - -def sample(child, start): - root = psutil.Process(child.pid) - seen = {child.pid: root.create_time()} - trace = [] - processes = {} - while child.poll() is None: - try: - for process in root.children(recursive=True): - seen[process.pid] = process.create_time() - except psutil.Error: - pass - alive = [] - for pid, born in list(seen.items()): - try: - process = psutil.Process(pid) - with process.oneshot(): - if process.create_time() != born or process.status() == psutil.STATUS_ZOMBIE: - continue - rss = process.memory_info().rss - exe = process.exe() - elapsed = time.perf_counter() - start - key = f'{pid}:{exe}' - record = processes.setdefault(key, {'pid': pid, 'exe': exe, 'first': elapsed, 'peak_rss': 0}) - record['last'] = elapsed - record['peak_rss'] = max(record['peak_rss'], rss) - alive.append({'pid': pid, 'exe': exe, 'rss': rss}) - except psutil.Error: - pass - trace.append({'seconds': time.perf_counter() - start, 'rss': sum(p['rss'] for p in alive), 'processes': alive}) - time.sleep(0.02) - if time.perf_counter() - start > 300: - child.kill() - raise TimeoutError('Benchmark command exceeded 300 seconds') - return trace, list(processes.values()) - - -def run(project, scenario, backend, pair, phase, config=None): - args = ['check'] if scenario == 'check' else ['lint', '--type-aware', '--threads', '4', '--format', 'json'] - if scenario == 'full': - args += ['--type-check'] - if config: - args += ['-c', str(config)] - command = [project['node'], CONFIG['cli'], *args] - directory = OUT / phase / project['name'] - directory.mkdir(parents=True, exist_ok=True) - stem = directory / f'{scenario}-{backend}-{pair:02d}' - with stem.with_suffix('.stdout').open('w') as stdout, stem.with_suffix('.stderr').open('w') as stderr: - start = time.perf_counter() - child = subprocess.Popen(command, cwd=project['root'], env=environment(project, scenario, backend), stdout=stdout, stderr=stderr) - trace, processes = sample(child, start) if phase in ('validate', 'memory') else ([], []) - # Blocking wait avoids the polling latency of wait(timeout=...). - # The Codespaces worker enforces the overall task deadline. - status = child.wait() - seconds = time.perf_counter() - start - stdout = stem.with_suffix('.stdout').read_text() - stderr = stem.with_suffix('.stderr').read_text() - assert status == 0, (project['name'], scenario, backend, status, stdout[-5000:], stderr[-5000:]) - result = signature(project, scenario, stdout, stderr) - record = {'project': project['name'], 'scenario': scenario, 'backend': backend, - 'phase': phase, 'pair': pair, 'seconds': seconds, 'exit': status, - 'peak_rss': max((x['rss'] for x in trace), default=None), 'signature': result} - if trace: - native = [p for p in processes if pathlib.Path(p['exe']).name in ('tsgolint', 'tsrs')] - assert len(native) == 1, native - assert pathlib.Path(native[0]['exe']).name == ('tsgolint' if backend == 'go' else 'tsrs') - intervals = [b['seconds'] - a['seconds'] for a, b in zip(trace, trace[1:])] - record.update({'native': native[0], 'median_sample_interval_ms': statistics.median(intervals) * 1000}) - stem.with_suffix('.trace.json').write_text(json.dumps(trace)) - stem.with_suffix('.processes.json').write_text(json.dumps(processes, indent=2)) - stem.with_suffix('.json').write_text(json.dumps(record, indent=2) + '\n') - RECORDS.append(record) - (OUT / 'samples.json').write_text(json.dumps(RECORDS, indent=2) + '\n') - print(project['name'], scenario, backend, phase, pair, f'{seconds:.3f}s', record['peak_rss'], flush=True) - return record - - -def measure(project, scenario, phase): - handle = None - if scenario == 'rules': - handle = tempfile.NamedTemporaryFile(mode='w', suffix='.json', prefix='.tsrs-bench-', dir=project['root']) - json.dump(lint_config(project), handle) - handle.flush() - try: - pairs = 0 if phase == 'validate' else TIMING_PAIRS if phase == 'time' else MEMORY_PAIRS - for pair in range(-1, pairs): - backends = ('go', 'rust') if pair < 0 or pair % 2 == 0 else ('rust', 'go') - for backend in backends: - run(project, scenario, backend, pair, phase, handle.name if handle else None) - finally: - if handle: - handle.close() - - -def metadata(): - info = {'vp_commit': CONFIG['vp_commit'], 'python': platform.python_version(), 'psutil': psutil.__version__, - 'platform': platform.platform(), 'cpu_affinity': psutil.Process().cpu_affinity(), - 'memory_bytes': psutil.virtual_memory().total, 'timing_pairs': TIMING_PAIRS, - 'memory_pairs': MEMORY_PAIRS, 'sample_sleep_ms': 20, 'projects': CONFIG['projects'], - 'versions': json.loads((ENV / 'setup-receipt.json').read_text())['versions'], - 'selection': vars(options)} - for name, args in [('cpu', ['lscpu']), ('git_diff', ['git', 'diff', '--stat']), ('source', ['git', 'rev-parse', 'HEAD'])]: - info[name] = subprocess.check_output(args, text=True) - info['node_versions'] = {p['name']: subprocess.check_output([p['node'], '--version'], text=True).strip() for p in CONFIG['projects']} - info['cpu_percent_before'] = psutil.cpu_percent(interval=1) - (OUT / 'metadata.json').write_text(json.dumps(info, indent=2) + '\n') - - -def summarize(): - rows = [] - for project in selected_projects: - for scenario in selected_scenarios: - row = {'project': project['name'], 'scenario': scenario} - for backend in ('go', 'rust'): - for phase, metric in [('time', 'seconds'), ('memory', 'peak_rss')]: - if phase not in phases: - continue - values = [r[metric] for r in RECORDS if r['project'] == project['name'] and r['scenario'] == scenario and r['backend'] == backend and r['phase'] == phase and r['pair'] >= 0] - row[f'{backend}_{metric}'] = statistics.median(values) - row[f'{backend}_{metric}_range'] = [min(values), max(values)] - if 'time' in phases: - row['time_saved_percent'] = 100 * (1 - row['rust_seconds'] / row['go_seconds']) - if 'memory' in phases: - row['rss_saved_percent'] = 100 * (1 - row['rust_peak_rss'] / row['go_peak_rss']) - rows.append(row) - (OUT / 'summary.json').write_text(json.dumps(rows, indent=2) + '\n') - with (OUT / 'samples.csv').open('w') as file: - keys = ['project', 'scenario', 'backend', 'phase', 'pair', 'seconds', 'peak_rss', 'exit'] - writer = csv.DictWriter(file, fieldnames=keys, extrasaction='ignore') - writer.writeheader() - writer.writerows(RECORDS) - print('RESULTS', json.dumps(rows), flush=True) - - -parser = argparse.ArgumentParser() -parser.add_argument('--validate-only', action='store_true') -parser.add_argument('--project', choices=[p['name'] for p in CONFIG['projects']]) -parser.add_argument('--scenario', choices=SCENARIOS) -parser.add_argument('--phase', choices=('time', 'memory')) -options = parser.parse_args() -selected_projects = [p for p in CONFIG['projects'] if not options.project or p['name'] == options.project] -selected_scenarios = (options.scenario,) if options.scenario else SCENARIOS -metadata() -phases = ('validate',) if options.validate_only else (options.phase,) if options.phase else ('time', 'memory') -total = len(phases) * len(selected_projects) * len(selected_scenarios) -completed = 0 -for phase in phases: - for project in selected_projects: - for scenario in selected_scenarios: - progress(phase, f"{project['name']}: {scenario}", completed, total) - measure(project, scenario, phase) - completed += 1 - progress(phase, f"Completed {project['name']}: {scenario}", completed, total) -if not options.validate_only: - summarize() -expected_runs = len(selected_projects) * len(selected_scenarios) * sum( - 2 * (1 + (0 if phase == 'validate' else TIMING_PAIRS if phase == 'time' else MEMORY_PAIRS)) - for phase in phases) -assert len(RECORDS) == expected_runs, (len(RECORDS), expected_runs) -progress('validated' if options.validate_only else 'measured', 'All output assertions passed', total, total) diff --git a/bench/tsrs-lint-codespaces/inputs/docs.py b/bench/tsrs-lint-codespaces/inputs/docs.py deleted file mode 100644 index cc1cef3543..0000000000 --- a/bench/tsrs-lint-codespaces/inputs/docs.py +++ /dev/null @@ -1,11 +0,0 @@ -import os -import pathlib -import subprocess - -root = pathlib.Path.cwd() -env_root = pathlib.Path(os.environ['JOB_ENV_DIR']) -env = os.environ | {'PATH': str(env_root / 'node-v24.22.2-linux-x64/bin') + ':' + os.environ['PATH']} -subprocess.run([ - env_root / 'pnpm12/node_modules/.bin/pnpm', 'install', '--frozen-lockfile', - '--ignore-scripts', '--store-dir', env_root / 'pnpm-store', -], cwd=root / 'docs', env=env, check=True) diff --git a/bench/tsrs-lint-codespaces/inputs/docs.sh b/bench/tsrs-lint-codespaces/inputs/docs.sh deleted file mode 100644 index 0ff5cba507..0000000000 --- a/bench/tsrs-lint-codespaces/inputs/docs.sh +++ /dev/null @@ -1 +0,0 @@ -python3 "$JOB_INPUT_DIR/docs.py" diff --git a/bench/tsrs-lint-codespaces/inputs/run.sh b/bench/tsrs-lint-codespaces/inputs/run.sh deleted file mode 100644 index 68be2b7d7b..0000000000 --- a/bench/tsrs-lint-codespaces/inputs/run.sh +++ /dev/null @@ -1 +0,0 @@ -"$JOB_ENV_DIR/venv/bin/python" "$JOB_INPUT_DIR/bench.py" diff --git a/bench/tsrs-lint-codespaces/inputs/setup.py b/bench/tsrs-lint-codespaces/inputs/setup.py deleted file mode 100644 index 32fa921a55..0000000000 --- a/bench/tsrs-lint-codespaces/inputs/setup.py +++ /dev/null @@ -1,150 +0,0 @@ -import hashlib -import json -import os -import pathlib -import shutil -import subprocess -import urllib.request - -ROOT = pathlib.Path.cwd() -ENV = pathlib.Path(os.environ['JOB_ENV_DIR']) -ENV.mkdir(parents=True, exist_ok=True) -INPUT = pathlib.Path(os.environ['JOB_INPUT_DIR']) -OUT = pathlib.Path(os.environ['ARTIFACTS_DIR']) -VP_COMMIT = '22b07bd150aa57b3e04cc52e0b81d43294ffe982' -VINEXT_COMMIT = '249088af307c0a852fa6dc59c08fc6ec80e1620e' - - -def run(args, cwd=ROOT, env=None): - print('+', ' '.join(map(str, args)), flush=True) - subprocess.run(list(map(str, args)), cwd=cwd, env=env, check=True) - - -def progress(message): - run(['python3', os.environ['JOB_WORKER'], 'progress', os.environ['JOB_DIR'], - json.dumps({'phase': 'setup', 'message': message})]) - - -def fetch_git(url, revision, destination): - if not (destination / '.git').exists(): - destination.mkdir(parents=True, exist_ok=True) - run(['git', 'init', destination]) - run(['git', 'remote', 'add', 'origin', url], cwd=destination) - run(['git', 'fetch', '--depth', '1', 'origin', revision], cwd=destination) - run(['git', 'checkout', '--detach', 'FETCH_HEAD'], cwd=destination) - actual = subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=destination, text=True).strip() - assert actual == revision, (destination, actual, revision) - - -def node(version): - target = ENV / f'node-v{version}-linux-x64' - if not (target / 'bin/node').exists(): - filename = target.name + '.tar.xz' - base = f'https://nodejs.org/dist/v{version}/' - with urllib.request.urlopen(base + 'SHASUMS256.txt') as response: - checksums = response.read().decode() - (OUT / f'node-{version}-SHASUMS256.txt').write_text(checksums) - expected = next(line.split()[0] for line in checksums.splitlines() if line.split()[-1] == filename) - archive = ENV / filename - urllib.request.urlretrieve(base + filename, archive) - assert hashlib.sha256(archive.read_bytes()).hexdigest() == expected - run(['tar', '-xJf', archive, '-C', ENV]) - assert subprocess.check_output([target / 'bin/node', '--version'], text=True).strip() == 'v' + version - return target - - -def npm(prefix, packages, runtime): - prefix.mkdir(parents=True, exist_ok=True) - env = os.environ | {'PATH': str(runtime / 'bin') + ':' + os.environ['PATH']} - run([runtime / 'bin/npm', 'install', '--prefix', prefix, '--no-audit', '--no-fund', - '--ignore-scripts', '--package-lock=false', *packages], env=env) - - -def copy_dir(source, destination): - if destination.is_symlink(): - destination.unlink() - shutil.copytree(source, destination, dirs_exist_ok=True) - - -progress('Installing pinned Node and package-manager versions') -assert subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip() == VP_COMMIT -patch = INPUT / 'rfcs.patch' -if patch.stat().st_size: - reverse = subprocess.run(['git', 'apply', '--reverse', '--check', str(patch)], capture_output=True) - if reverse.returncode: - run(['git', 'apply', patch]) -node24 = node('24.22.2') -node26 = node('26.13.0') -npm(ENV / 'pnpm12', ['pnpm@12.11.2'], node24) -# pnpm 12 ships a shebang-less placeholder until its native installer runs. -run([node24 / 'bin/node', ENV / 'pnpm12/node_modules/pnpm/install.js']) -npm(ENV / 'pnpm11', ['pnpm@11.1.1'], node26) -for prefix, version in [('pnpm12', '12.11.2'), ('pnpm11', '11.1.1')]: - pm_env = os.environ | {'PATH': str(node26 / 'bin') + ':' + os.environ['PATH']} - assert subprocess.check_output([ENV / prefix / 'node_modules/.bin/pnpm', '--version'], cwd=ENV, env=pm_env, text=True).strip() == version -if not (ENV / 'venv/bin/python').exists(): - # The repository's minimal Ubuntu image can omit ensurepip. - check = subprocess.run(['python3', '-c', 'import ensurepip'], capture_output=True, text=True) - if check.returncode: - if 'No module named' not in check.stderr or 'ensurepip' not in check.stderr: - raise RuntimeError(check.stderr) - progress('Installing python3-venv because the container lacks ensurepip') - run(['sudo', 'apt-get', 'update']) - run(['sudo', 'env', 'DEBIAN_FRONTEND=noninteractive', 'apt-get', 'install', '-y', 'python3-venv']) - run(['python3', '-m', 'venv', ENV / 'venv']) -run([ENV / 'venv/bin/python', '-m', 'pip', 'install', 'psutil==7.0.0']) -base_env = os.environ | { - 'PATH': str(node24 / 'bin') + ':' + str(ENV / 'pnpm12/node_modules/.bin') + ':' + os.environ['PATH'], - 'VP_HOME': str(ENV / 'vp-home'), 'VP_SELF_SETUP_NO_MODIFY_PATH': '1', - 'CARGO_HOME': str(ENV / 'cargo'), 'CARGO_TARGET_DIR': str(ENV / 'cargo-target'), -} -progress('Fetching exact source dependencies and published native artifacts') -for name, spec in json.loads((ROOT / 'packages/tools/.upstream-versions.json').read_text()).items(): - fetch_git(spec['repo'], spec['hash'], ROOT / name) -run([node24 / 'bin/node', 'packages/tools/src/vendored-vitest.ts'], env=base_env) -npm(ENV / 'published', [ - 'vite-plus@1.5.0', '@voidzero-dev/vite-plus-core@1.5.0', - '@voidzero-dev/vite-plus-linux-x64-gnu@1.5.0', - '@voidzero-dev/vite-plus-prompts@1.5.0', 'rolldown@1.2.16', -], node24) -run([ENV / 'pnpm12/node_modules/.bin/pnpm', 'install', '--frozen-lockfile', '--ignore-scripts', - '--store-dir', ENV / 'pnpm-store'], env=base_env) -modules = ENV / 'published/node_modules' -for source, destination in [ - (modules / 'vite-plus/dist', ROOT / 'packages/cli/dist'), - (modules / '@voidzero-dev/vite-plus-core/dist', ROOT / 'packages/core/dist'), - (modules / '@voidzero-dev/vite-plus-prompts/dist', ROOT / 'packages/prompts/dist'), - (modules / 'rolldown/dist', ROOT / 'rolldown/packages/rolldown/dist'), - (modules / '@rolldown/binding-linux-x64-gnu', ROOT / 'rolldown/packages/rolldown/node_modules/@rolldown/binding-linux-x64-gnu'), - (modules / '@voidzero-dev/vite-plus-linux-x64-gnu', ROOT / 'packages/core/node_modules/@voidzero-dev/vite-plus-linux-x64-gnu'), -]: - copy_dir(source, destination) -native = modules / '@voidzero-dev/vite-plus-linux-x64-gnu/vite-plus.linux-x64-gnu.node' -shutil.copy2(native, ROOT / 'packages/cli/binding' / native.name) -progress('Building CLI JavaScript; using the published 1.5.0 Linux native binding and core') -run([ENV / 'pnpm12/node_modules/.bin/pnpm', '--filter', 'vite-plus', 'build-ts'], env=base_env) -progress('Preparing the pinned vinext benchmark corpus and generated types') -vinext = ENV / 'vinext' -fetch_git('https://github.com/fengmk2/vinext.git', VINEXT_COMMIT, vinext) -vinext_env = base_env | {'PATH': str(node26 / 'bin') + ':' + str(ENV / 'pnpm11/node_modules/.bin') + ':' + os.environ['PATH']} -run([ENV / 'pnpm11/node_modules/.bin/pnpm', 'install', '--frozen-lockfile', '--ignore-scripts', - '--store-dir', ENV / 'pnpm-store-vinext'], cwd=vinext, env=vinext_env) -run([ENV / 'pnpm11/node_modules/.bin/pnpm', 'run', 'postinstall'], cwd=vinext, env=vinext_env) -config = { - 'vp_commit': VP_COMMIT, 'cli': str(ROOT / 'packages/cli/bin/vp'), - 'go': str(ROOT / 'packages/cli/node_modules/.bin/tsgolint'), - 'tsrs': str(ROOT / 'packages/cli/node_modules/.bin/tsrs-lint'), - 'projects': [ - {'name': 'vite-plus', 'root': str(ROOT), 'node': str(node24 / 'bin/node'), 'files': 385, 'rules': 174, 'scheduling': None}, - {'name': 'vinext', 'root': str(vinext), 'node': str(node26 / 'bin/node'), 'files': 1828, 'rules': 151, 'scheduling': 'sorted', 'commit': VINEXT_COMMIT}, - ], -} -(ENV / 'bench-config.json').write_text(json.dumps(config, indent=2) + '\n') -versions = {} -for package in ['tsrs', 'oxlint', 'oxlint-tsgolint']: - versions[package] = json.loads((ROOT / 'packages/cli/node_modules' / package / 'package.json').read_text())['version'] -assert versions == {'tsrs': '0.22.0-ts7.1.0-beta', 'oxlint': '1.91.0', 'oxlint-tsgolint': '7.0.2005'}, versions -files = [ROOT / 'packages/cli/dist/bin.js', native, node24 / 'bin/node', node26 / 'bin/node', ENV / 'bench-config.json'] -receipt = {'versions': versions, 'hashes': {str(p): hashlib.sha256(p.read_bytes()).hexdigest() for p in files}} -(ENV / 'setup-receipt.json').write_text(json.dumps(receipt, indent=2) + '\n') -print('SETUP_COMPLETE', json.dumps(versions), flush=True) diff --git a/bench/tsrs-lint-codespaces/inputs/setup.sh b/bench/tsrs-lint-codespaces/inputs/setup.sh deleted file mode 100644 index b2ff6948e5..0000000000 --- a/bench/tsrs-lint-codespaces/inputs/setup.sh +++ /dev/null @@ -1 +0,0 @@ -python3 "$JOB_INPUT_DIR/setup.py" diff --git a/bench/tsrs-lint-codespaces/inputs/validate.sh b/bench/tsrs-lint-codespaces/inputs/validate.sh deleted file mode 100644 index 1f8e6ed6c9..0000000000 --- a/bench/tsrs-lint-codespaces/inputs/validate.sh +++ /dev/null @@ -1 +0,0 @@ -"$JOB_ENV_DIR/venv/bin/python" "$JOB_INPUT_DIR/bench.py" --validate-only diff --git a/bench/tsrs-lint-codespaces/inputs/verify-setup.py b/bench/tsrs-lint-codespaces/inputs/verify-setup.py deleted file mode 100644 index e14726debc..0000000000 --- a/bench/tsrs-lint-codespaces/inputs/verify-setup.py +++ /dev/null @@ -1,6 +0,0 @@ -import hashlib,json,os,pathlib -root=pathlib.Path(os.environ['JOB_ENV_DIR']) -receipt=json.loads((root/'setup-receipt.json').read_text()) -for name,expected in receipt['hashes'].items(): - assert hashlib.sha256(pathlib.Path(name).read_bytes()).hexdigest()==expected,name -print('Setup artifacts verified') diff --git a/bench/tsrs-lint-codespaces/inputs/verify-setup.sh b/bench/tsrs-lint-codespaces/inputs/verify-setup.sh deleted file mode 100644 index 3968785a51..0000000000 --- a/bench/tsrs-lint-codespaces/inputs/verify-setup.sh +++ /dev/null @@ -1 +0,0 @@ -python3 "$JOB_INPUT_DIR/verify-setup.py" diff --git a/bench/tsrs-lint-codespaces/results/main-metadata.json b/bench/tsrs-lint-codespaces/results/main-metadata.json deleted file mode 100644 index 84ee55482f..0000000000 --- a/bench/tsrs-lint-codespaces/results/main-metadata.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "vp_commit": "22b07bd150aa57b3e04cc52e0b81d43294ffe982", - "python": "3.10.12", - "psutil": "7.0.0", - "platform": "Linux-6.8.0-1064-azure-x86_64-with-glibc2.35", - "cpu_affinity": [0, 1, 2, 3], - "memory_bytes": 16770494464, - "timing_pairs": 10, - "memory_pairs": 5, - "sample_sleep_ms": 20, - "versions": { - "tsrs": "0.22.0-ts7.1.0-beta", - "oxlint": "1.91.0", - "oxlint-tsgolint": "7.0.2005" - }, - "node_versions": { - "vite-plus": "v24.22.2", - "vinext": "v26.13.0" - }, - "cpu_model": "AMD EPYC 9V74 80-Core Processor", - "rfcs_commit": "da260f86fce1d43810c62e4e6aad011066c88cb9", - "vinext_commit": "249088af307c0a852fa6dc59c08fc6ec80e1620e", - "job_id": "job-3dbd90be3d4f4aa3", - "validation_commands": 12, - "benchmark_records": 204, - "median_sampling_interval_ms": 21.781716749956104, - "sampling_interval_median_range_ms": [21.504334000383096, 24.079619999611168], - "full_check_signatures": { - "vite-plus": { - "files": 385, - "format_files": 632 - }, - "vinext": { - "files": 1828, - "format_files": 4673 - } - }, - "worker_result": { - "status": "succeeded", - "exit_code": 0, - "started_at": 1793866444.5705564, - "finished_at": 1793872607.508668 - }, - "excluded_timing_set": { - "project": "vinext", - "scenario": "full", - "phase": "time", - "reason": "Browser troubleshooting started editor extension activity during this timing set. A clean repetition replaces it." - }, - "harness_sha256": "5ae0087c81988829d74777717f4e06561aeb883c33017478f4ef33f2da85a42d", - "collected": true -} diff --git a/bench/tsrs-lint-codespaces/results/main-samples.csv b/bench/tsrs-lint-codespaces/results/main-samples.csv deleted file mode 100644 index 394d9e3859..0000000000 --- a/bench/tsrs-lint-codespaces/results/main-samples.csv +++ /dev/null @@ -1,205 +0,0 @@ -project,scenario,backend,phase,pair,seconds,peak_rss,exit -vite-plus,rules,go,time,-1,3.6880124389999764,,0 -vite-plus,rules,rust,time,-1,2.191973147999988,,0 -vite-plus,rules,go,time,0,3.713578874000177,,0 -vite-plus,rules,rust,time,0,2.2759946600001513,,0 -vite-plus,rules,rust,time,1,2.4920109750000847,,0 -vite-plus,rules,go,time,1,3.6422700799998893,,0 -vite-plus,rules,go,time,2,3.5738986520000253,,0 -vite-plus,rules,rust,time,2,2.242526223999903,,0 -vite-plus,rules,rust,time,3,2.223109660000091,,0 -vite-plus,rules,go,time,3,3.483162299000014,,0 -vite-plus,rules,go,time,4,3.7109736130000783,,0 -vite-plus,rules,rust,time,4,2.2263701169999877,,0 -vite-plus,rules,rust,time,5,2.2016010700001516,,0 -vite-plus,rules,go,time,5,3.9088208739999573,,0 -vite-plus,rules,go,time,6,3.736302202999923,,0 -vite-plus,rules,rust,time,6,2.187699441999939,,0 -vite-plus,rules,rust,time,7,2.1890677399999277,,0 -vite-plus,rules,go,time,7,3.5215749090000372,,0 -vite-plus,rules,go,time,8,3.7870844199999283,,0 -vite-plus,rules,rust,time,8,2.17189058200006,,0 -vite-plus,rules,rust,time,9,2.187169283000003,,0 -vite-plus,rules,go,time,9,3.839309356000058,,0 -vite-plus,full,go,time,-1,4.654679539999961,,0 -vite-plus,full,rust,time,-1,2.3562958489999346,,0 -vite-plus,full,go,time,0,4.566960510999934,,0 -vite-plus,full,rust,time,0,2.3931662680001864,,0 -vite-plus,full,rust,time,1,2.3698954330000106,,0 -vite-plus,full,go,time,1,4.634962337999923,,0 -vite-plus,full,go,time,2,4.632292471000028,,0 -vite-plus,full,rust,time,2,2.412981780000109,,0 -vite-plus,full,rust,time,3,2.430237735999981,,0 -vite-plus,full,go,time,3,4.53366976999996,,0 -vite-plus,full,go,time,4,4.307837832000132,,0 -vite-plus,full,rust,time,4,2.493581193999944,,0 -vite-plus,full,rust,time,5,2.3662074239998674,,0 -vite-plus,full,go,time,5,4.511783857999944,,0 -vite-plus,full,go,time,6,5.0457770020000225,,0 -vite-plus,full,rust,time,6,2.3568083630000274,,0 -vite-plus,full,rust,time,7,2.3296725099999094,,0 -vite-plus,full,go,time,7,4.698553215999937,,0 -vite-plus,full,go,time,8,4.591066730999955,,0 -vite-plus,full,rust,time,8,2.364594460999797,,0 -vite-plus,full,rust,time,9,2.456160010000076,,0 -vite-plus,full,go,time,9,4.855081535999943,,0 -vite-plus,check,go,time,-1,5.750060867999991,,0 -vite-plus,check,rust,time,-1,3.431430506999959,,0 -vite-plus,check,go,time,0,5.560635703999878,,0 -vite-plus,check,rust,time,0,3.5964835449999555,,0 -vite-plus,check,rust,time,1,3.4927115529999355,,0 -vite-plus,check,go,time,1,6.118377254000052,,0 -vite-plus,check,go,time,2,5.643318074000035,,0 -vite-plus,check,rust,time,2,3.5944409420001193,,0 -vite-plus,check,rust,time,3,3.4559823089998645,,0 -vite-plus,check,go,time,3,5.5692066710000745,,0 -vite-plus,check,go,time,4,5.942744820999906,,0 -vite-plus,check,rust,time,4,3.629871501000025,,0 -vite-plus,check,rust,time,5,3.5768438600000536,,0 -vite-plus,check,go,time,5,5.60959895100018,,0 -vite-plus,check,go,time,6,5.8363495700000385,,0 -vite-plus,check,rust,time,6,3.4873933159999524,,0 -vite-plus,check,rust,time,7,3.661024717999908,,0 -vite-plus,check,go,time,7,5.935824476999869,,0 -vite-plus,check,go,time,8,5.847994853000046,,0 -vite-plus,check,rust,time,8,3.4268209399999705,,0 -vite-plus,check,rust,time,9,3.530400184999962,,0 -vite-plus,check,go,time,9,6.113309405000109,,0 -vinext,rules,go,time,-1,52.948004241000035,,0 -vinext,rules,rust,time,-1,46.124062173000084,,0 -vinext,rules,go,time,0,52.21461774299996,,0 -vinext,rules,rust,time,0,46.608522696000136,,0 -vinext,rules,rust,time,1,46.39468327899999,,0 -vinext,rules,go,time,1,51.841172385999926,,0 -vinext,rules,go,time,2,52.348372229000006,,0 -vinext,rules,rust,time,2,43.62988959800009,,0 -vinext,rules,rust,time,3,44.699639799999886,,0 -vinext,rules,go,time,3,49.70923635600002,,0 -vinext,rules,go,time,4,53.68720330700012,,0 -vinext,rules,rust,time,4,43.043341374999954,,0 -vinext,rules,rust,time,5,46.44647001299995,,0 -vinext,rules,go,time,5,56.2561799030002,,0 -vinext,rules,go,time,6,55.63514441699999,,0 -vinext,rules,rust,time,6,42.57279852900001,,0 -vinext,rules,rust,time,7,45.58332635399984,,0 -vinext,rules,go,time,7,49.66620632200011,,0 -vinext,rules,go,time,8,51.518826092999916,,0 -vinext,rules,rust,time,8,45.142249451000225,,0 -vinext,rules,rust,time,9,46.268095335,,0 -vinext,rules,go,time,9,51.84008323399985,,0 -vinext,full,go,time,-1,54.97031217999984,,0 -vinext,full,rust,time,-1,49.50701832100003,,0 -vinext,full,go,time,0,54.54909113500025,,0 -vinext,full,rust,time,0,48.777478402999805,,0 -vinext,full,rust,time,1,50.565860768999755,,0 -vinext,full,go,time,1,54.497046376999606,,0 -vinext,full,go,time,2,55.068685018999986,,0 -vinext,full,rust,time,2,49.378554876999715,,0 -vinext,full,rust,time,3,46.8017662240004,,0 -vinext,full,go,time,3,54.68703606200006,,0 -vinext,full,go,time,4,56.505208954999944,,0 -vinext,full,rust,time,4,49.47823296100023,,0 -vinext,full,rust,time,5,46.74362833999976,,0 -vinext,full,go,time,5,55.63798557900009,,0 -vinext,full,go,time,6,54.669546999999966,,0 -vinext,full,rust,time,6,49.366139873000066,,0 -vinext,full,rust,time,7,48.29169880900008,,0 -vinext,full,go,time,7,60.90340383700004,,0 -vinext,full,go,time,8,58.05361229900018,,0 -vinext,full,rust,time,8,49.801143192999916,,0 -vinext,full,rust,time,9,48.88615762499967,,0 -vinext,full,go,time,9,56.70309664500019,,0 -vinext,check,go,time,-1,61.557492223999816,,0 -vinext,check,rust,time,-1,50.663133098000344,,0 -vinext,check,go,time,0,62.98640043899968,,0 -vinext,check,rust,time,0,48.491100012999595,,0 -vinext,check,rust,time,1,49.07317851800008,,0 -vinext,check,go,time,1,62.14544949099991,,0 -vinext,check,go,time,2,60.34417550099988,,0 -vinext,check,rust,time,2,50.885471571999915,,0 -vinext,check,rust,time,3,50.162232835000395,,0 -vinext,check,go,time,3,62.48697384699972,,0 -vinext,check,go,time,4,63.368093478000446,,0 -vinext,check,rust,time,4,50.91846492900004,,0 -vinext,check,rust,time,5,53.96345718400062,,0 -vinext,check,go,time,5,68.245969263,,0 -vinext,check,go,time,6,64.2242278700005,,0 -vinext,check,rust,time,6,52.53176643899951,,0 -vinext,check,rust,time,7,50.54870111699984,,0 -vinext,check,go,time,7,61.418192543999794,,0 -vinext,check,go,time,8,62.163307601999804,,0 -vinext,check,rust,time,8,47.784832813999856,,0 -vinext,check,rust,time,9,48.62056124399987,,0 -vinext,check,go,time,9,61.23984774499968,,0 -vite-plus,rules,go,memory,-1,3.940764267999839,1458286592,0 -vite-plus,rules,rust,memory,-1,2.329289043999779,1018667008,0 -vite-plus,rules,go,memory,0,3.857643793000534,1215549440,0 -vite-plus,rules,rust,memory,0,2.352922975000183,976420864,0 -vite-plus,rules,rust,memory,1,2.5503717050005434,1004322816,0 -vite-plus,rules,go,memory,1,3.7755272800004605,1337851904,0 -vite-plus,rules,go,memory,2,3.9427604910006266,1220345856,0 -vite-plus,rules,rust,memory,2,2.2911063749997993,1000955904,0 -vite-plus,rules,rust,memory,3,2.3516781999996965,998109184,0 -vite-plus,rules,go,memory,3,3.8952087559991924,1343397888,0 -vite-plus,rules,go,memory,4,3.78354734200002,1261670400,0 -vite-plus,rules,rust,memory,4,2.3114647920001516,964509696,0 -vite-plus,full,go,memory,-1,5.309363115000451,1652715520,0 -vite-plus,full,rust,memory,-1,2.525028889999703,1011937280,0 -vite-plus,full,go,memory,0,4.856635449999885,1443962880,0 -vite-plus,full,rust,memory,0,2.658091739000156,1061445632,0 -vite-plus,full,rust,memory,1,2.5839939859997685,1056391168,0 -vite-plus,full,go,memory,1,4.8830151010006375,1477206016,0 -vite-plus,full,go,memory,2,5.28511940000044,1383268352,0 -vite-plus,full,rust,memory,2,2.5511508569998114,1054629888,0 -vite-plus,full,rust,memory,3,2.5333675159999984,1069342720,0 -vite-plus,full,go,memory,3,5.299707000000126,1786830848,0 -vite-plus,full,go,memory,4,4.7314949120000165,1393152000,0 -vite-plus,full,rust,memory,4,2.576505080000061,1045348352,0 -vite-plus,check,go,memory,-1,5.992466878999949,1424461824,0 -vite-plus,check,rust,memory,-1,3.699139065000054,1118486528,0 -vite-plus,check,go,memory,0,5.935010570000486,1546260480,0 -vite-plus,check,rust,memory,0,3.6238119339996047,1078734848,0 -vite-plus,check,rust,memory,1,3.7297287820001657,1094008832,0 -vite-plus,check,go,memory,1,5.904669201999241,1786224640,0 -vite-plus,check,go,memory,2,6.3074621320001825,1534910464,0 -vite-plus,check,rust,memory,2,3.609159493000334,1111625728,0 -vite-plus,check,rust,memory,3,3.7218976559997827,1092087808,0 -vite-plus,check,go,memory,3,5.842184367999835,1543946240,0 -vite-plus,check,go,memory,4,6.1350453840004775,1427783680,0 -vite-plus,check,rust,memory,4,3.898626783000509,1090322432,0 -vinext,rules,go,memory,-1,54.02692431700052,4816293888,0 -vinext,rules,rust,memory,-1,46.12704977300018,3161968640,0 -vinext,rules,go,memory,0,53.78586441100015,4626669568,0 -vinext,rules,rust,memory,0,48.66075261300011,3163717632,0 -vinext,rules,rust,memory,1,48.704375655999684,3253788672,0 -vinext,rules,go,memory,1,53.11478282000007,4844425216,0 -vinext,rules,go,memory,2,50.72010573700027,5035069440,0 -vinext,rules,rust,memory,2,46.750776367999606,3171049472,0 -vinext,rules,rust,memory,3,43.352124744999855,3257614336,0 -vinext,rules,go,memory,3,55.42714346699995,4705017856,0 -vinext,rules,go,memory,4,50.09278948500014,4499263488,0 -vinext,rules,rust,memory,4,44.060889810000845,3327598592,0 -vinext,full,go,memory,-1,56.74663075999979,5482045440,0 -vinext,full,rust,memory,-1,49.11478131599961,3299753984,0 -vinext,full,go,memory,0,57.2608009810001,5504036864,0 -vinext,full,rust,memory,0,50.5766341210001,3399245824,0 -vinext,full,rust,memory,1,46.43882131400005,3354329088,0 -vinext,full,go,memory,1,56.82105735300047,5004701696,0 -vinext,full,go,memory,2,57.29950182000084,5265002496,0 -vinext,full,rust,memory,2,50.1835814720007,3529072640,0 -vinext,full,rust,memory,3,49.69551689400032,3426652160,0 -vinext,full,go,memory,3,57.23072275499999,4893655040,0 -vinext,full,go,memory,4,57.49116492400026,5388804096,0 -vinext,full,rust,memory,4,47.16479458400045,3357630464,0 -vinext,check,go,memory,-1,63.65577673000007,5786767360,0 -vinext,check,rust,memory,-1,50.898781433000295,3493175296,0 -vinext,check,go,memory,0,60.84386064899991,5591572480,0 -vinext,check,rust,memory,0,50.34470988399971,3572527104,0 -vinext,check,rust,memory,1,49.92382415800057,3534950400,0 -vinext,check,go,memory,1,61.628392914999495,6064586752,0 -vinext,check,go,memory,2,58.81841730999986,5774688256,0 -vinext,check,rust,memory,2,51.66727800700028,3535589376,0 -vinext,check,rust,memory,3,48.62187229600022,3533045760,0 -vinext,check,go,memory,3,63.150578589999895,6065008640,0 -vinext,check,go,memory,4,62.494274702999974,5812363264,0 -vinext,check,rust,memory,4,50.306470861999514,3523481600,0 diff --git a/bench/tsrs-lint-codespaces/results/summary.json b/bench/tsrs-lint-codespaces/results/summary.json deleted file mode 100644 index bdb9ccf395..0000000000 --- a/bench/tsrs-lint-codespaces/results/summary.json +++ /dev/null @@ -1,98 +0,0 @@ -[ - { - "project": "vite-plus", - "scenario": "rules", - "go_seconds": 3.7122762435001277, - "go_seconds_range": [3.483162299000014, 3.9088208739999573], - "go_peak_rss": 1261670400, - "go_peak_rss_range": [1215549440, 1343397888], - "rust_seconds": 2.2123553650001213, - "rust_seconds_range": [2.17189058200006, 2.4920109750000847], - "rust_peak_rss": 998109184, - "rust_peak_rss_range": [964509696, 1004322816], - "time_saved_percent": 40.40434439991467, - "rss_saved_percent": 20.88986283580878, - "timing_job": "job-3dbd90be3d4f4aa3", - "memory_job": "job-3dbd90be3d4f4aa3" - }, - { - "project": "vite-plus", - "scenario": "full", - "go_seconds": 4.611679600999992, - "go_seconds_range": [4.307837832000132, 5.0457770020000225], - "go_peak_rss": 1443962880, - "go_peak_rss_range": [1383268352, 1786830848], - "rust_seconds": 2.3815308505000985, - "rust_seconds_range": [2.3296725099999094, 2.493581193999944], - "rust_peak_rss": 1056391168, - "rust_peak_rss_range": [1045348352, 1069342720], - "time_saved_percent": 48.35870969909336, - "rss_saved_percent": 26.8408362408873, - "timing_job": "job-3dbd90be3d4f4aa3", - "memory_job": "job-3dbd90be3d4f4aa3" - }, - { - "project": "vite-plus", - "scenario": "check", - "go_seconds": 5.842172211500042, - "go_seconds_range": [5.560635703999878, 6.118377254000052], - "go_peak_rss": 1543946240, - "go_peak_rss_range": [1427783680, 1786224640], - "rust_seconds": 3.553622022500008, - "rust_seconds_range": [3.4268209399999705, 3.661024717999908], - "rust_peak_rss": 1092087808, - "rust_peak_rss_range": [1078734848, 1111625728], - "time_saved_percent": 39.172932706350736, - "rss_saved_percent": 29.26646150580995, - "timing_job": "job-3dbd90be3d4f4aa3", - "memory_job": "job-3dbd90be3d4f4aa3" - }, - { - "project": "vinext", - "scenario": "rules", - "go_seconds": 52.027895064499944, - "go_seconds_range": [49.66620632200011, 56.2561799030002], - "go_peak_rss": 4705017856, - "go_peak_rss_range": [4499263488, 5035069440], - "rust_seconds": 45.36278790250003, - "rust_seconds_range": [42.57279852900001, 46.608522696000136], - "rust_peak_rss": 3253788672, - "rust_peak_rss_range": [3163717632, 3327598592], - "time_saved_percent": 12.810641587050675, - "rss_saved_percent": 30.84428642814485, - "timing_job": "job-3dbd90be3d4f4aa3", - "memory_job": "job-3dbd90be3d4f4aa3" - }, - { - "project": "vinext", - "scenario": "full", - "go_seconds": 54.19228503350001, - "go_seconds_range": [51.108869654000046, 55.4913207269999], - "go_peak_rss": 5265002496, - "go_peak_rss_range": [4893655040, 5504036864], - "rust_seconds": 44.79370169149996, - "rust_seconds_range": [42.42826828800003, 46.32845908899998], - "rust_peak_rss": 3399245824, - "rust_peak_rss_range": [3354329088, 3529072640], - "time_saved_percent": 17.343028322555753, - "rss_saved_percent": 35.436957027417904, - "timing_job": "job-4685d3c6e32f48bf", - "memory_job": "job-3dbd90be3d4f4aa3" - }, - { - "project": "vinext", - "scenario": "check", - "go_seconds": 62.32514072449976, - "go_seconds_range": [60.34417550099988, 68.245969263], - "go_peak_rss": 5812363264, - "go_peak_rss_range": [5591572480, 6065008640], - "rust_seconds": 50.355466976000116, - "rust_seconds_range": [47.784832813999856, 53.96345718400062], - "rust_peak_rss": 3534950400, - "rust_peak_rss_range": [3523481600, 3572527104], - "time_saved_percent": 19.205209341459884, - "rss_saved_percent": 39.18221832598796, - "timing_job": "job-3dbd90be3d4f4aa3", - "memory_job": "job-3dbd90be3d4f4aa3" - } -] diff --git a/bench/tsrs-lint-codespaces/results/timing-rerun-metadata.json b/bench/tsrs-lint-codespaces/results/timing-rerun-metadata.json deleted file mode 100644 index 083a981844..0000000000 --- a/bench/tsrs-lint-codespaces/results/timing-rerun-metadata.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "vp_commit": "22b07bd150aa57b3e04cc52e0b81d43294ffe982", - "python": "3.10.12", - "psutil": "7.0.0", - "platform": "Linux-6.8.0-1064-azure-x86_64-with-glibc2.35", - "cpu_affinity": [0, 1, 2, 3], - "memory_bytes": 16770494464, - "timing_pairs": 10, - "versions": { - "tsrs": "0.22.0-ts7.1.0-beta", - "oxlint": "1.91.0", - "oxlint-tsgolint": "7.0.2005" - }, - "node_versions": { - "vite-plus": "v24.22.2", - "vinext": "v26.13.0" - }, - "selection": { - "validate_only": false, - "project": "vinext", - "scenario": "full", - "phase": "time" - }, - "cpu_model": "AMD EPYC 7763 64-Core Processor", - "job_id": "job-4685d3c6e32f48bf", - "records": 22, - "worker_result": { - "status": "succeeded", - "exit_code": 0, - "started_at": 1793893477.6474826, - "finished_at": 1793894561.3346298 - }, - "harness_sha256": "734df51a34c3f202dad7e007648a1ca718daa3e9c2a26d5d0c9ba32a952992f8", - "collected": true, - "shutdown_confirmed_at": 1793894671.662227 -} diff --git a/bench/tsrs-lint-codespaces/results/timing-rerun-samples.csv b/bench/tsrs-lint-codespaces/results/timing-rerun-samples.csv deleted file mode 100644 index c82e7ce8c5..0000000000 --- a/bench/tsrs-lint-codespaces/results/timing-rerun-samples.csv +++ /dev/null @@ -1,23 +0,0 @@ -project,scenario,backend,phase,pair,seconds,peak_rss,exit -vinext,full,go,time,-1,53.405041512000025,,0 -vinext,full,rust,time,-1,45.34521164800003,,0 -vinext,full,go,time,0,54.13607682600002,,0 -vinext,full,rust,time,0,45.48383163799997,,0 -vinext,full,rust,time,1,46.32845908899998,,0 -vinext,full,go,time,1,54.58117138299997,,0 -vinext,full,go,time,2,51.108869654000046,,0 -vinext,full,rust,time,2,42.42826828800003,,0 -vinext,full,rust,time,3,45.07651155499991,,0 -vinext,full,go,time,3,52.29078975899995,,0 -vinext,full,go,time,4,54.71693397199999,,0 -vinext,full,rust,time,4,44.51089182800001,,0 -vinext,full,rust,time,5,45.55147170500004,,0 -vinext,full,go,time,5,54.248493241000006,,0 -vinext,full,go,time,6,54.310657021,,0 -vinext,full,rust,time,6,42.593737007999835,,0 -vinext,full,rust,time,7,44.00615199700019,,0 -vinext,full,go,time,7,55.4913207269999,,0 -vinext,full,go,time,8,54.02196162600012,,0 -vinext,full,rust,time,8,43.040702052999904,,0 -vinext,full,rust,time,9,45.36723157000006,,0 -vinext,full,go,time,9,53.41371220799988,,0 diff --git a/bench/tsrs-lint-codespaces/stages.json b/bench/tsrs-lint-codespaces/stages.json deleted file mode 100644 index 5506f61409..0000000000 --- a/bench/tsrs-lint-codespaces/stages.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "stages": [ - { - "name": "setup", - "script": "setup.sh", - "inputs": { - "workspace": [ - "pnpm-lock.yaml", - "pnpm-workspace.yaml", - "packages/cli/package.json", - "packages/tools/.upstream-versions.json" - ], - "inputs": ["setup.py", "verify-setup.py", "rfcs.patch"] - }, - "outputs": { - "env": ["setup-receipt.json", "bench-config.json"] - }, - "verify": "verify-setup.sh" - }, - { - "name": "docs", - "script": "docs.sh", - "inputs": { - "workspace": ["docs/package.json", "docs/pnpm-lock.yaml", "docs/pnpm-workspace.yaml"], - "inputs": ["docs.py"], - "env": ["setup-receipt.json"] - } - }, - { - "name": "validate", - "script": "validate.sh", - "inputs": { - "inputs": ["bench.py"], - "env": ["setup-receipt.json", "bench-config.json"] - } - }, - { - "name": "benchmark", - "script": "run.sh", - "inputs": { - "inputs": ["bench.py"], - "env": ["setup-receipt.json", "bench-config.json"] - } - } - ] -} diff --git a/bench/tsrs-lint.md b/bench/tsrs-lint.md index b91343953d..9ef24c0174 100644 --- a/bench/tsrs-lint.md +++ b/bench/tsrs-lint.md @@ -40,7 +40,7 @@ descendants, including Node, Oxlint, and the native backend. One GiB is 1,073,74 The sampler sleeps for 20 ms. The median observed interval across runs was 21.8 ms. Shared pages can count more than once, and sampling can miss short peaks. These values are process-tree RSS, not total -allocation or container memory. Per-case ranges are in the [summary](./tsrs-lint-codespaces/results/summary.json). +allocation or container memory. ## Conditions and interpretation @@ -74,28 +74,15 @@ Vinext retains its original JS plugin rules, including in place. This limits the elapsed-time improvement even though the memory reduction is larger. The measurements do not include that rule's separate optimization. -## Evidence and reproduction +## Run selection -Use the [Codespaces recipe](./tsrs-lint-codespaces/README.md) to recreate the pinned -setup and all six comparisons. The recipe uses frozen lockfiles and retains -verified dependencies between jobs. - -The [main samples](./tsrs-lint-codespaces/results/main-samples.csv) contain all 204 -invocations, including warm-ups. A browser connection used to diagnose an SSH +The main job completed 204 invocations, including warm-ups. +A browser connection used to diagnose an SSH outage started editor extension activity during vinext's type-checking timing set. That entire timing set is excluded. The table instead uses the 22 records -from the [clean timing repetition](./tsrs-lint-codespaces/results/timing-rerun-samples.csv). +from a clean timing repetition. The repetition uses the same retained build and dependencies, with their hashes checked before execution. All memory values come from the main job. -Records with `pair=-1` are warm-ups and do not contribute to medians. For the -vinext `full`/`time` group, select the repetition CSV; select the main CSV for -all other groups. Take each backend's median `seconds` or `peak_rss` value and -calculate the reduction as `100 * (1 - rust / go)`. - -The [main metadata](./tsrs-lint-codespaces/results/main-metadata.json) and -[repetition metadata](./tsrs-lint-codespaces/results/timing-rerun-metadata.json) -record versions, hardware, source revisions, and successful worker results. -The main worker finished while the controller was disconnected; its complete -artifacts were recovered from the stopped Codespace. The jobs are `job-3dbd90be3d4f4aa3` and -`job-4685d3c6e32f48bf`. +Warm-up runs do not contribute to medians. Each reduction is calculated as +`100 * (1 - rust / go)` from the corresponding backend medians. From 765414285ad8e3943eff726ed2adba105f7679b0 Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 6 Nov 2026 15:55:51 +0800 Subject: [PATCH 10/25] bench(lint): compare installed dependency disk usage --- bench/tsrs-lint.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/bench/tsrs-lint.md b/bench/tsrs-lint.md index 9ef24c0174..2c7652aafc 100644 --- a/bench/tsrs-lint.md +++ b/bench/tsrs-lint.md @@ -42,6 +42,44 @@ The sampler sleeps for 20 ms. The median observed interval across runs was and sampling can miss short peaks. These values are process-tree RSS, not total allocation or container memory. +## Installed disk footprint + +Clean production installations on Linux x64 with glibc compare published `1.5.0` +with preview `0.0.0-commit.4fcf99edbe42fe3305dcd41dc884a4c4b7f5fd45`. +The preview binary-size comment measures individual executables and native bindings; +it does not measure the installed dependency tree. + +| Scope | Go (MiB) | tsrs (MiB) | Increase (MiB) | Increase | +| -------------------------------- | -------: | ---------: | -------------: | -------: | +| Complete `node_modules`, on disk | 132.93 | 150.87 | 17.94 | 13.5% | +| Complete `node_modules`, files | 123.39 | 139.27 | 15.88 | 12.9% | +| Backend packages, on disk | 21.63 | 39.69 | 18.06 | 83.5% | + +One MiB is 1,048,576 bytes. On-disk values use `du -s -B1 node_modules` and +include directory allocation. File sizes sum regular-file lengths without following +symlinks or counting an inode twice. The filesystem uses 4 KiB blocks. +Stores, download caches, Node.js, pnpm, and the global `vp` executable are excluded. + +The job used Node.js `24.22.2`, pnpm `10.34.7`, and each version's published +`vp-install-lock.yaml`. It reproduced the global installer's manifest and frozen +installation flags. Separate stores and `--config.package-import-method=copy` +prevent shared hard links from affecting the standalone footprint. Each installation +contained 80 packages. Version output and type-aware linting with TypeScript +diagnostics passed for both installations. The preview contained no `oxlint-tsgolint`. + +The backend totals include `oxlint-tsgolint` and `@oxlint-tsgolint/linux-x64`, or +`tsrs` and `@tsrs/linux-x64-gnu`. The Rust distribution includes a larger executable, +TypeScript library declarations, JavaScript APIs, and source maps. Its package +footprint increases despite the runtime and memory improvements. The total +installation comparison also includes other release-to-preview changes; +the backend row separates the backend packages from those changes. + +The measurement ran on 2026-11-06 in Codespaces job `job-2a30c8ec3fb84524`. +Scripts, raw package inventories, lockfiles, and logs remain outside the repository. +The lockfile SHA256 values are +`eb06955c3e76589da771286d3bfc6e6ea940c1a09ac64f597d2c72eb94403ad1` for `1.5.0` +and `cf8fe673142ace0abaa362552bff78281f3b8f00e01bc13fa0eb93c5b1344683` for the preview. + ## Conditions and interpretation The jobs used one GitHub Codespace with four vCPUs, 16 GB advertised RAM, and an From c04b21bba2e1fb15b1182374aecdabf5b4eb1096 Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 6 Nov 2026 16:50:11 +0800 Subject: [PATCH 11/25] docs(lint): record ecosystem compatibility regressions --- bench/tsrs-lint-ecosystem.md | 212 +++++++++++++++++++++++++++++++++++ bench/tsrs-lint.md | 4 + 2 files changed, 216 insertions(+) create mode 100644 bench/tsrs-lint-ecosystem.md diff --git a/bench/tsrs-lint-ecosystem.md b/bench/tsrs-lint-ecosystem.md new file mode 100644 index 0000000000..af9d8129ac --- /dev/null +++ b/bench/tsrs-lint-ecosystem.md @@ -0,0 +1,212 @@ +# Ecosystem correctness comparison + +The default `tsrs@0.22.0-ts7.1.0-beta` switch is not diagnostically equivalent to +`oxlint-tsgolint@7.0.2005`. The representative sweep found new type-check errors in +Hono and Vue Core, and a DOM-library compatibility change in OneSignal. These +findings prevent a regression-free release claim. + +On 2026-11-06, a Linux x64/glibc Codespace ran 141 commands across 14 pinned forks +from the [ecosystem catalog](https://github.com/vite-plus-ecosystem-ci/.github). +The explicit type-check workload covered 2,289 files. No command timed out or +terminated by a signal. Seven repositories had identical normalized diagnostics; +seven had differences. This covers the selected repositories, not all 83 catalog entries. + +## Controls and method + +The comparison used three toolchains: + +| Control | Published Vite+ package | Native lint backend | +| ----------------------- | ------------------------------------------------------- | -------------------------- | +| Stable | `1.5.0` | `oxlint-tsgolint@7.0.2005` | +| Preview with Go control | `0.0.0-commit.4fcf99edbe42fe3305dcd41dc884a4c4b7f5fd45` | `oxlint-tsgolint@7.0.2005` | +| Default preview | Same preview | `tsrs@0.22.0-ts7.1.0-beta` | + +All controls used Oxlint `1.91.0`. Stable and preview with Go produced identical +normalized lint and TypeScript diagnostics in every selected repository. This +control separates backend differences from other preview changes. + +Each project retained its original frozen lockfile, application dependencies, and +source configuration. The runner temporarily linked the published CLI, Vite, +Oxlint, and Oxfmt into workspace importers, then restored those links. Tracked +source files remained unchanged. The setup used each project's pinned package +manager, with lifecycle scripts disabled. Node.js was `24.22.2`, except +`eslint-plugin-oxlint` and `react-data-grid`, which used `26.13.0` to meet their engine requirements. + +Each control ran these commands: + +```sh +vp lint --format json --threads 4 +vp lint --type-aware --type-check --format json --threads 4 +vp check +``` + +Hono retained its lint scopes: `src`, `runtime-tests`, `build`, `perf-measures`, +`benchmarks`, and `adapters`. Hono and Vue Core do not enable type-aware linting in +their current configurations; the explicit flags add stress coverage. React Data +Grid enables type-aware rules but does not enable TypeScript diagnostics by default. + +The runner also used `vp run --no-cache` for five existing project tasks: +`oxc-walker`'s `test:types`, `urllib`'s `typecheck`, `media-processors`'s `typecheck`, +`react-data-grid`'s `typecheck`, and `vue-core`'s `check`. These tasks retain their +own compiler selection. A `tsc` task does not automatically become a `tsrs` task. + +The runner compared sorted JSON diagnostics, including codes, severity, messages, +files, spans, and help text. It normalized absolute project paths. Process traces +confirmed the expected native backend in all 42 explicit type-check runs. React +Data Grid and Vue Core required the matching optional `@vitest/browser-playwright@5.1.0` +peer for configuration imports. No browser installation was required. + +## Repository results + +The counts below include errors and warnings from the explicit type-check workload. +Equal counts alone do not establish equality; the comparison also checked diagnostic content. +All source links point to immutable commits in `vite-plus-ecosystem-ci`. + +| Repository / commit | Files | Go diagnostics | tsrs diagnostics | Comparison | +| -------------------------------------------------------------------------------------------------------------------------------------------------- | ----: | -------------: | ---------------: | --------------------------------------------- | +| [oxc-walker `9e520f25d`](https://github.com/vite-plus-ecosystem-ci/oxc-walker/tree/9e520f25d2bf5c3748f51d143c534fedff207147) | 12 | 0 | 0 | Identical diagnostics | +| [eslint-plugin-oxlint `ca4b10897`](https://github.com/vite-plus-ecosystem-ci/eslint-plugin-oxlint/tree/ca4b1089757245fa9a6a6ee46341d94a218d9c05) | 41 | 0 | 0 | Identical diagnostics | +| [solid-services `add4b036b`](https://github.com/vite-plus-ecosystem-ci/solid-services/tree/add4b036b72de7e6372b07faf0aed6b3f2e506d4) | 10 | 0 | 0 | Identical diagnostics | +| [pinia-shared-state `1b1ec6109`](https://github.com/vite-plus-ecosystem-ci/pinia-shared-state/tree/1b1ec6109b6f46079e4ddb5aeb9feff1d7188c24) | 6 | 0 | 0 | Identical diagnostics | +| [urllib `91f32d53f`](https://github.com/vite-plus-ecosystem-ci/urllib/tree/91f32d53faaae34a9f09f38e402e617420bc4d15) | 72 | 6 | 6 | Identical diagnostics | +| [anywidget `443d720b2`](https://github.com/vite-plus-ecosystem-ci/anywidget/tree/443d720b2233fff131a0f0a842b08744c8ffabd6) | 66 | 14 | 14 | Identical diagnostics | +| [css-modules-kit `634608fdd`](https://github.com/vite-plus-ecosystem-ci/css-modules-kit/tree/634608fdd401001d1fb05511a89177acf38b8111) | 131 | 0 | 0 | Identical diagnostics | +| [media-processors `79c87cd5c`](https://github.com/vite-plus-ecosystem-ci/media-processors/tree/79c87cd5c066a0a9abea979044c38c69603a85e8) | 22 | 101 | 91 | 10 fewer diagnostics | +| [web-app-template `f9a09f3d8`](https://github.com/vite-plus-ecosystem-ci/web-app-template/tree/f9a09f3d81002372e839247c6274af43177bbeca) | 8 | 3 | 1 | 2 fewer diagnostics | +| [OneSignal-Website-SDK `5375640c0`](https://github.com/vite-plus-ecosystem-ci/OneSignal-Website-SDK/tree/5375640c001a2dc462ed1cb938520edfb9a23d07) | 298 | 8 | 9 | New DOM-library requirement | +| [react-data-grid `384aedffc`](https://github.com/vite-plus-ecosystem-ci/react-data-grid/tree/384aedffc2cac013666b58b6787d888b9dde3a94) | 127 | 302 | 300 | 2 fewer diagnostics; message changes | +| [vue-clamp `6f0da3991`](https://github.com/vite-plus-ecosystem-ci/vue-clamp/tree/6f0da3991f804587ba402a7ca22aec4564eab938) | 134 | 5 | 1 | 4 fewer recursion errors | +| [hono `c91b60a25`](https://github.com/vite-plus-ecosystem-ci/hono/tree/c91b60a251186fb024557c6d55a11d8e388f179c) | 457 | 474 | 476 | 2 new contextual-typing errors; span changes | +| [vue-core `747e8d668`](https://github.com/vite-plus-ecosystem-ci/vue-core/tree/747e8d668c4a64ae6bd9dcc74c173364f1c1490b) | 905 | 547 | 546 | 7 new source errors; 8 fewer recursion errors | + +Default `vp lint` diagnostics matched in ten repositories. Its four differences +were OneSignal, media-processors, web-app-template, and vue-clamp. Vue Clamp's lint +exit changed from `1` to `0` after four `TS2321` errors disappeared; one warning remained. +The additional Hono, Vue Core, and React Data Grid differences appeared with explicit type-check flags. + +`vp check` passed in oxc-walker, eslint-plugin-oxlint, pinia-shared-state, and +css-modules-kit under all controls. The other ten failed under all controls. +Formatting stopped five of them before lint: solid-services, anywidget, +vue-clamp, Hono, and Vue Core. These failures do not demonstrate backend failure +or backend coverage. + +## Confirmed contextual-typing regression + +[Hono's `Style` implementation](https://github.com/vite-plus-ecosystem-ci/hono/blob/c91b60a251186fb024557c6d55a11d8e388f179c/src/helper/css/index.ts#L192) +produces two new `TS7031` errors for `children` and `nonce` with tsrs. This reduced +source reproduces the failure with strict type checking: + +```ts +interface StyleType { + (args?: { children?: Promise; nonce?: string }): string; +} +export const Style: StyleType = ({ children, nonce } = {}) => `${children}${nonce}`; +``` + +TypeScript `6.0.3`, TypeScript `7.0.2`, and both Go lint controls accept this source. +The default preview and standalone tsrs report both errors. An explicit parameter +type annotation makes all controls pass. Hono's production project also passes +`tsc -p tsconfig.build.json --noEmit` but fails the same check with standalone tsrs. +This confirms a compiler difference independent of Vite+ backend resolution. + +[Vue Core's `applyTextModel`](https://github.com/vite-plus-ecosystem-ci/vue-core/blob/747e8d668c4a64ae6bd9dcc74c173364f1c1490b/packages/runtime-vapor/src/directives/vModel.ts#L46) +has the same default-destructuring pattern. Its `trim`, `number`, and `lazy` bindings +produce three new `TS7031` errors. A four-line reduction with a mapped optional +parameter passes both TypeScript versions and both Go controls, but fails both tsrs paths. + +## Vue Core selector types + +[Vue Core's scoped-style transformer](https://github.com/vite-plus-ecosystem-ci/vue-core/blob/747e8d668c4a64ae6bd9dcc74c173364f1c1490b/packages/compiler-sfc/src/style/pluginScoped.ts#L107) +produces a new `TS5115` circular-instantiation error and two `TS7006` callback errors. +This smaller case uses the project's locked `postcss-selector-parser@7.1.6`: + +```ts +import type parser from 'postcss-selector-parser'; +export function inspect(selector: parser.Selector): void { + selector.each((n) => { + if (n.type === 'pseudo') { + n.nodes[0].each((ss) => { + void ss; + }); + } + }); +} +``` + +Both TypeScript versions and both Go controls accept the reduced source. The +default preview and standalone tsrs report `TS5115` and `TS7006`. The reduction +uses strict mode, bundler module resolution, and `skipLibCheck: true`. + +## Vue Core generic conditional return type + +[Vue Core's `toHandlerKey`](https://github.com/vite-plus-ecosystem-ci/vue-core/blob/747e8d668c4a64ae6bd9dcc74c173364f1c1490b/packages/shared/src/general.ts#L147) +produces a new `TS2322` when it assigns a generic function with a conditional +return type. Both an isolated copy of the original implementation and this +reduction reproduce the error: + +```ts +declare function cache string>(fn: T): T; +export const handler: (s: T) => T extends '' ? '' : `on${Capitalize}` = cache( + (s: T) => { + const value = s ? `on${s}` : ''; + return value as T extends '' ? '' : `on${Capitalize}`; + }, +); +``` + +Both TypeScript versions and both Go controls accept both cases. The default +preview and standalone tsrs reject both cases with `TS2322`. The reduction uses +the same strict compiler options as the selector case. + +## OneSignal DOM-library difference + +[OneSignal's notification mock](https://github.com/vite-plus-ecosystem-ci/OneSignal-Website-SDK/blob/5375640c001a2dc462ed1cb938520edfb9a23d07/__test__/support/environment/TestEnvironmentHelpers.ts#L46) +produces a new `TS2741`: the constructor lacks `Notification.maxActions`. The tsrs +`lib.dom.d.ts` requires that static property. The tested TypeScript `6.0.3` and +`7.0.2` libraries do not contain it. + +```ts +type HasMaxActions = 'maxActions' extends keyof typeof Notification ? true : false; +export const hasMaxActions: HasMaxActions = false; +``` + +The TypeScript and Go controls accept this probe. Both tsrs paths report `TS2322`. +This establishes a library-contract difference; it does not establish that the +new DOM declaration is incorrect. The difference affects OneSignal's existing +configuration because it already enables TypeScript diagnostics. + +## Other differences and limits + +Media-processors, web-app-template, vue-clamp, and React Data Grid report fewer +recursive-comparison or configuration-compatibility errors with tsrs. Vue Core +also loses eight `TS2321` errors but adds seven production-source errors. Its total +therefore falls from 547 to 546 despite the new failures. Fewer diagnostics are +not classified as fixes without a separate correctness check. + +React Data Grid also changes expanded type text in twelve existing diagnostics. +Vue Core changes two existing messages, and Hono moves three existing `TS2875` +spans. These presentation changes are included in the comparison. + +Several errors are shared by both backends. They include urllib's six +`Http2Stream.respond` errors, anywidget's 14 type errors, missing generated +`video-track-processor` declarations in media-processors, and a missing translation +file in web-app-template. The broad Hono workload includes optional benchmark +and runtime fixtures. React Data Grid and Vue Core include test declarations +that conflict with the overlaid toolchain's Vitest types. + +Only oxc-walker's existing type-check task passed. The other four tasks failed +under every control, with matching TypeScript diagnostics after installation-path +normalization. Vue Core initially returned `2` and then `1` as its incremental +cache warmed; fresh build-info files produced exit `2` and identical output in +all three controls. No task compiler was replaced by the default lint backend. + +This method isolates the backend while preserving application dependencies. It +does not reproduce each repository's full build pipeline or a complete dependency +upgrade. Shared setup and source errors must not be counted as new tsrs regressions. +The reduced cases provide independent evidence for the new compiler failures. + +The primary sweep used Codespaces job `job-d8ac88c64fc64b11`. Final anywidget, +React Data Grid, and Vue Core results came from `job-f5f12efe4b514e83`, which also +reduced the Hono and OneSignal cases. Vue reductions used `job-cf53f53e0a2d4216` and `job-120ca876783b4213`. +The harness, frozen dependency receipts, process traces, compiler outputs, and +raw JSON remain outside this repository. diff --git a/bench/tsrs-lint.md b/bench/tsrs-lint.md index 2c7652aafc..8b5d3c3270 100644 --- a/bench/tsrs-lint.md +++ b/bench/tsrs-lint.md @@ -3,6 +3,10 @@ This comparison uses the default `tsrs@0.22.0-ts7.1.0-beta` integration and an explicit `oxlint-tsgolint@7.0.2005` override. Both use Oxlint `1.91.0` and the same built CLI. +The separate [ecosystem correctness comparison](./tsrs-lint-ecosystem.md) found +compiler regressions and library differences in representative forks. The performance +results below do not establish diagnostic equivalence. + ## Elapsed time Each value is the median of ten fresh CLI processes after one warm-up per backend. From 7d5d66a9cb222f2a82fe4138df885064988eab7a Mon Sep 17 00:00:00 2001 From: MK Date: Sun, 8 Nov 2026 23:23:08 +0800 Subject: [PATCH 12/25] docs(lint): attribute compatibility changes to upstream TypeScript --- bench/tsrs-lint-ecosystem.md | 113 ++++++++++++++++++++++++++++++++--- bench/tsrs-lint.md | 3 +- 2 files changed, 106 insertions(+), 10 deletions(-) diff --git a/bench/tsrs-lint-ecosystem.md b/bench/tsrs-lint-ecosystem.md index af9d8129ac..4b085542cb 100644 --- a/bench/tsrs-lint-ecosystem.md +++ b/bench/tsrs-lint-ecosystem.md @@ -2,8 +2,10 @@ The default `tsrs@0.22.0-ts7.1.0-beta` switch is not diagnostically equivalent to `oxlint-tsgolint@7.0.2005`. The representative sweep found new type-check errors in -Hono and Vue Core, and a DOM-library compatibility change in OneSignal. These -findings prevent a regression-free release claim. +Hono and Vue Core, and a DOM-library compatibility change in OneSignal. The +exact-version audit below attributes all four reproduced categories to the pinned +TypeScript `7.1.0-beta` behavior. It found no Rust-port defect in these cases. +The default switch still changes diagnostics for projects that use the older backend. On 2026-11-06, a Linux x64/glibc Codespace ran 141 commands across 14 pinned forks from the [ecosystem catalog](https://github.com/vite-plus-ecosystem-ci/.github). @@ -11,6 +13,50 @@ The explicit type-check workload covered 2,289 files. No command timed out or terminated by a signal. Seven repositories had identical normalized diagnostics; seven had differences. This covers the selected repositories, not all 83 catalog entries. +## Exact-upstream attribution (2026-11-08) + +The original `6.0.3`, `7.0.2`, and `oxlint-tsgolint@7.0.2005` controls establish +upgrade compatibility, but cannot identify a Rust-port defect by themselves. +The [tsrs compatibility contract](https://github.com/oxc-project/tsrs/blob/625f918c85a3e12824ede39cd127d0f866b8d9c8/docs/COMPATIBILITY.md#conformance) +defines correctness against its exact upstream Go compiler, including upstream bugs. + +This audit compared the retained tsrs executable with published `typescript@7.1.0-beta`. +Both target `microsoft/TypeScript@3e9236361fa97a9e6c685f4833ac457ae2d8c50a`. +The reference wrapper and native package both identify that commit in `gitHead`. +The Rust source audit used release tag `v0.22.0`, commit +`625f918c85a3e12824ede39cd127d0f866b8d9c8`. + +All seven reduced cases produced identical diagnostic output and exit codes: + +| Reduced case | TypeScript `7.1.0-beta` | tsrs `0.22.0` | +| ----------------------------------------------- | ------------------------------- | ------------- | +| Hono default destructuring | Two `TS7031`, exit `2` | Identical | +| Hono explicit parameter annotation | No errors, exit `0` | Identical | +| Vue mapped-parameter destructuring | Three `TS7031`, exit `2` | Identical | +| Vue selector iteration | `TS5115` and `TS7006`, exit `2` | Identical | +| Vue conditional return, reduced | `TS2322`, exit `2` | Identical | +| Vue conditional return, original implementation | `TS2322`, exit `2` | Identical | +| DOM `maxActions` probe | `TS2322`, exit `2` | Identical | + +The four categories require different interpretations: + +| Category | Assessment | Follow-up | +| ----------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| Default destructuring | Upstream TypeScript `7.1` false positive; tsrs matches it | Follow TypeScript #64974; an explicit parameter annotation passes | +| Conditional return type | Upstream `7.1` compatibility regression; no Rust divergence | Investigate the related conditional-type normalization fix in TypeScript #64307 | +| Selector recursion | Upstream `7.1` recursion restriction exposes the dependency's circular type construction | Rework or update the selector declarations; do not assume the older acceptance proves validity | +| Notification mock | Correct enforcement of the newer DOM declaration | Add the missing static member to the mock | + +Matching the pinned compiler does not mean every upstream diagnostic is desirable. +It means these reproductions do not justify changing the Rust port independently. +The new default also adopts a newer TypeScript version, so its compatibility cost +must be assessed separately from the implementation language. + +The 14 compiler invocations ran in Codespaces job `job-3aecfcc9bb6f49bb`. +The input files and compiler options were unchanged from the earlier reductions. +Raw output, executable/version evidence, package-lock hashes, and scripts remain +outside this repository. This follow-up did not repeat the full 14-repository sweep. + ## Controls and method The comparison used three toolchains: @@ -90,7 +136,7 @@ Formatting stopped five of them before lint: solid-services, anywidget, vue-clamp, Hono, and Vue Core. These failures do not demonstrate backend failure or backend coverage. -## Confirmed contextual-typing regression +## Upstream contextual-typing regression [Hono's `Style` implementation](https://github.com/vite-plus-ecosystem-ci/hono/blob/c91b60a251186fb024557c6d55a11d8e388f179c/src/helper/css/index.ts#L192) produces two new `TS7031` errors for `children` and `nonce` with tsrs. This reduced @@ -107,12 +153,21 @@ TypeScript `6.0.3`, TypeScript `7.0.2`, and both Go lint controls accept this so The default preview and standalone tsrs report both errors. An explicit parameter type annotation makes all controls pass. Hono's production project also passes `tsc -p tsconfig.build.json --noEmit` but fails the same check with standalone tsrs. -This confirms a compiler difference independent of Vite+ backend resolution. +This confirms a compiler-version difference independent of Vite+ backend resolution. [Vue Core's `applyTextModel`](https://github.com/vite-plus-ecosystem-ci/vue-core/blob/747e8d668c4a64ae6bd9dcc74c173364f1c1490b/packages/runtime-vapor/src/directives/vModel.ts#L46) has the same default-destructuring pattern. Its `trim`, `number`, and `lazy` bindings produce three new `TS7031` errors. A four-line reduction with a mapped optional -parameter passes both TypeScript versions and both Go controls, but fails both tsrs paths. +parameter passes TypeScript `6.0.3`, TypeScript `7.0.2`, and both Go controls, but fails both tsrs paths. + +The exact `7.1.0-beta` reference reports the same errors. Its +[`checkDeclarationInitializer` / `padObjectLiteralType`](https://github.com/microsoft/TypeScript/blob/3e9236361fa97a9e6c685f4833ac457ae2d8c50a/tsc/internal/checker/checker.go#L17205) +pads missing properties of the `{}` initializer and calls +`getTypeFromBindingElement` with `reportErrors: true`. The +[Rust port follows the same path](https://github.com/oxc-project/tsrs/blob/625f918c85a3e12824ede39cd127d0f866b8d9c8/crates/tsrs_checker/src/checker_08.rs#L2980). +That path can report implicit `any` despite an available contextual parameter type. +TypeScript tracks this as [bug #64974](https://github.com/microsoft/TypeScript/issues/64974). +The rejection is a false positive, while the tsrs implementation conforms to its reference. ## Vue Core selector types @@ -133,10 +188,25 @@ export function inspect(selector: parser.Selector): void { } ``` -Both TypeScript versions and both Go controls accept the reduced source. The +TypeScript `6.0.3`, TypeScript `7.0.2`, and both Go controls accept the reduced source. The default preview and standalone tsrs report `TS5115` and `TS7006`. The reduction uses strict mode, bundler module resolution, and `skipLibCheck: true`. +The dependency declares `Selector = _Selector`, where `_Selector` +extends `Container>`. `Node` itself includes `Selector`, and +`Diff` is a distributive conditional type. Resolving the base members can +therefore require comparing the type whose base members are still being resolved. +TypeScript [#64372](https://github.com/microsoft/TypeScript/pull/64372) deliberately +stopped allowing base type arguments to depend on partially resolved members. + +Both the [upstream instantiation guard](https://github.com/microsoft/TypeScript/blob/3e9236361fa97a9e6c685f4833ac457ae2d8c50a/tsc/internal/checker/checker.go#L22601) +and [Rust guard](https://github.com/oxc-project/tsrs/blob/625f918c85a3e12824ede39cd127d0f866b8d9c8/crates/tsrs_checker/src/checker_11.rs#L1319) +stop at depth `100` or `5,000,000` instantiations and report the circular type names. +The exact upstream compiler reports the same `Container` / `Diff` error and lost +callback type. This is a dependency compatibility problem under the newer rules, +not evidence of a Rust implementation error. The causal link to the stricter +inheritance rule follows from the source review; this audit did not bisect that upstream change. + ## Vue Core generic conditional return type [Vue Core's `toHandlerKey`](https://github.com/vite-plus-ecosystem-ci/vue-core/blob/747e8d668c4a64ae6bd9dcc74c173364f1c1490b/packages/shared/src/general.ts#L147) @@ -154,10 +224,24 @@ export const handler: (s: T) => T extends '' ? '' : `on${Capit ); ``` -Both TypeScript versions and both Go controls accept both cases. The default +TypeScript `6.0.3`, TypeScript `7.0.2`, and both Go controls accept both cases. The default preview and standalone tsrs reject both cases with `TS2322`. The reduction uses the same strict compiler options as the selector case. +The exact `7.1.0-beta` compiler rejects both cases identically. The +[upstream conditional-type comparison](https://github.com/microsoft/TypeScript/blob/3e9236361fa97a9e6c685f4833ac457ae2d8c50a/tsc/internal/checker/relater.go#L3758) +and [Rust comparison](https://github.com/oxc-project/tsrs/blob/625f918c85a3e12824ede39cd127d0f866b8d9c8/crates/tsrs_checker/src/relater_2.rs#L2541) +compare the conditional types without normalizing both distributed type parameters. +This is consistent with the upstream asymmetry described in +[#64673](https://github.com/microsoft/TypeScript/issues/64673): instantiation can +normalize one side while the other retains a distributed parameter. + +The open [fix #64307](https://github.com/microsoft/TypeScript/pull/64307) normalizes +both sides and is relevant to this failure. That root-cause connection is an +inference from the source and diagnostic chain. The patch was not applied or +validated against this case here. The exact-reference failure is sufficient to +rule out a Rust-only divergence for this reproduction. + ## OneSignal DOM-library difference [OneSignal's notification mock](https://github.com/vite-plus-ecosystem-ci/OneSignal-Website-SDK/blob/5375640c001a2dc462ed1cb938520edfb9a23d07/__test__/support/environment/TestEnvironmentHelpers.ts#L46) @@ -170,11 +254,20 @@ type HasMaxActions = 'maxActions' extends keyof typeof Notification ? true : fal export const hasMaxActions: HasMaxActions = false; ``` -The TypeScript and Go controls accept this probe. Both tsrs paths report `TS2322`. +The TypeScript `6.0.3` / `7.0.2` and Go lint controls accept this probe. Both tsrs paths report `TS2322`. This establishes a library-contract difference; it does not establish that the new DOM declaration is incorrect. The difference affects OneSignal's existing configuration because it already enables TypeScript diagnostics. +The tsrs and TypeScript `7.1.0-beta` `lib.dom.d.ts` files are byte-identical: +SHA256 `2f8c8d9e23d03e00c353412f5b2be3b1fd893290fc0976ad2229681bb7d46a35`. +The [Notifications standard](https://notifications.spec.whatwg.org/#dom-notification-maxactions) +also defines a static read-only `maxActions` member. Assigning `MockNotification` +to the constructor type now correctly requires that static member. Adding an +instance property would not satisfy it. A mock that supports no actions can +supply `static readonly maxActions = 0`. This is a mock adaptation to the updated +standard library, not a compiler false positive. + ## Other differences and limits Media-processors, web-app-template, vue-clamp, and React Data Grid report fewer @@ -203,7 +296,9 @@ all three controls. No task compiler was replaced by the default lint backend. This method isolates the backend while preserving application dependencies. It does not reproduce each repository's full build pipeline or a complete dependency upgrade. Shared setup and source errors must not be counted as new tsrs regressions. -The reduced cases provide independent evidence for the new compiler failures. +The reduced cases provide independent evidence for the compiler-version differences. +The exact-reference audit attributes the four reproduced categories; it does not +establish exact-upstream parity for every diagnostic in the full sweep. The primary sweep used Codespaces job `job-d8ac88c64fc64b11`. Final anywidget, React Data Grid, and Vue Core results came from `job-f5f12efe4b514e83`, which also diff --git a/bench/tsrs-lint.md b/bench/tsrs-lint.md index 8b5d3c3270..c9ba28b082 100644 --- a/bench/tsrs-lint.md +++ b/bench/tsrs-lint.md @@ -4,7 +4,8 @@ This comparison uses the default `tsrs@0.22.0-ts7.1.0-beta` integration and an e `oxlint-tsgolint@7.0.2005` override. Both use Oxlint `1.91.0` and the same built CLI. The separate [ecosystem correctness comparison](./tsrs-lint-ecosystem.md) found -compiler regressions and library differences in representative forks. The performance +upstream TypeScript `7.1` regressions and library differences in representative forks. +The exact-version audit found no Rust-port divergence in the reduced cases. The performance results below do not establish diagnostic equivalence. ## Elapsed time From 2ed63677954914f187da6acf1beb58db5e223185 Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 9 Nov 2026 14:13:32 +0800 Subject: [PATCH 13/25] chore(deps): update tsrs to 0.23.0-ts7.1.0-beta --- bench/tsrs-lint-ecosystem.md | 40 ++++++++++++++++++++- bench/tsrs-lint.md | 3 ++ pnpm-lock.yaml | 68 ++++++++++++++++++------------------ pnpm-workspace.yaml | 2 +- rfcs/check-command.md | 9 ++++- rfcs/toolchain-command.md | 2 +- 6 files changed, 86 insertions(+), 38 deletions(-) diff --git a/bench/tsrs-lint-ecosystem.md b/bench/tsrs-lint-ecosystem.md index 4b085542cb..1c54263050 100644 --- a/bench/tsrs-lint-ecosystem.md +++ b/bench/tsrs-lint-ecosystem.md @@ -1,6 +1,6 @@ # Ecosystem correctness comparison -The default `tsrs@0.22.0-ts7.1.0-beta` switch is not diagnostically equivalent to +The `tsrs@0.22.0-ts7.1.0-beta` experiment is not diagnostically equivalent to `oxlint-tsgolint@7.0.2005`. The representative sweep found new type-check errors in Hono and Vue Core, and a DOM-library compatibility change in OneSignal. The exact-version audit below attributes all four reproduced categories to the pinned @@ -13,6 +13,44 @@ The explicit type-check workload covered 2,289 files. No command timed out or terminated by a signal. Seven repositories had identical normalized diagnostics; seven had differences. This covers the selected repositories, not all 83 catalog entries. +## Version 0.23 upgrade check (2026-11-09) + +The current integration pins `tsrs@0.23.0-ts7.1.0-beta`. The full ecosystem sweep +and performance measurements below remain results for `0.22.0`. + +On macOS ARM64, all seven reduced cases retained identical diagnostics and exit +codes between `0.22.0` and `0.23.0`, both through `vp lint` and the standalone +compiler. The standalone results also matched `typescript@7.1.0-beta`. +The TypeScript source pin remains `3e9236361fa97a9e6c685f4833ac457ae2d8c50a`. +The four existing compatibility follow-ups remain open. + +The [0.23 release](https://github.com/oxc-project/tsrs/blob/v0.23.0/docs/releases/0.23.0.md) +expands `--type-check` to whole programs. A smoke fixture confirmed that selecting +a clean file now reports `TS2322` in an ignored file from the same program. +Type-aware linting without `--type-check` still passes that fixture. A separate +invalid declaration remains suppressed by `skipLibCheck: true`. + +The rebuilt CLI passed 3,403 unit tests, with one skipped. However, the full +repository `vp check` now fails with seven diagnostics. With the same source and +dependencies, `0.22.0` and `oxlint-tsgolint@7.0.2005` pass the lint/type-check phase: + +| Dependency | New diagnostics | +| ----------------------------------------------------- | -------------------------------------------------------------- | +| `@voidzero-dev/vitepress-theme@5.0.7` | One `TS2614` for the `Sponsors` export from a Vue file | +| `zod@4.6.5` | One `TS5115` for recursive JSON schema types | +| `webpack-virtual-modules@0.6.2` and `unplugin@2.3.11` | Five `TS2307` errors for missing optional bundler declarations | + +Standalone `typescript@7.1.0-beta -p tsconfig.json --pretty false` passes the +repository root configuration. These new whole-program diagnostics need a separate +scope and compiler-options investigation; the earlier seven-case parity result +does not establish their correctness. + +This check used the rebased branch on `main` at `92edd550c`, Node.js `24.22.2`, +Oxlint `1.91.0`, and fresh CLI JavaScript. The native binding and core artifacts +remain from published `1.5.0`; the JS build used published Rolldown `1.2.16`. +This is not a full native release build or a repeat of the 14-repository sweep. +Scripts, fixtures, and raw logs remain outside the repository. + ## Exact-upstream attribution (2026-11-08) The original `6.0.3`, `7.0.2`, and `oxlint-tsgolint@7.0.2005` controls establish diff --git a/bench/tsrs-lint.md b/bench/tsrs-lint.md index c9ba28b082..e66a0aacba 100644 --- a/bench/tsrs-lint.md +++ b/bench/tsrs-lint.md @@ -1,5 +1,8 @@ # Type-aware linting with tsrs-lint +The current integration pins `tsrs@0.23.0-ts7.1.0-beta`. The measurements below +remain results for `0.22.0`; they have not been repeated for `0.23.0`. + This comparison uses the default `tsrs@0.22.0-ts7.1.0-beta` integration and an explicit `oxlint-tsgolint@7.0.2005` override. Both use Oxlint `1.91.0` and the same built CLI. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a9f83a7d97..24c4b52e6e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -345,8 +345,8 @@ catalogs: specifier: ^0.23.4 version: 0.23.4 tsrs: - specifier: '=0.22.0-ts7.1.0-beta' - version: 0.22.0-ts7.1.0-beta + specifier: '=0.23.0-ts7.1.0-beta' + version: 0.23.0-ts7.1.0-beta typescript: specifier: ~7.0.2 version: 7.0.2 @@ -471,7 +471,7 @@ importers: version: 1.62.1 tsrs: specifier: 'catalog:' - version: 0.22.0-ts7.1.0-beta + version: 0.23.0-ts7.1.0-beta vite: specifier: workspace:@voidzero-dev/vite-plus-core@* version: link:../core @@ -3582,48 +3582,48 @@ packages: peerDependencies: tsdown: 0.23.4 - '@tsrs/darwin-arm64@0.22.0-ts7.1.0-beta': - resolution: {integrity: sha512-mY0dMEtFxig4h5At3nByjZe8E4DTDPqqz73kkxQ6gqW5K9ym+u/VHQhtnpTF6ZOTkYyjP3bTaj5T8HcyoGVddA==} + '@tsrs/darwin-arm64@0.23.0-ts7.1.0-beta': + resolution: {integrity: sha512-Bk1aFZRo4d4RKtOU8OEVLXWZ7E0JqnL8xKhOb+FuvRRb9Qxu5UIgD0W3xiGvixzZ99I7f8Bj/NCTN30xX7MkEA==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [darwin] - '@tsrs/darwin-x64@0.22.0-ts7.1.0-beta': - resolution: {integrity: sha512-dvsSnyU8rpI3OqTIH2xxjt9ThvZC52BsL6/LS69eWg7Ieq2oni1PnLels5gGTFqvHFoRbezi5Y0KY7PQ1OmK5w==} + '@tsrs/darwin-x64@0.23.0-ts7.1.0-beta': + resolution: {integrity: sha512-asvC3hDKmRHjgfrr3o+bGVxhdPA5xfLCwcpfhIW4MCLcutBA8nltb4chtWRfEWsnKVimxtNvjnP0XldhgnJtkw==} engines: {node: '>=16.20.0'} cpu: [x64] os: [darwin] - '@tsrs/linux-arm64-gnu@0.22.0-ts7.1.0-beta': - resolution: {integrity: sha512-igg42LxSN/DHFqafvDqZW2JNBMH8WIwc1JE/IY4QUDOXNhZnUfDgEu9xo1QYwbgryeo5llmhRwM4CI/BQUWF1Q==} + '@tsrs/linux-arm64-gnu@0.23.0-ts7.1.0-beta': + resolution: {integrity: sha512-5Uhhvo7q5b5Xwv11uLCDcPBlzTvNcsDtog05eK5lsS8763BioGuKobYtmsBPQU6KyowzRv4GoocqSePd5yaEJw==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [linux] libc: [glibc] - '@tsrs/linux-arm64-musl@0.22.0-ts7.1.0-beta': - resolution: {integrity: sha512-f0zCqMkxPof7QVwjdgA8Fb0n6BtX5MOdILH/xfS+He6gRLCoECky0ejbsgLWCpB06GMlsL1gCbDr0/tmc91APg==} + '@tsrs/linux-arm64-musl@0.23.0-ts7.1.0-beta': + resolution: {integrity: sha512-v5vZCEec88vACtPHRPKie9JziE+X77VCNj/dNhj/buGAJwUZW2MMaQjJxKiSY4VwVo302kRfKKATyPljD23U6Q==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [linux] libc: [musl] - '@tsrs/linux-x64-gnu@0.22.0-ts7.1.0-beta': - resolution: {integrity: sha512-/e511w7xXmxBf14paz8eMLUbmlIc33Sy1E00y1FqmnUK04FTotWl+80mNvIVX5NrlOOJgrh+B125t1CHWu/8vQ==} + '@tsrs/linux-x64-gnu@0.23.0-ts7.1.0-beta': + resolution: {integrity: sha512-QJTJkk5XhkZSU/LBvjeFPpfRLDuICJbS9ij6q2rBy/Olw4T8WN7DGiE8ig687gI2dfnzlfScZsJYFtK2+CngnA==} engines: {node: '>=16.20.0'} cpu: [x64] os: [linux] libc: [glibc] - '@tsrs/linux-x64-musl@0.22.0-ts7.1.0-beta': - resolution: {integrity: sha512-B3aDOdS5+h77HcFep+KtOI+XmLm2G+FQPQC3KPSDjS/IG0tY9GPSc14mYnLwgO++dXluukO/oufk3J4Lo0CFjg==} + '@tsrs/linux-x64-musl@0.23.0-ts7.1.0-beta': + resolution: {integrity: sha512-cOW2i9W+ybOABvrmJpd8fI+Cbm5ej0unCSYBRyFAE0FLnQyuQT0+7vdNkrJn4QjaK62OS99vknW56/DfCcRrMA==} engines: {node: '>=16.20.0'} cpu: [x64] os: [linux] libc: [musl] - '@tsrs/win32-x64@0.22.0-ts7.1.0-beta': - resolution: {integrity: sha512-cZ69w+wOECbU6YF5UWi15TK73eX1pgoMEHKSmUF4K8PbS7DPIh/nWMsdb3rM8Ee1SUZ9ONCx+S6myh64SRi3Bg==} + '@tsrs/win32-x64@0.23.0-ts7.1.0-beta': + resolution: {integrity: sha512-Af9+bfGuTHtp41AOkWMmkQlWYPYvp3hvdw6dCRI6IlLdy4/5AqaJaFjDoCUTLJMojNjJjC4Ux/GhE2+CRoIomg==} engines: {node: '>=16.20.0'} cpu: [x64] os: [win32] @@ -6715,8 +6715,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsrs@0.22.0-ts7.1.0-beta: - resolution: {integrity: sha512-DEnEqQZRM3sHCV7MnQKVeTh61U2SX+0WksVlWp4eVayy0NffQ2oiCOjgH/Eye3sMPYkGwpM3tht3HtVI9NdURg==} + tsrs@0.23.0-ts7.1.0-beta: + resolution: {integrity: sha512-A7UU5bUA8caDkidkMfrfeF4etU92ODZxasCqIb1eaJ36+BAZgyZ6Znq7NVu/c1HFG57E6la/TeofpZhPdgmLBw==} engines: {node: '>=16.20.0'} hasBin: true @@ -9012,25 +9012,25 @@ snapshots: tsdown: 0.23.4(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.23.4)(@tsdown/exe@0.23.4)(@typescript/native-preview@7.0.0-dev.20260707.2)(@vitejs/devtools@0.8.2(crossws@0.4.17(srvx@1.0.6))(srvx@1.0.6)(vite@packages+core))(@volar/typescript@2.4.28)(@vue/language-core@3.3.12)(oxc-resolver@11.24.2)(publint@0.3.24)(tsx@4.23.15)(typescript@7.0.2)(unplugin-unused@0.5.6)(unrun@0.3.1)(vue-tsc@3.3.12(typescript@7.0.2)) verkit: 0.5.0 - '@tsrs/darwin-arm64@0.22.0-ts7.1.0-beta': + '@tsrs/darwin-arm64@0.23.0-ts7.1.0-beta': optional: true - '@tsrs/darwin-x64@0.22.0-ts7.1.0-beta': + '@tsrs/darwin-x64@0.23.0-ts7.1.0-beta': optional: true - '@tsrs/linux-arm64-gnu@0.22.0-ts7.1.0-beta': + '@tsrs/linux-arm64-gnu@0.23.0-ts7.1.0-beta': optional: true - '@tsrs/linux-arm64-musl@0.22.0-ts7.1.0-beta': + '@tsrs/linux-arm64-musl@0.23.0-ts7.1.0-beta': optional: true - '@tsrs/linux-x64-gnu@0.22.0-ts7.1.0-beta': + '@tsrs/linux-x64-gnu@0.23.0-ts7.1.0-beta': optional: true - '@tsrs/linux-x64-musl@0.22.0-ts7.1.0-beta': + '@tsrs/linux-x64-musl@0.23.0-ts7.1.0-beta': optional: true - '@tsrs/win32-x64@0.22.0-ts7.1.0-beta': + '@tsrs/win32-x64@0.23.0-ts7.1.0-beta': optional: true '@tybys/wasm-util@0.10.3': @@ -12203,15 +12203,15 @@ snapshots: tslib@2.8.1: {} - tsrs@0.22.0-ts7.1.0-beta: + tsrs@0.23.0-ts7.1.0-beta: optionalDependencies: - '@tsrs/darwin-arm64': 0.22.0-ts7.1.0-beta - '@tsrs/darwin-x64': 0.22.0-ts7.1.0-beta - '@tsrs/linux-arm64-gnu': 0.22.0-ts7.1.0-beta - '@tsrs/linux-arm64-musl': 0.22.0-ts7.1.0-beta - '@tsrs/linux-x64-gnu': 0.22.0-ts7.1.0-beta - '@tsrs/linux-x64-musl': 0.22.0-ts7.1.0-beta - '@tsrs/win32-x64': 0.22.0-ts7.1.0-beta + '@tsrs/darwin-arm64': 0.23.0-ts7.1.0-beta + '@tsrs/darwin-x64': 0.23.0-ts7.1.0-beta + '@tsrs/linux-arm64-gnu': 0.23.0-ts7.1.0-beta + '@tsrs/linux-arm64-musl': 0.23.0-ts7.1.0-beta + '@tsrs/linux-x64-gnu': 0.23.0-ts7.1.0-beta + '@tsrs/linux-x64-musl': 0.23.0-ts7.1.0-beta + '@tsrs/win32-x64': 0.23.0-ts7.1.0-beta tsx@4.23.15: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4919dabbe8..a7ef785123 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -67,7 +67,7 @@ catalog: source-map: 0.8.0 tinyglobby: ^0.2.15 tsdown: ^0.23.4 - tsrs: =0.22.0-ts7.1.0-beta + tsrs: =0.23.0-ts7.1.0-beta typescript: ~7.0.2 valibot: 1.5.0 validate-npm-package-name: ^8.0.0 diff --git a/rfcs/check-command.md b/rfcs/check-command.md index 90bb4f4c44..1ba9f35196 100644 --- a/rfcs/check-command.md +++ b/rfcs/check-command.md @@ -202,12 +202,19 @@ The check module reuses `SubcommandResolver` and `resolve_and_capture_output` fr ### Experimental type-aware backend -This draft replaces the bundled Go backend with `tsrs@0.22.0-ts7.1.0-beta`, +This draft replaces the bundled Go backend with `tsrs@0.23.0-ts7.1.0-beta`, a Rust port of the TypeScript 7 compiler. `resolve-lint.ts` selects the bundled `tsrs` package and passes its `bin/tsrs-lint` launcher to Oxlint through `OXLINT_TSGOLINT_PATH`. The same resolver serves `vp lint` and `vp check`. Users do not need to set an environment variable for the default backend. +With TypeScript diagnostics enabled, this release checks the whole program for +each tsconfig selected by the lint input files. Unselected or ignored program +files can therefore report type errors. Oxlint ignores still control lint rules, +and `skipLibCheck` controls declaration checking. +Selected files outside the discovered tsconfigs use an inferred program with +fallback compiler options. + An explicit `OXLINT_TSGOLINT_PATH` takes precedence over the resolver default. The environment variable keeps its existing name because Oxlint uses it to select the type-aware backend. diff --git a/rfcs/toolchain-command.md b/rfcs/toolchain-command.md index 5f94f55812..ab11711110 100644 --- a/rfcs/toolchain-command.md +++ b/rfcs/toolchain-command.md @@ -164,7 +164,7 @@ vite-plus@0.2.4 │ └── bundles tsdown@0.22.3 ├── depends on vitest@4.1.10 ├── depends on oxlint@1.72.0 -├── depends on tsrs@0.22.0-ts7.1.0-beta +├── depends on tsrs@0.23.0-ts7.1.0-beta ├── depends on oxfmt@0.57.0 └── compiles vite-task (built 2026-08-06T09:30:00Z, revision ) ``` From 4c263b9fce2274770b6d53b898bc3e65cc06e0ba Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 9 Nov 2026 14:40:23 +0800 Subject: [PATCH 14/25] fix(check): align nested projects with whole-program checks --- bench/tsrs-lint-ecosystem.md | 29 ++++++++++++++----- docs/.vitepress/env.d.ts | 11 +++++++ .../cli/templates/generator/tsconfig.json | 1 + packages/cli/templates/monorepo/tsconfig.json | 3 +- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/bench/tsrs-lint-ecosystem.md b/bench/tsrs-lint-ecosystem.md index 1c54263050..fbef47fe23 100644 --- a/bench/tsrs-lint-ecosystem.md +++ b/bench/tsrs-lint-ecosystem.md @@ -16,7 +16,7 @@ seven had differences. This covers the selected repositories, not all 83 catalog ## Version 0.23 upgrade check (2026-11-09) The current integration pins `tsrs@0.23.0-ts7.1.0-beta`. The full ecosystem sweep -and performance measurements below remain results for `0.22.0`. +and performance measurements remain results for `0.22.0`. On macOS ARM64, all seven reduced cases retained identical diagnostics and exit codes between `0.22.0` and `0.23.0`, both through `vp lint` and the standalone @@ -30,9 +30,9 @@ a clean file now reports `TS2322` in an ignored file from the same program. Type-aware linting without `--type-check` still passes that fixture. A separate invalid declaration remains suppressed by `skipLibCheck: true`. -The rebuilt CLI passed 3,403 unit tests, with one skipped. However, the full -repository `vp check` now fails with seven diagnostics. With the same source and -dependencies, `0.22.0` and `oxlint-tsgolint@7.0.2005` pass the lint/type-check phase: +Whole-program checking initially exposed seven errors in three nested project +configs. With those original configs, `0.22.0` and `oxlint-tsgolint@7.0.2005` pass +the lint/type-check phase because they do not report these dependency errors: | Dependency | New diagnostics | | ----------------------------------------------------- | -------------------------------------------------------------- | @@ -40,10 +40,23 @@ dependencies, `0.22.0` and `oxlint-tsgolint@7.0.2005` pass the lint/type-check p | `zod@4.6.5` | One `TS5115` for recursive JSON schema types | | `webpack-virtual-modules@0.6.2` and `unplugin@2.3.11` | Five `TS2307` errors for missing optional bundler declarations | -Standalone `typescript@7.1.0-beta -p tsconfig.json --pretty false` passes the -repository root configuration. These new whole-program diagnostics need a separate -scope and compiler-options investigation; the earlier seven-case parity result -does not establish their correctness. +Standalone `typescript@7.1.0-beta` reproduces all seven errors when invoked against +the same nested configs: `packages/cli/templates/generator/tsconfig.json`, +`packages/cli/templates/monorepo/tsconfig.json`, and `docs/.vitepress/tsconfig.json`. +The root-only compiler check uses different file selection and compiler options; +its success did not establish a tsrs defect. + +Both starter templates now set `skipLibCheck: true`, consistent with the repository +root, so they do not check third-party declarations. The docs shim now declares +the named `Sponsors` type exported by `VPSponsors.vue`. It reuses the theme's +`SponsorTier` fields with an optional `tier`, matching the component's interface. + +After these corrections, `vp check` and all three nested TypeScript checks pass. +Negative probes still reject source type errors and invalid sponsor fields, and +unrelated Vue components do not acquire the named export. The rebuilt CLI's +3,403 unit tests also pass, with one skipped. These seven errors required project +configuration and Vue declaration fixes; the four earlier compiler-version +compatibility follow-ups remain open. This check used the rebased branch on `main` at `92edd550c`, Node.js `24.22.2`, Oxlint `1.91.0`, and fresh CLI JavaScript. The native binding and core artifacts diff --git a/docs/.vitepress/env.d.ts b/docs/.vitepress/env.d.ts index 23ce8f7581..514d964043 100644 --- a/docs/.vitepress/env.d.ts +++ b/docs/.vitepress/env.d.ts @@ -8,6 +8,17 @@ declare const __DOCS_INSTALL_SH_URL__: string; // oxlint-disable-next-line no-underscore-dangle declare const __DOCS_INSTALL_PS1_URL__: string; +// The theme re-exports this named type from an SFC, which plain TypeScript cannot read. +// Keep this specific shim before the generic Vue module declaration. +declare module '*VPSponsors.vue' { + import type { SponsorTier } from '@voidzero-dev/vitepress-theme/src/types/theme-config'; + import type { DefineComponent } from 'vue'; + + export type Sponsors = Omit & { tier?: string }; + const component: DefineComponent<{}, {}, unknown>; + export default component; +} + // Vue SFC module declaration declare module '*.vue' { import type { DefineComponent } from 'vue'; diff --git a/packages/cli/templates/generator/tsconfig.json b/packages/cli/templates/generator/tsconfig.json index f7d37cbfe8..65f1bd5511 100644 --- a/packages/cli/templates/generator/tsconfig.json +++ b/packages/cli/templates/generator/tsconfig.json @@ -5,6 +5,7 @@ "moduleResolution": "nodenext", "noEmit": true, "resolveJsonModule": true, + "skipLibCheck": true, "types": ["node"] } } diff --git a/packages/cli/templates/monorepo/tsconfig.json b/packages/cli/templates/monorepo/tsconfig.json index c785f304a8..94031829b7 100644 --- a/packages/cli/templates/monorepo/tsconfig.json +++ b/packages/cli/templates/monorepo/tsconfig.json @@ -4,6 +4,7 @@ "module": "nodenext", "moduleResolution": "nodenext", "allowImportingTsExtensions": true, - "esModuleInterop": true + "esModuleInterop": true, + "skipLibCheck": true } } From 0a695c6a2655c480558b3cf73e62aa6bf9d581fa Mon Sep 17 00:00:00 2001 From: MK Date: Wed, 11 Nov 2026 11:16:35 +0800 Subject: [PATCH 15/25] docs(rfc): describe the tsrs-lint backend replacement --- rfcs/check-command.md | 3 ++ rfcs/tsrs-lint-backend.md | 64 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 rfcs/tsrs-lint-backend.md diff --git a/rfcs/check-command.md b/rfcs/check-command.md index 1ba9f35196..34b84b4072 100644 --- a/rfcs/check-command.md +++ b/rfcs/check-command.md @@ -202,6 +202,9 @@ The check module reuses `SubcommandResolver` and `resolve_and_capture_output` fr ### Experimental type-aware backend +See the [backend replacement RFC](./tsrs-lint-backend.md) for the rationale, +compatibility considerations, and release conditions. + This draft replaces the bundled Go backend with `tsrs@0.23.0-ts7.1.0-beta`, a Rust port of the TypeScript 7 compiler. `resolve-lint.ts` selects the bundled `tsrs` package and passes its `bin/tsrs-lint` launcher to Oxlint through diff --git a/rfcs/tsrs-lint-backend.md b/rfcs/tsrs-lint-backend.md new file mode 100644 index 0000000000..262fe55ff5 --- /dev/null +++ b/rfcs/tsrs-lint-backend.md @@ -0,0 +1,64 @@ +# RFC: Replace tsgolint with tsrs-lint + +- Status: Proposed; experimental default in this draft +- Related: [check command](./check-command.md), [toolchain metadata](./toolchain-command.md) + +## Motivation + +Use `tsrs-lint`, the Rust implementation of the type-aware backend, to reduce +lint time and peak memory. The [Codespaces comparison](../bench/tsrs-lint.md) +shows improvements with `tsrs@0.22.0-ts7.1.0-beta`, at the cost of a larger +installed dependency tree. Those measurements do not describe the current +`0.23.0` integration and must be repeated before release. + +## Design + +Replace the production dependency on `oxlint-tsgolint` with an exact `tsrs` pin, +currently `0.23.0-ts7.1.0-beta`. Keep the Go package as a development dependency +for comparisons. Oxlint remains the linter frontend and delegates type-aware +rules and optional TypeScript diagnostics to the bundled `tsrs-lint` executable. + +The local `vite-plus` package owns this integration. Its +[`resolve-lint.ts`](../packages/cli/src/resolve-lint.ts) resolves the bundled +launcher and supplies it to Oxlint through the existing internal +`OXLINT_TSGOLINT_PATH` interface. Both `vp lint` and `vp check` use this resolver; +the global CLI is not required. Users keep the existing commands and +`lint.options.typeAware` / `lint.options.typeCheck` configuration. + +Version output, `vite-plus/versions`, and toolchain metadata identify `tsrs`. +The global version command remains compatible with older Go-backed installations. +Ordinary project tasks that invoke `tsc` keep their selected compiler. + +## Compatibility + +The pinned release targets TypeScript `7.1.0-beta`. Changing the backend also +changes the compiler version, so identical diagnostics are not guaranteed. +The [ecosystem report](../bench/tsrs-lint-ecosystem.md) records the four known +compatibility categories and their upstream attribution. Matching the pinned +TypeScript compiler does not make every new diagnostic desirable. + +With type checking enabled, `0.23.0` checks whole programs for the selected +tsconfigs. Unselected and ignored program files can report type errors; lint +ignore patterns still control lint rules. Files outside discovered configs use +an inferred program. This broader scope can increase the work per invocation. + +The backend respects each project's compiler options. `skipLibCheck` controls +declaration checking; it is not a requirement for using tsrs. This draft aligns +the two starter templates with the repository's existing setting and supplies +the docs theme's missing Vue type declaration. Existing projects need their own +configuration review. + +## Release conditions + +- Repeat elapsed-time, memory, and installed-size comparisons with the release + candidate. Keep historical measurements labeled with their original versions. +- Validate lint rules, fixes, suppression comments, and optional type checking + on representative ecosystem projects. Track compiler-version changes separately + from Rust implementation defects. +- Validate all supported runtime targets. The pinned release has no Windows + ARM64 binary, and this draft implements no automatic Go fallback. Resolve that + platform gap before a release that supports all Vite+ targets. + +If the candidate fails these conditions, retain the Go backend for the release +or revert the bundled-backend change. Backend selection remains a toolchain +decision rather than an additional user setup step. From c5cbe08767f27a36513392a0b61d44acbf8902c0 Mon Sep 17 00:00:00 2001 From: MK Date: Wed, 11 Nov 2026 11:27:52 +0800 Subject: [PATCH 16/25] chore(deps): update tsrs to 0.24.0-ts7.1.0-beta --- bench/tsrs-lint-ecosystem.md | 25 ++++++++++++- bench/tsrs-lint.md | 4 +-- pnpm-lock.yaml | 70 ++++++++++++++++++------------------ pnpm-workspace.yaml | 2 +- rfcs/check-command.md | 2 +- rfcs/toolchain-command.md | 2 +- rfcs/tsrs-lint-backend.md | 13 +++---- 7 files changed, 71 insertions(+), 47 deletions(-) diff --git a/bench/tsrs-lint-ecosystem.md b/bench/tsrs-lint-ecosystem.md index fbef47fe23..2d4cf11583 100644 --- a/bench/tsrs-lint-ecosystem.md +++ b/bench/tsrs-lint-ecosystem.md @@ -13,9 +13,32 @@ The explicit type-check workload covered 2,289 files. No command timed out or terminated by a signal. Seven repositories had identical normalized diagnostics; seven had differences. This covers the selected repositories, not all 83 catalog entries. +## Version 0.24 upgrade check (2026-11-11) + +The current integration pins `tsrs@0.24.0-ts7.1.0-beta`. The full ecosystem sweep +and performance measurements remain results for `0.22.0`. + +On macOS ARM64, all seven reduced cases retained identical diagnostics and exit +codes between `0.23.0` and `0.24.0`, both through `vp lint` and the standalone +compiler. The standalone results also matched `typescript@7.1.0-beta`. +The TypeScript source pin is unchanged, and the four compatibility follow-ups +remain open. + +The [0.24 release](https://github.com/oxc-project/tsrs/blob/v0.24.0/docs/releases/0.24.0.md) +checks whole programs before running lint rules. The existing smoke fixture still +reports the ignored program file's `TS2322` only when type checking is enabled. +Its invalid declaration remains suppressed by `skipLibCheck: true`. + +After rebasing onto `main` at `9f44ce760`, `vp check` passes, and the unit suite +passes 3,422 tests with one skipped. This check used Node.js `24.22.2`, Oxlint +`1.93.0`, and fresh CLI JavaScript built with published Rolldown `1.2.18`. +The native binding and core artifacts remain from published `1.5.0`. +This is not a full native release build or a repeat of the 14-repository sweep. +Scripts, fixtures, and raw logs remain outside the repository. + ## Version 0.23 upgrade check (2026-11-09) -The current integration pins `tsrs@0.23.0-ts7.1.0-beta`. The full ecosystem sweep +This upgrade pinned `tsrs@0.23.0-ts7.1.0-beta`. The full ecosystem sweep and performance measurements remain results for `0.22.0`. On macOS ARM64, all seven reduced cases retained identical diagnostics and exit diff --git a/bench/tsrs-lint.md b/bench/tsrs-lint.md index e66a0aacba..7af495fc69 100644 --- a/bench/tsrs-lint.md +++ b/bench/tsrs-lint.md @@ -1,7 +1,7 @@ # Type-aware linting with tsrs-lint -The current integration pins `tsrs@0.23.0-ts7.1.0-beta`. The measurements below -remain results for `0.22.0`; they have not been repeated for `0.23.0`. +The current integration pins `tsrs@0.24.0-ts7.1.0-beta`. The measurements below +remain results for `0.22.0`; they have not been repeated for `0.24.0`. This comparison uses the default `tsrs@0.22.0-ts7.1.0-beta` integration and an explicit `oxlint-tsgolint@7.0.2005` override. Both use Oxlint `1.91.0` and the same built CLI. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24c4b52e6e..e0a5c4eac4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -345,8 +345,8 @@ catalogs: specifier: ^0.23.4 version: 0.23.4 tsrs: - specifier: '=0.23.0-ts7.1.0-beta' - version: 0.23.0-ts7.1.0-beta + specifier: '=0.24.0-ts7.1.0-beta' + version: 0.24.0-ts7.1.0-beta typescript: specifier: ~7.0.2 version: 7.0.2 @@ -471,7 +471,7 @@ importers: version: 1.62.1 tsrs: specifier: 'catalog:' - version: 0.23.0-ts7.1.0-beta + version: 0.24.0-ts7.1.0-beta vite: specifier: workspace:@voidzero-dev/vite-plus-core@* version: link:../core @@ -538,7 +538,7 @@ importers: version: 0.3.0 oxlint-tsgolint: specifier: 'catalog:' - version: 7.0.2005 + version: 7.0.2006 picocolors: specifier: 'catalog:' version: 1.1.1 @@ -3582,48 +3582,48 @@ packages: peerDependencies: tsdown: 0.23.4 - '@tsrs/darwin-arm64@0.23.0-ts7.1.0-beta': - resolution: {integrity: sha512-Bk1aFZRo4d4RKtOU8OEVLXWZ7E0JqnL8xKhOb+FuvRRb9Qxu5UIgD0W3xiGvixzZ99I7f8Bj/NCTN30xX7MkEA==} + '@tsrs/darwin-arm64@0.24.0-ts7.1.0-beta': + resolution: {integrity: sha512-GxGPRieCGIXevJgGlbne9pRqkfAN+VmBQvBHWWcF8rXLNX9yCxYo78/a7Auqjf723b4eFTlM5mgkclUit/Fu9w==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [darwin] - '@tsrs/darwin-x64@0.23.0-ts7.1.0-beta': - resolution: {integrity: sha512-asvC3hDKmRHjgfrr3o+bGVxhdPA5xfLCwcpfhIW4MCLcutBA8nltb4chtWRfEWsnKVimxtNvjnP0XldhgnJtkw==} + '@tsrs/darwin-x64@0.24.0-ts7.1.0-beta': + resolution: {integrity: sha512-TrXdOyEq++blyYp6KphoHGmsGxAn1flYzjm0peLEI3c88z1UQa+pKkcuPgZ6FOxTW6fSaT9572n21kz+0bWi/g==} engines: {node: '>=16.20.0'} cpu: [x64] os: [darwin] - '@tsrs/linux-arm64-gnu@0.23.0-ts7.1.0-beta': - resolution: {integrity: sha512-5Uhhvo7q5b5Xwv11uLCDcPBlzTvNcsDtog05eK5lsS8763BioGuKobYtmsBPQU6KyowzRv4GoocqSePd5yaEJw==} + '@tsrs/linux-arm64-gnu@0.24.0-ts7.1.0-beta': + resolution: {integrity: sha512-Ph5NK0vnaR4gnBW2LgrZsUPx4mKF+9tCQUYZFOHmny6SMojSsm/5GCiBu0sHZEZhmyIqq8UbineUEA7T0QImnw==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [linux] libc: [glibc] - '@tsrs/linux-arm64-musl@0.23.0-ts7.1.0-beta': - resolution: {integrity: sha512-v5vZCEec88vACtPHRPKie9JziE+X77VCNj/dNhj/buGAJwUZW2MMaQjJxKiSY4VwVo302kRfKKATyPljD23U6Q==} + '@tsrs/linux-arm64-musl@0.24.0-ts7.1.0-beta': + resolution: {integrity: sha512-vjgx8wGuAVSOPLlb2eVQpQVolImZa7vsH/1kvYOaokcP/KZXVUmHzgroxK0t9gxJ0SXvT4VKNjWZ+t+4tDNu1A==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [linux] libc: [musl] - '@tsrs/linux-x64-gnu@0.23.0-ts7.1.0-beta': - resolution: {integrity: sha512-QJTJkk5XhkZSU/LBvjeFPpfRLDuICJbS9ij6q2rBy/Olw4T8WN7DGiE8ig687gI2dfnzlfScZsJYFtK2+CngnA==} + '@tsrs/linux-x64-gnu@0.24.0-ts7.1.0-beta': + resolution: {integrity: sha512-tj1SNWBnJoBUzU2zrD18tUu3h6o36FXQGPpmIXsADHSfU5H9pVQdK/QzPIO76rb72P7foXHluE+xGTXXf8nNaA==} engines: {node: '>=16.20.0'} cpu: [x64] os: [linux] libc: [glibc] - '@tsrs/linux-x64-musl@0.23.0-ts7.1.0-beta': - resolution: {integrity: sha512-cOW2i9W+ybOABvrmJpd8fI+Cbm5ej0unCSYBRyFAE0FLnQyuQT0+7vdNkrJn4QjaK62OS99vknW56/DfCcRrMA==} + '@tsrs/linux-x64-musl@0.24.0-ts7.1.0-beta': + resolution: {integrity: sha512-sizu0EdGw9ITqEaZKg60TDhDk5PXT9RzVkCdesBjk/BZhS0BVU2cRhvoRBmDbk+2vCUgW14j+Otg3tl+002qNQ==} engines: {node: '>=16.20.0'} cpu: [x64] os: [linux] libc: [musl] - '@tsrs/win32-x64@0.23.0-ts7.1.0-beta': - resolution: {integrity: sha512-Af9+bfGuTHtp41AOkWMmkQlWYPYvp3hvdw6dCRI6IlLdy4/5AqaJaFjDoCUTLJMojNjJjC4Ux/GhE2+CRoIomg==} + '@tsrs/win32-x64@0.24.0-ts7.1.0-beta': + resolution: {integrity: sha512-WAbzgd5JBOauQ/oL6DTdGEh09tM/q8tlGAcRORskhPK7eIQVVa8Js2pc7DVW0XokWTSADeD9v6HKtU2Ge0r/fQ==} engines: {node: '>=16.20.0'} cpu: [x64] os: [win32] @@ -6715,8 +6715,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsrs@0.23.0-ts7.1.0-beta: - resolution: {integrity: sha512-A7UU5bUA8caDkidkMfrfeF4etU92ODZxasCqIb1eaJ36+BAZgyZ6Znq7NVu/c1HFG57E6la/TeofpZhPdgmLBw==} + tsrs@0.24.0-ts7.1.0-beta: + resolution: {integrity: sha512-Iy7q93qfRHcI0Y31IKx6fCsDQCsSjtrT01PIgOVolBXPJTMdm84oH1pEYyAMPQ/SnRqWDSWI8wEUHbv6e9TmHA==} engines: {node: '>=16.20.0'} hasBin: true @@ -9012,25 +9012,25 @@ snapshots: tsdown: 0.23.4(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.23.4)(@tsdown/exe@0.23.4)(@typescript/native-preview@7.0.0-dev.20260707.2)(@vitejs/devtools@0.8.2(crossws@0.4.17(srvx@1.0.6))(srvx@1.0.6)(vite@packages+core))(@volar/typescript@2.4.28)(@vue/language-core@3.3.12)(oxc-resolver@11.24.2)(publint@0.3.24)(tsx@4.23.15)(typescript@7.0.2)(unplugin-unused@0.5.6)(unrun@0.3.1)(vue-tsc@3.3.12(typescript@7.0.2)) verkit: 0.5.0 - '@tsrs/darwin-arm64@0.23.0-ts7.1.0-beta': + '@tsrs/darwin-arm64@0.24.0-ts7.1.0-beta': optional: true - '@tsrs/darwin-x64@0.23.0-ts7.1.0-beta': + '@tsrs/darwin-x64@0.24.0-ts7.1.0-beta': optional: true - '@tsrs/linux-arm64-gnu@0.23.0-ts7.1.0-beta': + '@tsrs/linux-arm64-gnu@0.24.0-ts7.1.0-beta': optional: true - '@tsrs/linux-arm64-musl@0.23.0-ts7.1.0-beta': + '@tsrs/linux-arm64-musl@0.24.0-ts7.1.0-beta': optional: true - '@tsrs/linux-x64-gnu@0.23.0-ts7.1.0-beta': + '@tsrs/linux-x64-gnu@0.24.0-ts7.1.0-beta': optional: true - '@tsrs/linux-x64-musl@0.23.0-ts7.1.0-beta': + '@tsrs/linux-x64-musl@0.24.0-ts7.1.0-beta': optional: true - '@tsrs/win32-x64@0.23.0-ts7.1.0-beta': + '@tsrs/win32-x64@0.24.0-ts7.1.0-beta': optional: true '@tybys/wasm-util@0.10.3': @@ -12203,15 +12203,15 @@ snapshots: tslib@2.8.1: {} - tsrs@0.23.0-ts7.1.0-beta: + tsrs@0.24.0-ts7.1.0-beta: optionalDependencies: - '@tsrs/darwin-arm64': 0.23.0-ts7.1.0-beta - '@tsrs/darwin-x64': 0.23.0-ts7.1.0-beta - '@tsrs/linux-arm64-gnu': 0.23.0-ts7.1.0-beta - '@tsrs/linux-arm64-musl': 0.23.0-ts7.1.0-beta - '@tsrs/linux-x64-gnu': 0.23.0-ts7.1.0-beta - '@tsrs/linux-x64-musl': 0.23.0-ts7.1.0-beta - '@tsrs/win32-x64': 0.23.0-ts7.1.0-beta + '@tsrs/darwin-arm64': 0.24.0-ts7.1.0-beta + '@tsrs/darwin-x64': 0.24.0-ts7.1.0-beta + '@tsrs/linux-arm64-gnu': 0.24.0-ts7.1.0-beta + '@tsrs/linux-arm64-musl': 0.24.0-ts7.1.0-beta + '@tsrs/linux-x64-gnu': 0.24.0-ts7.1.0-beta + '@tsrs/linux-x64-musl': 0.24.0-ts7.1.0-beta + '@tsrs/win32-x64': 0.24.0-ts7.1.0-beta tsx@4.23.15: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a7ef785123..09119009df 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -67,7 +67,7 @@ catalog: source-map: 0.8.0 tinyglobby: ^0.2.15 tsdown: ^0.23.4 - tsrs: =0.23.0-ts7.1.0-beta + tsrs: =0.24.0-ts7.1.0-beta typescript: ~7.0.2 valibot: 1.5.0 validate-npm-package-name: ^8.0.0 diff --git a/rfcs/check-command.md b/rfcs/check-command.md index 34b84b4072..555ecd37a4 100644 --- a/rfcs/check-command.md +++ b/rfcs/check-command.md @@ -205,7 +205,7 @@ The check module reuses `SubcommandResolver` and `resolve_and_capture_output` fr See the [backend replacement RFC](./tsrs-lint-backend.md) for the rationale, compatibility considerations, and release conditions. -This draft replaces the bundled Go backend with `tsrs@0.23.0-ts7.1.0-beta`, +This draft replaces the bundled Go backend with `tsrs@0.24.0-ts7.1.0-beta`, a Rust port of the TypeScript 7 compiler. `resolve-lint.ts` selects the bundled `tsrs` package and passes its `bin/tsrs-lint` launcher to Oxlint through `OXLINT_TSGOLINT_PATH`. The same resolver serves `vp lint` and `vp check`. diff --git a/rfcs/toolchain-command.md b/rfcs/toolchain-command.md index ab11711110..1df6ae6d7c 100644 --- a/rfcs/toolchain-command.md +++ b/rfcs/toolchain-command.md @@ -164,7 +164,7 @@ vite-plus@0.2.4 │ └── bundles tsdown@0.22.3 ├── depends on vitest@4.1.10 ├── depends on oxlint@1.72.0 -├── depends on tsrs@0.23.0-ts7.1.0-beta +├── depends on tsrs@0.24.0-ts7.1.0-beta ├── depends on oxfmt@0.57.0 └── compiles vite-task (built 2026-08-06T09:30:00Z, revision ) ``` diff --git a/rfcs/tsrs-lint-backend.md b/rfcs/tsrs-lint-backend.md index 262fe55ff5..038be21934 100644 --- a/rfcs/tsrs-lint-backend.md +++ b/rfcs/tsrs-lint-backend.md @@ -9,12 +9,12 @@ Use `tsrs-lint`, the Rust implementation of the type-aware backend, to reduce lint time and peak memory. The [Codespaces comparison](../bench/tsrs-lint.md) shows improvements with `tsrs@0.22.0-ts7.1.0-beta`, at the cost of a larger installed dependency tree. Those measurements do not describe the current -`0.23.0` integration and must be repeated before release. +`0.24.0` integration and must be repeated before release. ## Design Replace the production dependency on `oxlint-tsgolint` with an exact `tsrs` pin, -currently `0.23.0-ts7.1.0-beta`. Keep the Go package as a development dependency +currently `0.24.0-ts7.1.0-beta`. Keep the Go package as a development dependency for comparisons. Oxlint remains the linter frontend and delegates type-aware rules and optional TypeScript diagnostics to the bundled `tsrs-lint` executable. @@ -37,10 +37,11 @@ The [ecosystem report](../bench/tsrs-lint-ecosystem.md) records the four known compatibility categories and their upstream attribution. Matching the pinned TypeScript compiler does not make every new diagnostic desirable. -With type checking enabled, `0.23.0` checks whole programs for the selected -tsconfigs. Unselected and ignored program files can report type errors; lint -ignore patterns still control lint rules. Files outside discovered configs use -an inferred program. This broader scope can increase the work per invocation. +With type checking enabled, `0.24.0` checks whole programs for the selected +tsconfigs before running lint rules. Unselected and ignored program files can +report type errors; lint ignore patterns still control lint rules. Files outside +discovered configs use an inferred program. This broader scope can increase the +work per invocation. The backend respects each project's compiler options. `skipLibCheck` controls declaration checking; it is not a requirement for using tsrs. This draft aligns From 8b1cd9c8690f5baf3868049d53077449f6bda113 Mon Sep 17 00:00:00 2001 From: MK Date: Wed, 11 Nov 2026 11:30:52 +0800 Subject: [PATCH 17/25] docs(rfc): compare lint backend dependencies --- rfcs/tsrs-lint-backend.md | 42 +++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/rfcs/tsrs-lint-backend.md b/rfcs/tsrs-lint-backend.md index 038be21934..13eea48cc2 100644 --- a/rfcs/tsrs-lint-backend.md +++ b/rfcs/tsrs-lint-backend.md @@ -3,14 +3,6 @@ - Status: Proposed; experimental default in this draft - Related: [check command](./check-command.md), [toolchain metadata](./toolchain-command.md) -## Motivation - -Use `tsrs-lint`, the Rust implementation of the type-aware backend, to reduce -lint time and peak memory. The [Codespaces comparison](../bench/tsrs-lint.md) -shows improvements with `tsrs@0.22.0-ts7.1.0-beta`, at the cost of a larger -installed dependency tree. Those measurements do not describe the current -`0.24.0` integration and must be repeated before release. - ## Design Replace the production dependency on `oxlint-tsgolint` with an exact `tsrs` pin, @@ -29,6 +21,40 @@ Version output, `vite-plus/versions`, and toolchain metadata identify `tsrs`. The global version command remains compatible with older Go-backed installations. Ordinary project tasks that invoke `tsc` keep their selected compiler. +## Dependency changes + +Compare the package declarations on `main` at `9f44ce760` with this draft: + +| Dependency | Before | After | +| ------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------- | +| Linter frontend | `oxlint@1.93.0` | Unchanged | +| Production backend | `oxlint-tsgolint@7.0.2006` | `tsrs@0.24.0-ts7.1.0-beta` | +| Development-only backend | None | `oxlint-tsgolint@7.0.2006` for comparisons | +| Backend platform packages | `@oxlint-tsgolint/*`: six optional packages | `@tsrs/*`: seven optional packages | +| Backend package contents | Launcher and Go executable | Launchers, Rust executable, TypeScript library declarations, and JavaScript APIs | + +Both backends use platform packages for their native executables. tsrs has +separate Linux glibc and musl packages, but no Windows ARM64 package. A normal +production installation selects the matching platform package. Repository +development installs retain both backends for comparisons. + +Oxlint declares `oxlint-tsgolint` as an optional peer dependency; it does not +require the Go backend in production. The existing project `typescript` +dependency is unchanged. tsrs supplies its own compiler and library declarations +for type-aware linting and optional type checking. + +The [existing disk comparison](../bench/tsrs-lint.md#installed-disk-footprint) +measured these Linux x64/glibc production installations: + +| On-disk scope | Vite+ `1.5.0`, `oxlint-tsgolint@7.0.2005` | Preview, `tsrs@0.22.0-ts7.1.0-beta` | Increase | +| ----------------------- | ----------------------------------------- | ----------------------------------- | ----------------- | +| Complete `node_modules` | 132.93 MiB | 150.87 MiB | 17.94 MiB (13.5%) | +| Backend packages | 21.63 MiB | 39.69 MiB | 18.06 MiB (83.5%) | + +The complete installation includes other release-to-preview dependency changes; +the backend row isolates the backend packages. These are historical measurements, +not sizes for the current `0.24.0` integration. Repeat them before release. + ## Compatibility The pinned release targets TypeScript `7.1.0-beta`. Changing the backend also From d9f60885a1fa5e5df8a4b8e58096ae1e28120158 Mon Sep 17 00:00:00 2001 From: MK Date: Wed, 11 Nov 2026 13:34:28 +0800 Subject: [PATCH 18/25] docs(rfc): explain the default tsrs user experience --- rfcs/tsrs-lint-backend.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rfcs/tsrs-lint-backend.md b/rfcs/tsrs-lint-backend.md index 13eea48cc2..c3e829ab41 100644 --- a/rfcs/tsrs-lint-backend.md +++ b/rfcs/tsrs-lint-backend.md @@ -3,6 +3,16 @@ - Status: Proposed; experimental default in this draft - Related: [check command](./check-command.md), [toolchain metadata](./toolchain-command.md) +## User experience + +Once this change ships, users get tsrs by upgrading `vite-plus`. Existing +`vp lint` and `vp check` commands automatically use the bundled backend for +type-aware linting and type checking. Projects that enable these checks can +benefit from tsrs performance improvements without additional setup. + +Users keep their existing commands and configuration. No separate installation +of `tsrs` is required. + ## Design Replace the production dependency on `oxlint-tsgolint` with an exact `tsrs` pin, From e33c377b5456a207f65c7dc283d72e5c3abf46df Mon Sep 17 00:00:00 2001 From: MK Date: Wed, 11 Nov 2026 14:51:50 +0800 Subject: [PATCH 19/25] docs(rfc): simplify the tsrs proposal --- rfcs/tsrs-lint-backend.md | 113 +++++++++----------------------------- 1 file changed, 27 insertions(+), 86 deletions(-) diff --git a/rfcs/tsrs-lint-backend.md b/rfcs/tsrs-lint-backend.md index c3e829ab41..40ec37467f 100644 --- a/rfcs/tsrs-lint-backend.md +++ b/rfcs/tsrs-lint-backend.md @@ -1,101 +1,42 @@ # RFC: Replace tsgolint with tsrs-lint -- Status: Proposed; experimental default in this draft -- Related: [check command](./check-command.md), [toolchain metadata](./toolchain-command.md) +Status: Proposed; experimental default in this draft. ## User experience -Once this change ships, users get tsrs by upgrading `vite-plus`. Existing -`vp lint` and `vp check` commands automatically use the bundled backend for -type-aware linting and type checking. Projects that enable these checks can -benefit from tsrs performance improvements without additional setup. - -Users keep their existing commands and configuration. No separate installation -of `tsrs` is required. +Upgrading `vite-plus` makes tsrs the default backend for type-aware linting and +type checking in `vp lint` and `vp check`. Projects with these checks enabled +can benefit from tsrs performance improvements without additional setup. +Users keep their existing commands and configuration; no separate `tsrs` +installation is required. ## Design -Replace the production dependency on `oxlint-tsgolint` with an exact `tsrs` pin, -currently `0.24.0-ts7.1.0-beta`. Keep the Go package as a development dependency -for comparisons. Oxlint remains the linter frontend and delegates type-aware -rules and optional TypeScript diagnostics to the bundled `tsrs-lint` executable. - -The local `vite-plus` package owns this integration. Its -[`resolve-lint.ts`](../packages/cli/src/resolve-lint.ts) resolves the bundled -launcher and supplies it to Oxlint through the existing internal -`OXLINT_TSGOLINT_PATH` interface. Both `vp lint` and `vp check` use this resolver; -the global CLI is not required. Users keep the existing commands and -`lint.options.typeAware` / `lint.options.typeCheck` configuration. - -Version output, `vite-plus/versions`, and toolchain metadata identify `tsrs`. -The global version command remains compatible with older Go-backed installations. -Ordinary project tasks that invoke `tsc` keep their selected compiler. +Oxlint remains the linter frontend. The local `vite-plus` package bundles +`tsrs-lint` and selects it through the shared +[`resolve-lint.ts`](../packages/cli/src/resolve-lint.ts) resolver. Version output +and toolchain metadata identify `tsrs`. ## Dependency changes -Compare the package declarations on `main` at `9f44ce760` with this draft: - -| Dependency | Before | After | -| ------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------- | -| Linter frontend | `oxlint@1.93.0` | Unchanged | -| Production backend | `oxlint-tsgolint@7.0.2006` | `tsrs@0.24.0-ts7.1.0-beta` | -| Development-only backend | None | `oxlint-tsgolint@7.0.2006` for comparisons | -| Backend platform packages | `@oxlint-tsgolint/*`: six optional packages | `@tsrs/*`: seven optional packages | -| Backend package contents | Launcher and Go executable | Launchers, Rust executable, TypeScript library declarations, and JavaScript APIs | - -Both backends use platform packages for their native executables. tsrs has -separate Linux glibc and musl packages, but no Windows ARM64 package. A normal -production installation selects the matching platform package. Repository -development installs retain both backends for comparisons. - -Oxlint declares `oxlint-tsgolint` as an optional peer dependency; it does not -require the Go backend in production. The existing project `typescript` -dependency is unchanged. tsrs supplies its own compiler and library declarations -for type-aware linting and optional type checking. - -The [existing disk comparison](../bench/tsrs-lint.md#installed-disk-footprint) -measured these Linux x64/glibc production installations: - -| On-disk scope | Vite+ `1.5.0`, `oxlint-tsgolint@7.0.2005` | Preview, `tsrs@0.22.0-ts7.1.0-beta` | Increase | -| ----------------------- | ----------------------------------------- | ----------------------------------- | ----------------- | -| Complete `node_modules` | 132.93 MiB | 150.87 MiB | 17.94 MiB (13.5%) | -| Backend packages | 21.63 MiB | 39.69 MiB | 18.06 MiB (83.5%) | - -The complete installation includes other release-to-preview dependency changes; -the backend row isolates the backend packages. These are historical measurements, -not sizes for the current `0.24.0` integration. Repeat them before release. - -## Compatibility - -The pinned release targets TypeScript `7.1.0-beta`. Changing the backend also -changes the compiler version, so identical diagnostics are not guaranteed. -The [ecosystem report](../bench/tsrs-lint-ecosystem.md) records the four known -compatibility categories and their upstream attribution. Matching the pinned -TypeScript compiler does not make every new diagnostic desirable. - -With type checking enabled, `0.24.0` checks whole programs for the selected -tsconfigs before running lint rules. Unselected and ignored program files can -report type errors; lint ignore patterns still control lint rules. Files outside -discovered configs use an inferred program. This broader scope can increase the -work per invocation. +| Dependency | Before | After | +| ------------------------- | ------------------------------------------- | ------------------------------------------ | +| Linter frontend | `oxlint@1.93.0` | Unchanged | +| Production backend | `oxlint-tsgolint@7.0.2006` | `tsrs@0.24.0-ts7.1.0-beta` | +| Development-only backend | None | `oxlint-tsgolint@7.0.2006` for comparisons | +| Backend platform packages | `@oxlint-tsgolint/*`: six optional packages | `@tsrs/*`: seven optional packages | -The backend respects each project's compiler options. `skipLibCheck` controls -declaration checking; it is not a requirement for using tsrs. This draft aligns -the two starter templates with the repository's existing setting and supplies -the docs theme's missing Vue type declaration. Existing projects need their own -configuration review. +Production installs use tsrs; repository development retains Go for comparisons. +The project's `typescript` dependency and standalone `tsc` tasks are unchanged. -## Release conditions +## Compatibility and release considerations -- Repeat elapsed-time, memory, and installed-size comparisons with the release - candidate. Keep historical measurements labeled with their original versions. -- Validate lint rules, fixes, suppression comments, and optional type checking - on representative ecosystem projects. Track compiler-version changes separately - from Rust implementation defects. -- Validate all supported runtime targets. The pinned release has no Windows - ARM64 binary, and this draft implements no automatic Go fallback. Resolve that - platform gap before a release that supports all Vite+ targets. +- tsrs targets TypeScript `7.1.0-beta`, so compiler diagnostics can change. +- With `lint.options.typeCheck` enabled, tsrs checks whole TypeScript programs, + including files excluded by lint ignore patterns. Existing projects can + report additional errors. +- Windows ARM64 remains a release blocker: tsrs has no binary for that target, + and this integration has no automatic Go fallback. -If the candidate fails these conditions, retain the Go backend for the release -or revert the bundled-backend change. Backend selection remains a toolchain -decision rather than an additional user setup step. +The [benchmark report](../bench/tsrs-lint.md) contains historical timing, memory, +and disk-usage measurements. Refresh them for the release candidate. From a36541a80b25ab4d2efc9a51a11eb2c57d551614 Mon Sep 17 00:00:00 2001 From: MK Date: Wed, 11 Nov 2026 16:29:46 +0800 Subject: [PATCH 20/25] test(lint): update inherited type-check snapshots --- .../migration_oxlint_extends/snapshots.toml | 4 ++-- .../migration_oxlint_extends_type_options.md | 20 +++++++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends/snapshots.toml index 762a835758..81ebc4143c 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends/snapshots.toml @@ -38,11 +38,11 @@ steps = [ { argv = ["vpt", "write-file", "type-error.ts", "export const value: string = 1;\n"], snapshot = false }, { argv = ["vpt", "write-file", "type-aware.ts", "Promise.resolve(1);\n"], snapshot = false }, { argv = ["vp", "lint", "--threads=1", "-c", ".oxlintrc.json", "type-error.ts"], comment = "Inherited typeCheck reports the original TypeScript diagnostic", continue-on-failure = true }, - { argv = ["vp", "lint", "--threads=1", "-c", ".oxlintrc.json", "type-aware.ts"], comment = "Inherited typeAware enables no-floating-promises", continue-on-failure = true }, + { argv = ["vp", "lint", "--threads=1", "-c", ".oxlintrc.json", "type-aware.ts"], comment = "Inherited options enable no-floating-promises and report type errors outside the lint input", continue-on-failure = true }, { argv = ["vp", "migrate", "--no-interactive", "--no-agent", "--no-editor", "--no-hooks"] }, { argv = ["vpt", "print-file", "vite.config.ts"], comment = "Local options stay empty so inherited settings remain live" }, { argv = ["vp", "lint", "--threads=1", "type-error.ts"], comment = "Imported typeCheck produces the same diagnostic and exit code", continue-on-failure = true }, - { argv = ["vp", "lint", "--threads=1", "type-aware.ts"], comment = "Imported typeAware produces the same diagnostic and exit code", continue-on-failure = true }, + { argv = ["vp", "lint", "--threads=1", "type-aware.ts"], comment = "Imported options preserve both diagnostics and the exit code", continue-on-failure = true }, { argv = ["vpt", "replace-file-content", "shared/base.ts", "typeAware: true", "typeAware: false"] }, { argv = ["vpt", "replace-file-content", "shared/base.ts", "typeCheck: true", "typeCheck: false"] }, { argv = ["vp", "lint", "--threads=1", "type-error.ts"], comment = "Disabling the shared options removes the TypeScript diagnostic" }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends/snapshots/migration_oxlint_extends_type_options.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends/snapshots/migration_oxlint_extends_type_options.md index dd4f60cfa4..6fe47a0368 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends/snapshots/migration_oxlint_extends_type_options.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_oxlint_extends/snapshots/migration_oxlint_extends_type_options.md @@ -37,7 +37,7 @@ Finished in on 1 file with rules using threads. ## `vp lint --threads=1 -c .oxlintrc.json type-aware.ts` -Inherited typeAware enables no-floating-promises +Inherited options enable no-floating-promises and report type errors outside the lint input **Exit code:** 1 @@ -51,7 +51,13 @@ Inherited typeAware enables no-floating-promises ╰──── help: The promise must end with a call to .catch, or end with a call to .then with a rejection handler, or be explicitly marked as ignored with the `void` operator. -Found 0 warnings and 1 error. + × typescript(TS2322): Type 'number' is not assignable to type 'string'. + ╭─[type-error.ts:1:14] + 1 │ export const value: string = 1; + · ───── + ╰──── + +Found 0 warnings and 2 errors. Finished in on 1 file with rules using threads. ``` @@ -114,7 +120,7 @@ Finished in on 1 file with rules using threads. ## `vp lint --threads=1 type-aware.ts` -Imported typeAware produces the same diagnostic and exit code +Imported options preserve both diagnostics and the exit code **Exit code:** 1 @@ -128,7 +134,13 @@ Imported typeAware produces the same diagnostic and exit code ╰──── help: The promise must end with a call to .catch, or end with a call to .then with a rejection handler, or be explicitly marked as ignored with the `void` operator. -Found 0 warnings and 1 error. + × typescript(TS2322): Type 'number' is not assignable to type 'string'. + ╭─[type-error.ts:1:14] + 1 │ export const value: string = 1; + · ───── + ╰──── + +Found 0 warnings and 2 errors. Finished in on 1 file with rules using threads. ``` From 6350fb7538d3cbf89813ff97e9861cff475f4e82 Mon Sep 17 00:00:00 2001 From: MK Date: Thu, 12 Nov 2026 14:12:46 +0800 Subject: [PATCH 21/25] feat(lint): ship tsrs as the default backend --- .github/renovate.json | 1 + .../scripts/__tests__/upgrade-deps.spec.ts | 5 ++ .github/scripts/upgrade-deps.ts | 11 +++ .../snapshots/migration_monorepo_pnpm.md | 2 + docs/guide/check.md | 2 +- docs/guide/lint.md | 4 +- packages/cli/src/init-config.ts | 2 +- .../src/migration/__tests__/migrator.spec.ts | 2 + .../cli/src/migration/migrator/catalog.ts | 2 + .../cli/src/migration/migrator/vite-config.ts | 2 +- packages/cli/src/utils/tsconfig.ts | 2 +- pnpm-lock.yaml | 78 +++++++++++-------- pnpm-workspace.yaml | 2 +- rfcs/check-command.md | 13 ++-- rfcs/toolchain-command.md | 13 ++-- rfcs/tsrs-lint-backend.md | 12 +-- 16 files changed, 91 insertions(+), 62 deletions(-) diff --git a/.github/renovate.json b/.github/renovate.json index ea6ef3018b..7baa60cb64 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -96,6 +96,7 @@ "oxlint", "oxlint-tsgolint", "tsdown", + "tsrs", "vite", "vitest", "vitest-dev", diff --git a/.github/scripts/__tests__/upgrade-deps.spec.ts b/.github/scripts/__tests__/upgrade-deps.spec.ts index 08ab6d0a5f..bf68c15892 100644 --- a/.github/scripts/__tests__/upgrade-deps.spec.ts +++ b/.github/scripts/__tests__/upgrade-deps.spec.ts @@ -123,6 +123,9 @@ test.each([false, true])( if (url === 'https://registry.npmjs.org/oxlint-tsgolint/latest') { return Response.json({ version: '7.0.2004' }); } + if (url === 'https://registry.npmjs.org/tsrs/latest') { + return Response.json({ version: '0.26.0-ts7.1.0-beta' }); + } if ( url === 'https://registry.npmjs.org/oxc-minify/latest' || url === 'https://registry.npmjs.org/oxc-transform/latest' @@ -173,6 +176,7 @@ test.each([false, true])( expect(readFileSync(workspacePath, 'utf8')).toContain("\n '@oxlint/plugins': =1.88.0\n"); expect(readFileSync(workspacePath, 'utf8')).toContain('\n oxlint: =1.88.0\n'); expect(readFileSync(workspacePath, 'utf8')).toContain('\n oxlint-tsgolint: =7.0.2004\n'); + expect(readFileSync(workspacePath, 'utf8')).toContain('\n tsrs: =0.26.0-ts7.1.0-beta\n'); expect(readFileSync(nodeVersionPath, 'utf8')).toBe('24.10.2\n'); expect(readFileSync(installerPath, 'utf8')).toContain( 'const PINNED_PNPM_VERSION: &str = "10.40.2";', @@ -201,6 +205,7 @@ test.each([false, true])( expect(versions['@oxlint/plugins'].new).toBe('1.88.0'); expect(versions['@oxlint/plugins'].new).toBe(versions.oxlint.new); expect(versions['oxlint-tsgolint'].new).toBe('7.0.2004'); + expect(versions.tsrs.new).toBe('0.26.0-ts7.1.0-beta'); expect(versions).not.toHaveProperty('oxc-minify'); expect(versions).not.toHaveProperty('oxc-transform'); expect(versions['oxc-node']).toEqual( diff --git a/.github/scripts/upgrade-deps.ts b/.github/scripts/upgrade-deps.ts index 06a0302111..9ca8fb16a9 100644 --- a/.github/scripts/upgrade-deps.ts +++ b/.github/scripts/upgrade-deps.ts @@ -66,6 +66,7 @@ type PnpmWorkspaceVersions = { oxfmt: string; oxlint: string; oxlintTsgolint: string; + tsrs: string; oxcProjectRuntime: string; oxcProjectTypes: string; oxcParser: string; @@ -369,6 +370,12 @@ async function updatePnpmWorkspace(versions: PnpmWorkspaceVersions): Promise { oxfmtVersion, oxlintVersion, oxlintTsgolintVersion, + tsrsVersion, oxcProjectRuntimeVersion, oxcProjectTypesVersion, oxcParserVersion, @@ -582,6 +590,7 @@ async function upgradeDependencies(): Promise { getLatestNpmVersion('oxfmt'), getLatestNpmVersion('oxlint'), getLatestNpmVersion('oxlint-tsgolint'), + getLatestNpmVersion('tsrs'), getLatestNpmVersion('@oxc-project/runtime'), getLatestNpmVersion('@oxc-project/types'), getLatestNpmVersion('oxc-parser'), @@ -599,6 +608,7 @@ async function upgradeDependencies(): Promise { console.log(`oxlint: ${oxlintVersion}`); console.log(`@oxlint/plugins (from oxlint): ${oxlintVersion}`); console.log(`oxlint-tsgolint: ${oxlintTsgolintVersion}`); + console.log(`tsrs: ${tsrsVersion}`); console.log(`@oxc-project/runtime: ${oxcProjectRuntimeVersion}`); console.log(`@oxc-project/types: ${oxcProjectTypesVersion}`); console.log(`oxc-parser: ${oxcParserVersion}`); @@ -621,6 +631,7 @@ async function upgradeDependencies(): Promise { oxfmt: oxfmtVersion, oxlint: oxlintVersion, oxlintTsgolint: oxlintTsgolintVersion, + tsrs: tsrsVersion, oxcProjectRuntime: oxcProjectRuntimeVersion, oxcProjectTypes: oxcProjectTypesVersion, oxcParser: oxcParserVersion, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_monorepo_pnpm/snapshots/migration_monorepo_pnpm.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_monorepo_pnpm/snapshots/migration_monorepo_pnpm.md index 9b74a136c7..9649b48eae 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_monorepo_pnpm/snapshots/migration_monorepo_pnpm.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_monorepo_pnpm/snapshots/migration_monorepo_pnpm.md @@ -147,6 +147,8 @@ minimumReleaseAgeExclude: - '@oxlint/*' - oxlint-tsgolint - '@oxlint-tsgolint/*' + - tsrs + - '@tsrs/*' - oxfmt - '@oxfmt/*' - vitest diff --git a/docs/guide/check.md b/docs/guide/check.md index 6cebe2f077..da68affd38 100644 --- a/docs/guide/check.md +++ b/docs/guide/check.md @@ -4,7 +4,7 @@ ## Overview -`vp check` is the default command for fast static checks in Vite+. It brings together formatting through [Oxfmt](https://oxc.rs/docs/guide/usage/formatter.html), linting through [Oxlint](https://oxc.rs/docs/guide/usage/linter.html), and TypeScript type checks through [tsrs](https://github.com/oxc-project/tsrs) in this experimental build. By merging all of these tasks into a single command, `vp check` is faster than running formatting, linting, and type checking as separate tools in separate commands. +`vp check` is the default command for fast static checks in Vite+. It brings together formatting through [Oxfmt](https://oxc.rs/docs/guide/usage/formatter.html), linting through [Oxlint](https://oxc.rs/docs/guide/usage/linter.html), and TypeScript type checks through [tsrs](https://github.com/oxc-project/tsrs). By merging all of these tasks into a single command, `vp check` is faster than running formatting, linting, and type checking as separate tools in separate commands. When both `typeAware` and `typeCheck` are enabled in the `lint.options` block in `vite.config.ts`, `vp check` also runs TypeScript type checks through the Oxlint type-aware path powered by `tsrs-lint`. `vp create` and `vp migrate` enable both `typeAware` and `typeCheck` by default. diff --git a/docs/guide/lint.md b/docs/guide/lint.md index 6e37103417..08fa54e07a 100644 --- a/docs/guide/lint.md +++ b/docs/guide/lint.md @@ -49,9 +49,9 @@ We recommend enabling both `typeAware` and `typeCheck` in the `lint` block: - `typeAware: true` enables rules that require TypeScript type information - `typeCheck: true` enables full type checking during linting -This experimental build uses [tsrs](https://github.com/oxc-project/tsrs), a Rust port of the TypeScript 7 compiler. Its `tsrs-lint` executable gives Oxlint access to type information and allows type checking directly via `vp lint` and `vp check`. +Vite+ uses [tsrs](https://github.com/oxc-project/tsrs), a Rust port of the TypeScript 7 compiler. Its `tsrs-lint` executable gives Oxlint access to type information and allows type checking directly via `vp lint` and `vp check`. Upgrading Vite+ enables this backend without a separate installation or configuration change. -The pinned `tsrs` release does not provide a Windows ARM64 binary. This experimental replacement does not support that platform yet. +With `typeCheck: true`, compiler checks cover the TypeScript programs selected by the lint input files. Lint ignore patterns only exclude lint rules; use `include` and `exclude` in `tsconfig.json` to set the compiler scope. Files imported by the program can still be checked even when they match `exclude`. ## JS Plugins diff --git a/packages/cli/src/init-config.ts b/packages/cli/src/init-config.ts index d687ff70f9..eae56746a3 100644 --- a/packages/cli/src/init-config.ts +++ b/packages/cli/src/init-config.ts @@ -224,7 +224,7 @@ export async function applyToolInitConfigToViteConfig( const lintInitConfigPath = path.join(projectPath, '.vite-plus-lint-init.oxlintrc.json'); const { fixBaseUrlInTsconfig, hasBaseUrlInTsconfig } = await import('./utils/tsconfig.ts'); await fixBaseUrlInTsconfig(projectPath); - // Skip typeAware/typeCheck when tsconfig still has baseUrl (unsupported by tsgolint) + // Skip typeAware/typeCheck when tsconfig still has baseUrl (unsupported by TypeScript 7) const hasBaseUrl = hasBaseUrlInTsconfig(projectPath); const initConfig = createDefaultVitePlusLintConfig({ includeTypeAwareDefaults: !hasBaseUrl, diff --git a/packages/cli/src/migration/__tests__/migrator.spec.ts b/packages/cli/src/migration/__tests__/migrator.spec.ts index 58bebeebd0..d89bacb9ff 100644 --- a/packages/cli/src/migration/__tests__/migrator.spec.ts +++ b/packages/cli/src/migration/__tests__/migrator.spec.ts @@ -4480,6 +4480,8 @@ describe('ensureVitePlusBootstrap', () => { // Vite+ pins a possibly-fresh vitest; its family must bypass the gate. expect(workspace.minimumReleaseAgeExclude).toContain('vitest'); expect(workspace.minimumReleaseAgeExclude).toContain('@vitest/*'); + expect(workspace.minimumReleaseAgeExclude).toContain('tsrs'); + expect(workspace.minimumReleaseAgeExclude).toContain('@tsrs/*'); // The user's own entry and the vite-plus/ox families are preserved. expect(workspace.minimumReleaseAgeExclude).toContain('my-internal-pkg'); expect(workspace.minimumReleaseAgeExclude).toContain('vite-plus'); diff --git a/packages/cli/src/migration/migrator/catalog.ts b/packages/cli/src/migration/migrator/catalog.ts index 76c5e8b932..254ebc0514 100644 --- a/packages/cli/src/migration/migrator/catalog.ts +++ b/packages/cli/src/migration/migrator/catalog.ts @@ -69,6 +69,8 @@ const PNPM_MINIMUM_RELEASE_AGE_EXCLUDES = [ '@oxlint/*', 'oxlint-tsgolint', '@oxlint-tsgolint/*', + 'tsrs', + '@tsrs/*', 'oxfmt', '@oxfmt/*', ...VITEST_AGE_GATE_EXEMPT_PACKAGES, diff --git a/packages/cli/src/migration/migrator/vite-config.ts b/packages/cli/src/migration/migrator/vite-config.ts index 96e6e6d46f..0cb6cc12ed 100644 --- a/packages/cli/src/migration/migrator/vite-config.ts +++ b/packages/cli/src/migration/migrator/vite-config.ts @@ -418,7 +418,7 @@ function mergeOxlintConfigFile( Object.assign(inheritedOptions, layer.options); } oxlintJson.options ||= {}; - // Skip typeAware/typeCheck when tsconfig.json has baseUrl (unsupported by tsgolint) + // Skip typeAware/typeCheck when tsconfig.json has baseUrl (unsupported by TypeScript 7) if (!hasBaseUrlInTsconfig(projectPath)) { if (inheritedOptions.typeAware === undefined) { oxlintJson.options.typeAware = true; diff --git a/packages/cli/src/utils/tsconfig.ts b/packages/cli/src/utils/tsconfig.ts index 6da34c4b73..dfca62356b 100644 --- a/packages/cli/src/utils/tsconfig.ts +++ b/packages/cli/src/utils/tsconfig.ts @@ -14,7 +14,7 @@ export type BaseUrlFixStatus = 'not-needed' | 'fixed' | 'declined' | 'failed'; /** * Check if tsconfig.json has compilerOptions.baseUrl set. - * oxlint's TypeScript checker (tsgolint) does not support baseUrl, + * Oxlint's TypeScript 7 backend does not support baseUrl, * so typeAware/typeCheck must be disabled when it is present. */ export function hasBaseUrlInTsconfigFile(filePath: string): boolean { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e0a5c4eac4..e3059746e3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -345,8 +345,8 @@ catalogs: specifier: ^0.23.4 version: 0.23.4 tsrs: - specifier: '=0.24.0-ts7.1.0-beta' - version: 0.24.0-ts7.1.0-beta + specifier: '=0.25.0-ts7.1.0-beta' + version: 0.25.0-ts7.1.0-beta typescript: specifier: ~7.0.2 version: 7.0.2 @@ -471,7 +471,7 @@ importers: version: 1.62.1 tsrs: specifier: 'catalog:' - version: 0.24.0-ts7.1.0-beta + version: 0.25.0-ts7.1.0-beta vite: specifier: workspace:@voidzero-dev/vite-plus-core@* version: link:../core @@ -3582,48 +3582,54 @@ packages: peerDependencies: tsdown: 0.23.4 - '@tsrs/darwin-arm64@0.24.0-ts7.1.0-beta': - resolution: {integrity: sha512-GxGPRieCGIXevJgGlbne9pRqkfAN+VmBQvBHWWcF8rXLNX9yCxYo78/a7Auqjf723b4eFTlM5mgkclUit/Fu9w==} + '@tsrs/darwin-arm64@0.25.0-ts7.1.0-beta': + resolution: {integrity: sha512-+pYoQ+AqpdUolO4kJBQlQmwxJOoc5INuusw/2AZnR4frfnyVGza+gEn0br8Z0LOk7nklQuILuMKH93BU3hpHvA==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [darwin] - '@tsrs/darwin-x64@0.24.0-ts7.1.0-beta': - resolution: {integrity: sha512-TrXdOyEq++blyYp6KphoHGmsGxAn1flYzjm0peLEI3c88z1UQa+pKkcuPgZ6FOxTW6fSaT9572n21kz+0bWi/g==} + '@tsrs/darwin-x64@0.25.0-ts7.1.0-beta': + resolution: {integrity: sha512-MnD5Bv48TdIcTpNavsg7PV6RUuwAKhozJ29CZDlKq6oZwGjGulcQLI3Sok6MvvCG7U6v4l9RONKO8BhNXCytvQ==} engines: {node: '>=16.20.0'} cpu: [x64] os: [darwin] - '@tsrs/linux-arm64-gnu@0.24.0-ts7.1.0-beta': - resolution: {integrity: sha512-Ph5NK0vnaR4gnBW2LgrZsUPx4mKF+9tCQUYZFOHmny6SMojSsm/5GCiBu0sHZEZhmyIqq8UbineUEA7T0QImnw==} + '@tsrs/linux-arm64-gnu@0.25.0-ts7.1.0-beta': + resolution: {integrity: sha512-Mc/YuFQa7APDuX39UMr8NMQHKkCOmEx8SfUXVnew7eljJHKnUkpLHpoQFQA0aKnzIqDAxnNudXhIWB0DjZEX2A==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [linux] libc: [glibc] - '@tsrs/linux-arm64-musl@0.24.0-ts7.1.0-beta': - resolution: {integrity: sha512-vjgx8wGuAVSOPLlb2eVQpQVolImZa7vsH/1kvYOaokcP/KZXVUmHzgroxK0t9gxJ0SXvT4VKNjWZ+t+4tDNu1A==} + '@tsrs/linux-arm64-musl@0.25.0-ts7.1.0-beta': + resolution: {integrity: sha512-ZTZxIv/W4qiqDBCXcDlr8ma76JCO0DQHbUukuhpSpKHz3E2m5FCwLcra/NTqAVniyLU6/RjIOCIcDRpVQvq6cQ==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [linux] libc: [musl] - '@tsrs/linux-x64-gnu@0.24.0-ts7.1.0-beta': - resolution: {integrity: sha512-tj1SNWBnJoBUzU2zrD18tUu3h6o36FXQGPpmIXsADHSfU5H9pVQdK/QzPIO76rb72P7foXHluE+xGTXXf8nNaA==} + '@tsrs/linux-x64-gnu@0.25.0-ts7.1.0-beta': + resolution: {integrity: sha512-dGRvubxWQXNP3aow+RLgFAbW3pR4oQ+UPfwrSgdggKCKKL9sAPr8nFGYM50JWAiqGQYSwRJl48AzIC7bHuQ2cQ==} engines: {node: '>=16.20.0'} cpu: [x64] os: [linux] libc: [glibc] - '@tsrs/linux-x64-musl@0.24.0-ts7.1.0-beta': - resolution: {integrity: sha512-sizu0EdGw9ITqEaZKg60TDhDk5PXT9RzVkCdesBjk/BZhS0BVU2cRhvoRBmDbk+2vCUgW14j+Otg3tl+002qNQ==} + '@tsrs/linux-x64-musl@0.25.0-ts7.1.0-beta': + resolution: {integrity: sha512-zm89HYpXAv36XuXrEsfxAEmGTehY5K0L27+tFmJzxX2CqLwki0AX4FGA78Fy97x8Yk8kOR8FJO2xTvSW9vXBIg==} engines: {node: '>=16.20.0'} cpu: [x64] os: [linux] libc: [musl] - '@tsrs/win32-x64@0.24.0-ts7.1.0-beta': - resolution: {integrity: sha512-WAbzgd5JBOauQ/oL6DTdGEh09tM/q8tlGAcRORskhPK7eIQVVa8Js2pc7DVW0XokWTSADeD9v6HKtU2Ge0r/fQ==} + '@tsrs/win32-arm64@0.25.0-ts7.1.0-beta': + resolution: {integrity: sha512-fVqAnHvFD2vxIHxVJEeQq9W63QZUW6WLy1+qFVuMUdM5ViURhjzcuLgOx8EIGqp6qOqFDpuAlNPQmjUpTXYg2g==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [win32] + + '@tsrs/win32-x64@0.25.0-ts7.1.0-beta': + resolution: {integrity: sha512-BfRCSI5E0LQ3kim43dBqX3Dq3dmRUI2aMWLGcgUxbTEkyvsESX01tM7u31flzRRajw3zVZwVB95kh5xvUeIOkA==} engines: {node: '>=16.20.0'} cpu: [x64] os: [win32] @@ -6715,8 +6721,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsrs@0.24.0-ts7.1.0-beta: - resolution: {integrity: sha512-Iy7q93qfRHcI0Y31IKx6fCsDQCsSjtrT01PIgOVolBXPJTMdm84oH1pEYyAMPQ/SnRqWDSWI8wEUHbv6e9TmHA==} + tsrs@0.25.0-ts7.1.0-beta: + resolution: {integrity: sha512-J7eAkkriIwHExX2TzwTW6flJ/fmg3U+FDQwmTUkvaYmo02iva69YPmxFaCXGYIlsED3l1Aa62hSsEDJewBrIKA==} engines: {node: '>=16.20.0'} hasBin: true @@ -9012,25 +9018,28 @@ snapshots: tsdown: 0.23.4(@arethetypeswrong/core@0.18.2)(@tsdown/css@0.23.4)(@tsdown/exe@0.23.4)(@typescript/native-preview@7.0.0-dev.20260707.2)(@vitejs/devtools@0.8.2(crossws@0.4.17(srvx@1.0.6))(srvx@1.0.6)(vite@packages+core))(@volar/typescript@2.4.28)(@vue/language-core@3.3.12)(oxc-resolver@11.24.2)(publint@0.3.24)(tsx@4.23.15)(typescript@7.0.2)(unplugin-unused@0.5.6)(unrun@0.3.1)(vue-tsc@3.3.12(typescript@7.0.2)) verkit: 0.5.0 - '@tsrs/darwin-arm64@0.24.0-ts7.1.0-beta': + '@tsrs/darwin-arm64@0.25.0-ts7.1.0-beta': + optional: true + + '@tsrs/darwin-x64@0.25.0-ts7.1.0-beta': optional: true - '@tsrs/darwin-x64@0.24.0-ts7.1.0-beta': + '@tsrs/linux-arm64-gnu@0.25.0-ts7.1.0-beta': optional: true - '@tsrs/linux-arm64-gnu@0.24.0-ts7.1.0-beta': + '@tsrs/linux-arm64-musl@0.25.0-ts7.1.0-beta': optional: true - '@tsrs/linux-arm64-musl@0.24.0-ts7.1.0-beta': + '@tsrs/linux-x64-gnu@0.25.0-ts7.1.0-beta': optional: true - '@tsrs/linux-x64-gnu@0.24.0-ts7.1.0-beta': + '@tsrs/linux-x64-musl@0.25.0-ts7.1.0-beta': optional: true - '@tsrs/linux-x64-musl@0.24.0-ts7.1.0-beta': + '@tsrs/win32-arm64@0.25.0-ts7.1.0-beta': optional: true - '@tsrs/win32-x64@0.24.0-ts7.1.0-beta': + '@tsrs/win32-x64@0.25.0-ts7.1.0-beta': optional: true '@tybys/wasm-util@0.10.3': @@ -12203,15 +12212,16 @@ snapshots: tslib@2.8.1: {} - tsrs@0.24.0-ts7.1.0-beta: + tsrs@0.25.0-ts7.1.0-beta: optionalDependencies: - '@tsrs/darwin-arm64': 0.24.0-ts7.1.0-beta - '@tsrs/darwin-x64': 0.24.0-ts7.1.0-beta - '@tsrs/linux-arm64-gnu': 0.24.0-ts7.1.0-beta - '@tsrs/linux-arm64-musl': 0.24.0-ts7.1.0-beta - '@tsrs/linux-x64-gnu': 0.24.0-ts7.1.0-beta - '@tsrs/linux-x64-musl': 0.24.0-ts7.1.0-beta - '@tsrs/win32-x64': 0.24.0-ts7.1.0-beta + '@tsrs/darwin-arm64': 0.25.0-ts7.1.0-beta + '@tsrs/darwin-x64': 0.25.0-ts7.1.0-beta + '@tsrs/linux-arm64-gnu': 0.25.0-ts7.1.0-beta + '@tsrs/linux-arm64-musl': 0.25.0-ts7.1.0-beta + '@tsrs/linux-x64-gnu': 0.25.0-ts7.1.0-beta + '@tsrs/linux-x64-musl': 0.25.0-ts7.1.0-beta + '@tsrs/win32-arm64': 0.25.0-ts7.1.0-beta + '@tsrs/win32-x64': 0.25.0-ts7.1.0-beta tsx@4.23.15: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 09119009df..5e29670699 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -67,7 +67,7 @@ catalog: source-map: 0.8.0 tinyglobby: ^0.2.15 tsdown: ^0.23.4 - tsrs: =0.24.0-ts7.1.0-beta + tsrs: =0.25.0-ts7.1.0-beta typescript: ~7.0.2 valibot: 1.5.0 validate-npm-package-name: ^8.0.0 diff --git a/rfcs/check-command.md b/rfcs/check-command.md index 555ecd37a4..3ee05cd5a2 100644 --- a/rfcs/check-command.md +++ b/rfcs/check-command.md @@ -200,12 +200,12 @@ The check module reuses `SubcommandResolver` and `resolve_and_capture_output` fr `vp check` reuses `resolve-lint.ts` and `resolve-fmt.ts`. -### Experimental type-aware backend +### Type-aware backend See the [backend replacement RFC](./tsrs-lint-backend.md) for the rationale, -compatibility considerations, and release conditions. +dependency changes and compatibility considerations. -This draft replaces the bundled Go backend with `tsrs@0.24.0-ts7.1.0-beta`, +Vite+ replaces the bundled Go backend with `tsrs@0.25.0-ts7.1.0-beta`, a Rust port of the TypeScript 7 compiler. `resolve-lint.ts` selects the bundled `tsrs` package and passes its `bin/tsrs-lint` launcher to Oxlint through `OXLINT_TSGOLINT_PATH`. The same resolver serves `vp lint` and `vp check`. @@ -228,11 +228,8 @@ so a change to the default cannot make it compare tsrs against itself. See the [performance report](../bench/tsrs-lint.md) for elapsed time, peak memory, and measurement conditions. -The pinned tsrs release has no Windows ARM64 binary. This draft adds no automatic -Go fallback. A release for all supported Vite+ platforms needs an upstream -Windows ARM64 binary or an explicit fallback. Runtime smoke tests cover macOS -ARM64. The Codespaces comparison also validates both backends on Linux x64 -with Vite+ and vinext. Other platforms still need runtime validation. +The pinned tsrs release provides native packages for all supported Vite+ +platforms, including Windows ARM64. No platform uses a Go fallback. The backend switch preserves the existing formatting phase, lint configuration, and failure handling. Smoke fixtures compare diagnostics, suppression comments, diff --git a/rfcs/toolchain-command.md b/rfcs/toolchain-command.md index 1df6ae6d7c..b0a90c3fa6 100644 --- a/rfcs/toolchain-command.md +++ b/rfcs/toolchain-command.md @@ -164,13 +164,13 @@ vite-plus@0.2.4 │ └── bundles tsdown@0.22.3 ├── depends on vitest@4.1.10 ├── depends on oxlint@1.72.0 -├── depends on tsrs@0.24.0-ts7.1.0-beta +├── depends on tsrs@0.25.0-ts7.1.0-beta ├── depends on oxfmt@0.57.0 └── compiles vite-task (built 2026-08-06T09:30:00Z, revision ) ``` These versions illustrate the output format. They are not part of the command -contract. The tsrs row reflects the experimental default backend. +contract. The tsrs row identifies the default backend. The readable tree can repeat a shared node to show two relationships. JSON has one entry for each node ID. @@ -218,7 +218,7 @@ Initial aliases include: | `vite-task` | Vite Task | | `oxc-resolver` | Oxc Resolver | -The experimental manifest uses `tsrs` as the backend node ID and package name. +The manifest uses `tsrs` as the backend node ID and package name. It retains `tsgolint` and `oxlint-tsgolint` as aliases for existing queries. These aliases select the bundled tsrs node; they do not report a separate Go dependency. Older manifests retain their original `oxlint-tsgolint` node. @@ -419,15 +419,14 @@ the local Vite+ release to use the command. To show the global release, run It reads tool rows from the manifest. Use `vp toolchain` to select tools and show relationships or engine details. -For this experimental backend, the local version output and `vite-plus/versions` +The local version output and `vite-plus/versions` report the `tsrs` version. The generated export uses the `tsrs` key in place of `oxlint-tsgolint`. The global version command selects the tsrs row when the local manifest contains it. It retains the Go row for older local manifests. The manifest describes the bundled backend. An `OXLINT_TSGOLINT_PATH` override -does not change its nodes or the version exports. The manifest also does not -certify platform support: the pinned tsrs release lacks a Windows ARM64 binary. -See the [backend design](./check-command.md#experimental-type-aware-backend). +does not change its nodes or the version exports. +See the [backend design](./check-command.md#type-aware-backend). ## Relationship to `vp why` diff --git a/rfcs/tsrs-lint-backend.md b/rfcs/tsrs-lint-backend.md index 40ec37467f..2806353f61 100644 --- a/rfcs/tsrs-lint-backend.md +++ b/rfcs/tsrs-lint-backend.md @@ -1,6 +1,6 @@ # RFC: Replace tsgolint with tsrs-lint -Status: Proposed; experimental default in this draft. +Status: Proposed for the next release. ## User experience @@ -22,9 +22,9 @@ and toolchain metadata identify `tsrs`. | Dependency | Before | After | | ------------------------- | ------------------------------------------- | ------------------------------------------ | | Linter frontend | `oxlint@1.93.0` | Unchanged | -| Production backend | `oxlint-tsgolint@7.0.2006` | `tsrs@0.24.0-ts7.1.0-beta` | +| Production backend | `oxlint-tsgolint@7.0.2006` | `tsrs@0.25.0-ts7.1.0-beta` | | Development-only backend | None | `oxlint-tsgolint@7.0.2006` for comparisons | -| Backend platform packages | `@oxlint-tsgolint/*`: six optional packages | `@tsrs/*`: seven optional packages | +| Backend platform packages | `@oxlint-tsgolint/*`: six optional packages | `@tsrs/*`: eight optional packages | Production installs use tsrs; repository development retains Go for comparisons. The project's `typescript` dependency and standalone `tsc` tasks are unchanged. @@ -34,9 +34,9 @@ The project's `typescript` dependency and standalone `tsc` tasks are unchanged. - tsrs targets TypeScript `7.1.0-beta`, so compiler diagnostics can change. - With `lint.options.typeCheck` enabled, tsrs checks whole TypeScript programs, including files excluded by lint ignore patterns. Existing projects can - report additional errors. -- Windows ARM64 remains a release blocker: tsrs has no binary for that target, - and this integration has no automatic Go fallback. + report additional errors. Projects control compiler scope through `tsconfig.json`. +- tsrs `0.25.0` provides native packages for all Vite+ targets, including Windows ARM64. + The same backend runs on every supported platform. The [benchmark report](../bench/tsrs-lint.md) contains historical timing, memory, and disk-usage measurements. Refresh them for the release candidate. From 73d4b4ffdfe77402490411502ca97307742dba5f Mon Sep 17 00:00:00 2001 From: MK Date: Thu, 12 Nov 2026 14:32:49 +0800 Subject: [PATCH 22/25] fix(migrate): scope tsrs trust exception to reviewed release --- .../pnpm-workspace.yaml | 4 ++ .../snapshots/migration_monorepo_pnpm.md | 7 ++ docs/guide/migrate-rules.md | 6 ++ .../src/migration/__tests__/migrator.spec.ts | 71 +++++++++++++++++++ .../cli/src/migration/migrator/catalog.ts | 46 ++++++++++++ .../migration/migrator/vite-plus-bootstrap.ts | 5 +- rfcs/tsrs-lint-backend.md | 5 ++ 7 files changed, 143 insertions(+), 1 deletion(-) diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_monorepo_pnpm/pnpm-workspace.yaml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_monorepo_pnpm/pnpm-workspace.yaml index b8b7c1b4f9..9705df5dbb 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_monorepo_pnpm/pnpm-workspace.yaml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_monorepo_pnpm/pnpm-workspace.yaml @@ -11,3 +11,7 @@ catalog: oxlint-tsgolint: ^1.0.0 minimumReleaseAge: 1440 +trustPolicy: no-downgrade +trustPolicyExclude: + # Keep the project's existing exception. + - testnpm2@1.0.0 diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_monorepo_pnpm/snapshots/migration_monorepo_pnpm.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_monorepo_pnpm/snapshots/migration_monorepo_pnpm.md index 9649b48eae..f795cd2d28 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_monorepo_pnpm/snapshots/migration_monorepo_pnpm.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_monorepo_pnpm/snapshots/migration_monorepo_pnpm.md @@ -130,6 +130,13 @@ catalog: vite-plus: minimumReleaseAge: 1440 +trustPolicy: no-downgrade +trustPolicyExclude: + # Keep the project's existing exception. + - testnpm2@1.0.0 + # Reviewed OIDC release; the bootstrap placeholder used staged publishing. + # https://github.com/oxc-project/tsrs/issues/945 + - tsrs@0.25.0-ts7.1.0-beta overrides: vite@*: 'catalog:' vitest@*: 'catalog:' diff --git a/docs/guide/migrate-rules.md b/docs/guide/migrate-rules.md index 3101d3f5dd..a883f113fb 100644 --- a/docs/guide/migrate-rules.md +++ b/docs/guide/migrate-rules.md @@ -381,6 +381,12 @@ Migration converts the Volta `volta.node` pin to `.node-version`, because Vite+ **Other rules.** +- If `pnpm-workspace.yaml` enables `trustPolicy: no-downgrade`, migration adds + `tsrs@0.25.0-ts7.1.0-beta` to `trustPolicyExclude`. This reviewed release uses + trusted publishing with provenance, but pnpm ranks its staged bootstrap + release higher ([upstream issue](https://github.com/oxc-project/tsrs/issues/945)). + Existing exceptions are preserved. Other packages and future tsrs versions + remain subject to the policy. Run `vp migrate` if this policy blocks an upgrade. - Packages that use `vite-plus` without a direct `vite` declaration keep that layout (see [Vite and Overrides](#vite-and-overrides)). - Unrelated selector-shaped and object-valued overrides are preserved. diff --git a/packages/cli/src/migration/__tests__/migrator.spec.ts b/packages/cli/src/migration/__tests__/migrator.spec.ts index d89bacb9ff..97fa64b689 100644 --- a/packages/cli/src/migration/__tests__/migrator.spec.ts +++ b/packages/cli/src/migration/__tests__/migrator.spec.ts @@ -4517,6 +4517,77 @@ describe('ensureVitePlusBootstrap', () => { expect(workspace.minimumReleaseAgeExclude).toBeUndefined(); }); + it('reconciles the reviewed tsrs exception without disabling pnpm trust checks', () => { + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ + name: 'test', + devDependencies: { 'vite-plus': 'latest' }, + packageManager: 'pnpm@11.22.0', + }), + ); + const workspaceInfo = makeWorkspaceInfo(tmpDir, PackageManager.pnpm); + ensureVitePlusBootstrap(workspaceInfo); + expect(detectVitePlusBootstrapPending(tmpDir, PackageManager.pnpm)).toBe(false); + + const workspacePath = path.join(tmpDir, 'pnpm-workspace.yaml'); + fs.appendFileSync( + workspacePath, + '\ntrustPolicy: no-downgrade\ntrustPolicyExclude:\n # Keep the project exception.\n - example@1.0.0\n', + ); + expect(detectVitePlusBootstrapPending(tmpDir, PackageManager.pnpm)).toBe(true); + expect(ensureVitePlusBootstrap(workspaceInfo).changed).toBe(true); + + const workspace = readYamlObject(workspacePath); + expect(workspace.trustPolicy).toBe('no-downgrade'); + expect(workspace.trustPolicyExclude).toEqual(['example@1.0.0', 'tsrs@0.25.0-ts7.1.0-beta']); + expect(workspace.trustLockfile).toBeUndefined(); + const after = fs.readFileSync(workspacePath, 'utf-8'); + expect(after).toContain('# Keep the project exception.'); + expect(after).toContain('https://github.com/oxc-project/tsrs/issues/945'); + expect(detectVitePlusBootstrapPending(tmpDir, PackageManager.pnpm)).toBe(false); + expect(ensureVitePlusBootstrap(workspaceInfo).changed).toBe(false); + expect(fs.readFileSync(workspacePath, 'utf-8')).toBe(after); + }); + + it.each(['', 'trustPolicy: off\n'])( + 'leaves trust exceptions unchanged without no-downgrade: %j', + (policy) => { + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ name: 'test', devDependencies: { 'vite-plus': 'latest' } }), + ); + const workspacePath = path.join(tmpDir, 'pnpm-workspace.yaml'); + fs.writeFileSync(workspacePath, `${policy}trustPolicyExclude:\n - example@1.0.0\n`); + + ensureVitePlusBootstrap(makeWorkspaceInfo(tmpDir, PackageManager.pnpm)); + + const workspace = readYamlObject(workspacePath); + expect(workspace.trustPolicyExclude).toEqual(['example@1.0.0']); + expect(workspace.trustPolicy).toBe(policy ? 'off' : undefined); + }, + ); + + it.each(['tsrs', 'tsrs@0.25.0-ts7.1.0-beta'])( + 'preserves an existing tsrs trust exception: %s', + (exception) => { + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ name: 'test', devDependencies: { 'vite-plus': 'latest' } }), + ); + const workspacePath = path.join(tmpDir, 'pnpm-workspace.yaml'); + fs.writeFileSync( + workspacePath, + `trustPolicy: no-downgrade\ntrustPolicyExclude:\n - ${exception}\n`, + ); + + ensureVitePlusBootstrap(makeWorkspaceInfo(tmpDir, PackageManager.pnpm)); + + expect(readYamlObject(workspacePath).trustPolicyExclude).toEqual([exception]); + expect(detectVitePlusBootstrapPending(tmpDir, PackageManager.pnpm)).toBe(false); + }, + ); + it('merges the vitest family into an existing yarn npmPreapprovedPackages list', () => { fs.writeFileSync( path.join(tmpDir, 'package.json'), diff --git a/packages/cli/src/migration/migrator/catalog.ts b/packages/cli/src/migration/migrator/catalog.ts index 254ebc0514..1e76446ffd 100644 --- a/packages/cli/src/migration/migrator/catalog.ts +++ b/packages/cli/src/migration/migrator/catalog.ts @@ -76,6 +76,17 @@ const PNPM_MINIMUM_RELEASE_AGE_EXCLUDES = [ ...VITEST_AGE_GATE_EXEMPT_PACKAGES, ] as const; +// The bootstrap placeholder used staged publishing; the real release uses +// GitHub trusted publishing with provenance. pnpm ranks the former higher. +// Keep this reviewed exception version-specific: dependency updates must not +// automatically exempt future releases or the @tsrs/* platform packages. +// https://github.com/oxc-project/tsrs/issues/945 +const PNPM_TSRS_TRUST_POLICY_EXCLUDE = 'tsrs@0.25.0-ts7.1.0-beta'; + +function hasTsrsTrustPolicyException(excludes: readonly unknown[]): boolean { + return excludes.includes('tsrs') || excludes.includes(PNPM_TSRS_TRUST_POLICY_EXCLUDE); +} + // Whether `version` is at least `minVersion`. A non-coercible version passes // only when it is one of the given dist-tags, which always point at a current // (feature-capable) release. @@ -454,6 +465,22 @@ export function rewritePnpmWorkspaceYaml( } doc.setIn(['minimumReleaseAgeExclude'], minimumReleaseAgeExclude); } + + if (doc.get('trustPolicy') === 'no-downgrade') { + const node = doc.get('trustPolicyExclude'); + if (node != null && !(node instanceof YAMLSeq)) { + throw new Error('pnpm-workspace.yaml: trustPolicyExclude must be a sequence'); + } + const excludes = node ?? new YAMLSeq>(); + if (!hasTsrsTrustPolicyException(excludes.toJSON())) { + const entry = scalarString(PNPM_TSRS_TRUST_POLICY_EXCLUDE); + entry.commentBefore = + ' Reviewed OIDC release; the bootstrap placeholder used staged publishing.\n' + + ' https://github.com/oxc-project/tsrs/issues/945'; + excludes.add(entry); + doc.set('trustPolicyExclude', excludes); + } + } }); } @@ -1408,6 +1435,25 @@ export function pnpmWorkspaceMinimumReleaseAgeExemptionsPending(projectPath: str return PNPM_MINIMUM_RELEASE_AGE_EXCLUDES.some((exclude) => !existing.has(exclude)); } +// Reconcile the exception even when the project already uses Vite+ and its +// other migration settings are current. Mirror the writer's policy gate. +export function pnpmWorkspaceTsrsTrustPolicyExceptionPending(projectPath: string): boolean { + const workspacePath = path.join(projectPath, 'pnpm-workspace.yaml'); + if (!fs.existsSync(workspacePath)) { + return false; + } + const doc = readYamlFile(workspacePath) as { + trustPolicy?: unknown; + trustPolicyExclude?: unknown; + } | null; + return ( + doc?.trustPolicy === 'no-downgrade' && + !hasTsrsTrustPolicyException( + Array.isArray(doc.trustPolicyExclude) ? doc.trustPolicyExclude : [], + ) + ); +} + export function readPnpmWorkspacePeerDependencyRules( projectPath: string, ): { allowAny?: string[]; allowedVersions?: Record } | undefined { diff --git a/packages/cli/src/migration/migrator/vite-plus-bootstrap.ts b/packages/cli/src/migration/migrator/vite-plus-bootstrap.ts index 5a4197d669..14def720cb 100644 --- a/packages/cli/src/migration/migrator/vite-plus-bootstrap.ts +++ b/packages/cli/src/migration/migrator/vite-plus-bootstrap.ts @@ -34,6 +34,7 @@ import { supportsCatalog, bunSupportsCatalog, pnpmWorkspaceMinimumReleaseAgeExemptionsPending, + pnpmWorkspaceTsrsTrustPolicyExceptionPending, projectUsesVitestDirectly, pruneLegacyWrapperAliases, readBunCatalogDependencyResolver, @@ -789,7 +790,8 @@ export function detectVitePlusBootstrapPending( readPnpmWorkspacePeerDependencyRules(projectPath), usesVitest, ) || - pnpmWorkspaceMinimumReleaseAgeExemptionsPending(projectPath) + pnpmWorkspaceMinimumReleaseAgeExemptionsPending(projectPath) || + pnpmWorkspaceTsrsTrustPolicyExceptionPending(projectPath) ); } @@ -1194,6 +1196,7 @@ export function ensureVitePlusBootstrap( ecosystemCatalogReferencesPending || !pnpmWorkspaceExoticSubdepsSettingSatisfied(projectPath) || pnpmWorkspaceMinimumReleaseAgeExemptionsPending(projectPath) || + pnpmWorkspaceTsrsTrustPolicyExceptionPending(projectPath) || workspaceCatalogVitePlusDependencyPending( projectPath, workspaceInfo.packages, diff --git a/rfcs/tsrs-lint-backend.md b/rfcs/tsrs-lint-backend.md index 2806353f61..212e6c7902 100644 --- a/rfcs/tsrs-lint-backend.md +++ b/rfcs/tsrs-lint-backend.md @@ -37,6 +37,11 @@ The project's `typescript` dependency and standalone `tsc` tasks are unchanged. report additional errors. Projects control compiler scope through `tsconfig.json`. - tsrs `0.25.0` provides native packages for all Vite+ targets, including Windows ARM64. The same backend runs on every supported platform. +- pnpm's `trustPolicy: no-downgrade` rejects tsrs because its bootstrap release + used staged publishing ([upstream issue](https://github.com/oxc-project/tsrs/issues/945)). + `vp migrate` adds the reviewed `tsrs@0.25.0-ts7.1.0-beta` to `trustPolicyExclude` + when that policy is enabled in `pnpm-workspace.yaml`. Other packages and future + tsrs versions remain subject to the policy. New versions require a separate review. The [benchmark report](../bench/tsrs-lint.md) contains historical timing, memory, and disk-usage measurements. Refresh them for the release candidate. From 25a7c006af2db834bfc66f9689b02de1060f580f Mon Sep 17 00:00:00 2001 From: MK Date: Thu, 12 Nov 2026 17:02:09 +0800 Subject: [PATCH 23/25] chore(bench): keep tsgolint in root development dependencies --- bench/tsrs-lint.ts | 6 ++---- package.json | 1 + packages/cli/package.json | 1 - pnpm-lock.yaml | 6 +++--- rfcs/tsrs-lint-backend.md | 3 ++- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/bench/tsrs-lint.ts b/bench/tsrs-lint.ts index 5dfb3a42da..2b234afe16 100644 --- a/bench/tsrs-lint.ts +++ b/bench/tsrs-lint.ts @@ -11,13 +11,11 @@ if (!tsrs) { throw new Error('Set OXLINT_TSGOLINT_PATH to the tsrs-lint executable.'); } const vp = fileURLToPath(new URL('../packages/cli/bin/vp', import.meta.url)); -const require = createRequire(new URL('../packages/cli/package.json', import.meta.url)); +const require = createRequire(import.meta.url); const tsgolint = process.platform === 'win32' ? ['tsgolint.exe', 'tsgolint.cmd'] - .map((bin) => - fileURLToPath(new URL(`../packages/cli/node_modules/.bin/${bin}`, import.meta.url)), - ) + .map((bin) => fileURLToPath(new URL(`../node_modules/.bin/${bin}`, import.meta.url))) .find(existsSync) : require.resolve('oxlint-tsgolint/bin/tsgolint'); assert.ok(tsgolint, 'Install workspace development dependencies to get the Go baseline.'); diff --git a/package.json b/package.json index fd4ce34eb3..ccba8f90ed 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@types/node": "catalog:", "@typescript/native-preview": "catalog:", "@voidzero-dev/vite-plus-tools": "workspace:*", + "oxlint-tsgolint": "catalog:", "playwright": "catalog:", "typescript": "catalog:", "vite": "catalog:", diff --git a/packages/cli/package.json b/packages/cli/package.json index 1f7950851e..4110dfb271 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -388,7 +388,6 @@ "lint-staged": "catalog:", "minimatch": "catalog:", "nanotar": "catalog:", - "oxlint-tsgolint": "catalog:", "picocolors": "catalog:", "pirates": "catalog:", "tinyglobby": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3059746e3..3665918e1c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -412,6 +412,9 @@ importers: '@voidzero-dev/vite-plus-tools': specifier: workspace:* version: link:packages/tools + oxlint-tsgolint: + specifier: 'catalog:' + version: 7.0.2006 playwright: specifier: 'catalog:' version: 1.62.1 @@ -536,9 +539,6 @@ importers: nanotar: specifier: 'catalog:' version: 0.3.0 - oxlint-tsgolint: - specifier: 'catalog:' - version: 7.0.2006 picocolors: specifier: 'catalog:' version: 1.1.1 diff --git a/rfcs/tsrs-lint-backend.md b/rfcs/tsrs-lint-backend.md index 212e6c7902..72f7900f85 100644 --- a/rfcs/tsrs-lint-backend.md +++ b/rfcs/tsrs-lint-backend.md @@ -26,7 +26,8 @@ and toolchain metadata identify `tsrs`. | Development-only backend | None | `oxlint-tsgolint@7.0.2006` for comparisons | | Backend platform packages | `@oxlint-tsgolint/*`: six optional packages | `@tsrs/*`: eight optional packages | -Production installs use tsrs; repository development retains Go for comparisons. +Production installs use tsrs. The root `package.json` retains `oxlint-tsgolint` +as a development dependency for `bench/tsrs-lint.ts`. The project's `typescript` dependency and standalone `tsc` tasks are unchanged. ## Compatibility and release considerations From 68eb84008fd27a72a5e9426ad64636432385b0e1 Mon Sep 17 00:00:00 2001 From: MK Date: Thu, 12 Nov 2026 18:06:11 +0800 Subject: [PATCH 24/25] docs(bench): report complete fork checks with latest tsrs preview --- bench/tsrs-lint-ecosystem.md | 662 +++++++++++++++-------------------- bench/tsrs-lint.md | 12 +- 2 files changed, 294 insertions(+), 380 deletions(-) diff --git a/bench/tsrs-lint-ecosystem.md b/bench/tsrs-lint-ecosystem.md index 2d4cf11583..7ea37392b1 100644 --- a/bench/tsrs-lint-ecosystem.md +++ b/bench/tsrs-lint-ecosystem.md @@ -1,381 +1,295 @@ # Ecosystem correctness comparison -The `tsrs@0.22.0-ts7.1.0-beta` experiment is not diagnostically equivalent to -`oxlint-tsgolint@7.0.2005`. The representative sweep found new type-check errors in -Hono and Vue Core, and a DOM-library compatibility change in OneSignal. The -exact-version audit below attributes all four reproduced categories to the pinned -TypeScript `7.1.0-beta` behavior. It found no Rust-port defect in these cases. -The default switch still changes diagnostics for projects that use the older backend. - -On 2026-11-06, a Linux x64/glibc Codespace ran 141 commands across 14 pinned forks -from the [ecosystem catalog](https://github.com/vite-plus-ecosystem-ci/.github). -The explicit type-check workload covered 2,289 files. No command timed out or -terminated by a signal. Seven repositories had identical normalized diagnostics; -seven had differences. This covers the selected repositories, not all 83 catalog entries. - -## Version 0.24 upgrade check (2026-11-11) - -The current integration pins `tsrs@0.24.0-ts7.1.0-beta`. The full ecosystem sweep -and performance measurements remain results for `0.22.0`. - -On macOS ARM64, all seven reduced cases retained identical diagnostics and exit -codes between `0.23.0` and `0.24.0`, both through `vp lint` and the standalone -compiler. The standalone results also matched `typescript@7.1.0-beta`. -The TypeScript source pin is unchanged, and the four compatibility follow-ups -remain open. - -The [0.24 release](https://github.com/oxc-project/tsrs/blob/v0.24.0/docs/releases/0.24.0.md) -checks whole programs before running lint rules. The existing smoke fixture still -reports the ignored program file's `TS2322` only when type checking is enabled. -Its invalid declaration remains suppressed by `skipLibCheck: true`. - -After rebasing onto `main` at `9f44ce760`, `vp check` passes, and the unit suite -passes 3,422 tests with one skipped. This check used Node.js `24.22.2`, Oxlint -`1.93.0`, and fresh CLI JavaScript built with published Rolldown `1.2.18`. -The native binding and core artifacts remain from published `1.5.0`. -This is not a full native release build or a repeat of the 14-repository sweep. -Scripts, fixtures, and raw logs remain outside the repository. - -## Version 0.23 upgrade check (2026-11-09) - -This upgrade pinned `tsrs@0.23.0-ts7.1.0-beta`. The full ecosystem sweep -and performance measurements remain results for `0.22.0`. - -On macOS ARM64, all seven reduced cases retained identical diagnostics and exit -codes between `0.22.0` and `0.23.0`, both through `vp lint` and the standalone -compiler. The standalone results also matched `typescript@7.1.0-beta`. -The TypeScript source pin remains `3e9236361fa97a9e6c685f4833ac457ae2d8c50a`. -The four existing compatibility follow-ups remain open. - -The [0.23 release](https://github.com/oxc-project/tsrs/blob/v0.23.0/docs/releases/0.23.0.md) -expands `--type-check` to whole programs. A smoke fixture confirmed that selecting -a clean file now reports `TS2322` in an ignored file from the same program. -Type-aware linting without `--type-check` still passes that fixture. A separate -invalid declaration remains suppressed by `skipLibCheck: true`. - -Whole-program checking initially exposed seven errors in three nested project -configs. With those original configs, `0.22.0` and `oxlint-tsgolint@7.0.2005` pass -the lint/type-check phase because they do not report these dependency errors: - -| Dependency | New diagnostics | -| ----------------------------------------------------- | -------------------------------------------------------------- | -| `@voidzero-dev/vitepress-theme@5.0.7` | One `TS2614` for the `Sponsors` export from a Vue file | -| `zod@4.6.5` | One `TS5115` for recursive JSON schema types | -| `webpack-virtual-modules@0.6.2` and `unplugin@2.3.11` | Five `TS2307` errors for missing optional bundler declarations | - -Standalone `typescript@7.1.0-beta` reproduces all seven errors when invoked against -the same nested configs: `packages/cli/templates/generator/tsconfig.json`, -`packages/cli/templates/monorepo/tsconfig.json`, and `docs/.vitepress/tsconfig.json`. -The root-only compiler check uses different file selection and compiler options; -its success did not establish a tsrs defect. - -Both starter templates now set `skipLibCheck: true`, consistent with the repository -root, so they do not check third-party declarations. The docs shim now declares -the named `Sponsors` type exported by `VPSponsors.vue`. It reuses the theme's -`SponsorTier` fields with an optional `tier`, matching the component's interface. - -After these corrections, `vp check` and all three nested TypeScript checks pass. -Negative probes still reject source type errors and invalid sponsor fields, and -unrelated Vue components do not acquire the named export. The rebuilt CLI's -3,403 unit tests also pass, with one skipped. These seven errors required project -configuration and Vue declaration fixes; the four earlier compiler-version -compatibility follow-ups remain open. - -This check used the rebased branch on `main` at `92edd550c`, Node.js `24.22.2`, -Oxlint `1.91.0`, and fresh CLI JavaScript. The native binding and core artifacts -remain from published `1.5.0`; the JS build used published Rolldown `1.2.16`. -This is not a full native release build or a repeat of the 14-repository sweep. -Scripts, fixtures, and raw logs remain outside the repository. - -## Exact-upstream attribution (2026-11-08) - -The original `6.0.3`, `7.0.2`, and `oxlint-tsgolint@7.0.2005` controls establish -upgrade compatibility, but cannot identify a Rust-port defect by themselves. -The [tsrs compatibility contract](https://github.com/oxc-project/tsrs/blob/625f918c85a3e12824ede39cd127d0f866b8d9c8/docs/COMPATIBILITY.md#conformance) -defines correctness against its exact upstream Go compiler, including upstream bugs. - -This audit compared the retained tsrs executable with published `typescript@7.1.0-beta`. -Both target `microsoft/TypeScript@3e9236361fa97a9e6c685f4833ac457ae2d8c50a`. -The reference wrapper and native package both identify that commit in `gitHead`. -The Rust source audit used release tag `v0.22.0`, commit -`625f918c85a3e12824ede39cd127d0f866b8d9c8`. - -All seven reduced cases produced identical diagnostic output and exit codes: - -| Reduced case | TypeScript `7.1.0-beta` | tsrs `0.22.0` | -| ----------------------------------------------- | ------------------------------- | ------------- | -| Hono default destructuring | Two `TS7031`, exit `2` | Identical | -| Hono explicit parameter annotation | No errors, exit `0` | Identical | -| Vue mapped-parameter destructuring | Three `TS7031`, exit `2` | Identical | -| Vue selector iteration | `TS5115` and `TS7006`, exit `2` | Identical | -| Vue conditional return, reduced | `TS2322`, exit `2` | Identical | -| Vue conditional return, original implementation | `TS2322`, exit `2` | Identical | -| DOM `maxActions` probe | `TS2322`, exit `2` | Identical | - -The four categories require different interpretations: - -| Category | Assessment | Follow-up | -| ----------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| Default destructuring | Upstream TypeScript `7.1` false positive; tsrs matches it | Follow TypeScript #64974; an explicit parameter annotation passes | -| Conditional return type | Upstream `7.1` compatibility regression; no Rust divergence | Investigate the related conditional-type normalization fix in TypeScript #64307 | -| Selector recursion | Upstream `7.1` recursion restriction exposes the dependency's circular type construction | Rework or update the selector declarations; do not assume the older acceptance proves validity | -| Notification mock | Correct enforcement of the newer DOM declaration | Add the missing static member to the mock | - -Matching the pinned compiler does not mean every upstream diagnostic is desirable. -It means these reproductions do not justify changing the Rust port independently. -The new default also adopts a newer TypeScript version, so its compatibility cost -must be assessed separately from the implementation language. - -The 14 compiler invocations ran in Codespaces job `job-3aecfcc9bb6f49bb`. -The input files and compiler options were unchanged from the earlier reductions. -Raw output, executable/version evidence, package-lock hashes, and scripts remain -outside this repository. This follow-up did not repeat the full 14-repository sweep. - -## Controls and method - -The comparison used three toolchains: - -| Control | Published Vite+ package | Native lint backend | -| ----------------------- | ------------------------------------------------------- | -------------------------- | -| Stable | `1.5.0` | `oxlint-tsgolint@7.0.2005` | -| Preview with Go control | `0.0.0-commit.4fcf99edbe42fe3305dcd41dc884a4c4b7f5fd45` | `oxlint-tsgolint@7.0.2005` | -| Default preview | Same preview | `tsrs@0.22.0-ts7.1.0-beta` | - -All controls used Oxlint `1.91.0`. Stable and preview with Go produced identical -normalized lint and TypeScript diagnostics in every selected repository. This -control separates backend differences from other preview changes. - -Each project retained its original frozen lockfile, application dependencies, and -source configuration. The runner temporarily linked the published CLI, Vite, -Oxlint, and Oxfmt into workspace importers, then restored those links. Tracked -source files remained unchanged. The setup used each project's pinned package -manager, with lifecycle scripts disabled. Node.js was `24.22.2`, except -`eslint-plugin-oxlint` and `react-data-grid`, which used `26.13.0` to meet their engine requirements. - -Each control ran these commands: +On 2026-11-12, the local comparison covered **all 83 catalog forks plus vinext** with +the latest preview built from PR #3352 at `25a7c006af2db834bfc66f9689b02de1060f580f`. +The 740 commands found six repositories where a command passes with Go and fails +with tsrs. Four of those repositories also change from pass to fail for `vp check`. +The full results and exact fork commits are listed below. + +This is a correctness comparison on macOS ARM64. Two repository batches ran +concurrently; their elapsed times are not performance or memory measurements. + +## Tested toolchain + +| Component | Version / source | +| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Published preview | `0.0.0-commit.25a7c006af2db834bfc66f9689b02de1060f580f` | +| Preview build | [Build run](https://github.com/voidzero-dev/vite-plus/actions/runs/43190612524), [publication run](https://github.com/voidzero-dev/vite-plus/actions/runs/43194215156) | +| Oxlint / Oxfmt | `1.93.0` / `0.78.0` | +| Go control | `oxlint-tsgolint@7.0.2006` | +| Default backend | `tsrs@0.25.0-ts7.1.0-beta` | +| TypeScript reference | `7.1.0-beta`, commit `3e9236361fa97a9e6c685f4833ac457ae2d8c50a` | +| Machine | Apple M4 Max, 48 GiB RAM, macOS `27.0.1`, ARM64 | +| Node.js | `24.22.2` or `26.13.0`, according to the project engine requirement | +| Fork catalog | [83 entries at `5a74ee011`](https://github.com/vite-plus-ecosystem-ci/.github/blob/5a74ee011971dfcb4545b08b740a4d713dc90290/ecosystem.json), plus the vinext fork | + +Both controls use the same published preview, application commit, installed +dependencies, generated files, and command flags. Only the lint backend changes. +The fork commits and dependency trees are retained from the November 11 local run +to isolate the preview update. The current catalog has the same 83 repository names. + +## Exit-status summary + +| Workload | Both pass | Both fail | Go passes, tsrs fails | Go fails, tsrs passes | +| ----------------------------------- | --------: | --------: | --------------------: | --------------------: | +| `vp lint` | 46 | 33 | 5 | 0 | +| `vp lint --type-aware` | 32 | 47 | 5 | 0 | +| `vp lint --type-aware --type-check` | 17 | 61 | 6 | 0 | +| `vp check` | 28 | 52 | 4 | 0 | +| Declared project type-check task | 15 | 19 | 0 | 0 | + +`Both fail` includes configuration, formatting, dependency, and source errors. It does not identify a backend regression. + +## New command failures + +These are compiler diagnostics. All five default-lint cases already enable +`typeCheck` in their configuration. `yielded-auth` passes default lint with both +backends and fails only when the command explicitly enables type checking. + +| Repository | First failing workload | Errors, Go / tsrs | Cause and follow-up | +| --------------- | ---------------------- | ----------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| css-modules-kit | Default lint | 0 / 3 | `TS2307` in three CSS-module fixture imports. Lint ignore patterns omit the fixtures, but whole-program checking still includes them. Review the fixture declarations or their TypeScript project boundaries. | +| elysia-clerk | Default lint | 0 / 4 | Two `TS2868` errors for `Bun` and two `TS2339` errors for `ImportMeta.dir` in the ignored `dev` directory. Add the intended Bun types or exclude this separate development program. | +| nostalgist | Default lint | 0 / 1 | `TS5115` in `zod@4.6.5` declarations. Follow up on the circular type construction with the dependency. | +| ramen-api | Default lint | 0 / 118 | Generated Cloudflare Worker declarations conflict with Node globals, plus Hono and Node declaration errors. Align the runtime declaration sets and review the affected dependency types. | +| vinext | Default lint | 0 / 17 | The ignored Sentry fixture reports 11 `TS2578`, four `TS2339`, one `TS7006`, and one `TS7016`. Exclude the separately checked fixture from the root TypeScript project. | +| yielded-auth | Explicit type check | 0 / 5 | Five `TS2339` errors in the docs dependency `starlight-links-validator`. Review its declared API types. | + +`vp check` changes from pass to fail in css-modules-kit, nostalgist, ramen-api, and +vinext. Elysia-clerk stops at formatting with both controls. + +All 148 errors above match the exact upstream TypeScript reference at the same +file, line, column, and diagnostic code. Standalone tsrs and TypeScript also produce +identical error headers for all 8 selected programs. Vinext's standalone root +check covers more files than lint and reports 241 errors with either compiler; +these include all 17 new lint errors. + +These six checks found no tsrs implementation divergence from its pinned compiler. +They still identify upgrade compatibility work: whole-program type checking can +report errors in lint-ignored fixtures and imported declarations. The additional +diagnostics in already-failing projects, listed below, have not all received this +independent compiler audit. + +## npmx.dev and vinext + +For the pinned npmx.dev fork, default lint passes with both backends. `vp check` +stops on the same eight formatting files. Explicit type checking reports 260 errors +with Go and 273 with tsrs; both fail. Its `test:types` task also fails under both +controls with two Vite/Vitest configuration-type errors. + +The tested vinext input is the unmodified catalog-org fork at `42d940ece`. +It does not contain the `tsconfig.exclude` fix in +[the separate vinext draft PR](https://github.com/fengmk2/vinext/pull/40). +That PR's [successful Check run](https://github.com/fengmk2/vinext/actions/runs/43164787804/job/131004010311) +uses a different commit and earlier preview; it is separate evidence from this sweep. + +## Complete fork results + +Each exit-code pair is **Go / tsrs**: `0` means success; `1` or `2` means failure. All source links pin the tested commit. +The error column counts error-severity diagnostics in the explicit type-check workload; warnings are excluded. Equal counts do not establish identical diagnostics. +`—` means that no suitable declared type-check task was found. Tasks retain their own compiler selection; switching the lint backend does not replace `tsc`, `vue-tsc`, or `svelte-check`. + +| Repository / commit | Lint | Type-aware lint | Explicit type check | Type-check errors | `vp check` | Project task / exits | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | --------------- | ------------------- | ----------------: | ---------- | -------------------- | +| [agentsview `4a9d932b2`](https://github.com/vite-plus-ecosystem-ci/agentsview/tree/4a9d932b2b41ee6ee8d30e215b99b258c61c3163) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | `check` 0 / 0 | +| [anywidget `35160ac08`](https://github.com/vite-plus-ecosystem-ci/anywidget/tree/35160ac08e75918faee008a3b8996ab2a0a16a46) | 1 / 1 | 1 / 1 | 1 / 1 | 14 / 14 | 1 / 1 | — | +| [arrow-hero `e6dedbefb`](https://github.com/vite-plus-ecosystem-ci/arrow-hero/tree/e6dedbefb94f9e93b3a8c8d9479a94b2b0c0d45a) | 0 / 0 | 1 / 1 | 1 / 1 | 6 / 6 | 0 / 0 | — | +| [better-ajv-errors `03ce620b1`](https://github.com/vite-plus-ecosystem-ci/better-ajv-errors/tree/03ce620b155357aa2968459bf754949b7e07e3e5) | 1 / 1 | 1 / 1 | 1 / 1 | 1 / 1 | 1 / 1 | — | +| [bippy `fbafc407c`](https://github.com/vite-plus-ecosystem-ci/bippy/tree/fbafc407ca31ad82f391404c6c6539008f9e0d79) | 0 / 0 | 0 / 0 | 1 / 1 | 257 / 257 | 0 / 0 | `typecheck` 0 / 0 | +| [brazilian-utils-javascript `0f0f9a153`](https://github.com/vite-plus-ecosystem-ci/brazilian-utils-javascript/tree/0f0f9a1539d7263a08e245e9b03f8f7dffc7cd9e) | 1 / 1 | 1 / 1 | 1 / 1 | Config blocked | 1 / 1 | — | +| [cloudflare-cf `e2c720597`](https://github.com/vite-plus-ecosystem-ci/cloudflare-cf/tree/e2c720597887fb742e52cbe79132e4bdfec92f16) | 0 / 0 | 0 / 0 | 1 / 1 | 41 / 41 | 0 / 0 | `check:type` 1 / 1 | +| [cloudflare-os `1541cc07f`](https://github.com/vite-plus-ecosystem-ci/cloudflare-os/tree/1541cc07fc47c1beafeb98cd9927bb3f112d523f) | 0 / 0 | 1 / 1 | 1 / 1 | 8112 / 8119 | 0 / 0 | — | +| [codiff `2167e44b3`](https://github.com/vite-plus-ecosystem-ci/codiff/tree/2167e44b3a362480d8fca47a7fefcf98f8a24a27) | 1 / 1 | 1 / 1 | 1 / 1 | 53 / 344 | 1 / 1 | — | +| [Cookies-EU-banner `1193c4c41`](https://github.com/vite-plus-ecosystem-ci/Cookies-EU-banner/tree/1193c4c41a91f26d722c680564bc9f0af05903ed) | 1 / 1 | 1 / 1 | 1 / 1 | 6 / 6 | 1 / 1 | — | +| [critical `03897c82e`](https://github.com/vite-plus-ecosystem-ci/critical/tree/03897c82e255416ca7104774a9d19142bd1e2744) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | — | +| [crust `f007d2d0d`](https://github.com/vite-plus-ecosystem-ci/crust/tree/f007d2d0d3d6ca030a39cb3d1d978c707115b01d) | 1 / 1 | 1 / 1 | 1 / 1 | 1173 / 1173 | 1 / 1 | `check:types` 1 / 1 | +| [cs-demo-manager `164e48745`](https://github.com/vite-plus-ecosystem-ci/cs-demo-manager/tree/164e487451ed24478a02c6225f558cf0268efa65) | 1 / 1 | 1 / 1 | 1 / 1 | 3 / 3 | 1 / 1 | `compile` 0 / 0 | +| [css-modules-kit `1dec592b9`](https://github.com/vite-plus-ecosystem-ci/css-modules-kit/tree/1dec592b930c6b65cf15169dc85cbabdd44ef64f) | 0 / 1 | 0 / 1 | 0 / 1 | 0 / 3 | 0 / 1 | — | +| [delta-comic `d2b007579`](https://github.com/vite-plus-ecosystem-ci/delta-comic/tree/d2b007579b703f9d1561e4bd1b39cd7e7c17b6ab) | 0 / 0 | 1 / 1 | 1 / 1 | 288 / 288 | 1 / 1 | `typecheck` 1 / 1 | +| [digital-signature `2d6e49c24`](https://github.com/vite-plus-ecosystem-ci/digital-signature/tree/2d6e49c24448fca9929bfa1973c29bca09bebdc8) | 0 / 0 | 0 / 0 | 1 / 1 | 1 / 1 | 0 / 0 | `check` 0 / 0 | +| [docx-editor `2457e2765`](https://github.com/vite-plus-ecosystem-ci/docx-editor/tree/2457e2765d76e231cff7c87a2b2993cb6071e668) | 1 / 1 | 1 / 1 | 1 / 1 | 2580 / 2555 | 1 / 1 | `type-check` 2 / 2 | +| [dotabod-backend `65d80c3ef`](https://github.com/vite-plus-ecosystem-ci/dotabod-backend/tree/65d80c3ef8168150f5a32c8586ec136a2d877e01) | 1 / 1 | 1 / 1 | 1 / 1 | 5379 / 5381 | 1 / 1 | `typecheck` 1 / 1 | +| [dotabod-frontend `e06184067`](https://github.com/vite-plus-ecosystem-ci/dotabod-frontend/tree/e06184067abf0f97c8f74abf6e992638bc50b90d) | 1 / 1 | 1 / 1 | 1 / 1 | 6774 / 6774 | 1 / 1 | `typecheck` 0 / 0 | +| [electron-vite-template `4b1f4d64b`](https://github.com/vite-plus-ecosystem-ci/electron-vite-template/tree/4b1f4d64b36bab216391668efc24f12598553628) | 0 / 0 | 0 / 0 | 1 / 1 | 7 / 7 | 1 / 1 | — | +| [elysia-clerk `6e42ab83b`](https://github.com/vite-plus-ecosystem-ci/elysia-clerk/tree/6e42ab83b4fd8401f5ddb13ae206cbf4887df469) | 0 / 1 | 0 / 1 | 0 / 1 | 0 / 4 | 1 / 1 | — | +| [eslint-plugin-oxlint `4eab8b2d8`](https://github.com/vite-plus-ecosystem-ci/eslint-plugin-oxlint/tree/4eab8b2d87d80c508247209fde18e749160bf840) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | — | +| [f3-app `b03939e5c`](https://github.com/vite-plus-ecosystem-ci/f3-app/tree/b03939e5c9ac280dcc96454a69c950dec9bcd243) | 1 / 1 | 1 / 1 | 1 / 1 | 9 / 11 | 1 / 1 | — | +| [faasjs `813638437`](https://github.com/vite-plus-ecosystem-ci/faasjs/tree/813638437070cd8f30bec0b93d2c730903db6579) | 1 / 1 | 1 / 1 | 1 / 1 | 78 / 78 | 1 / 1 | — | +| [fate `97a730445`](https://github.com/vite-plus-ecosystem-ci/fate/tree/97a73044585c748b5a23be8f363ba6a6bb606295) | 1 / 1 | 1 / 1 | 1 / 1 | 100 / 100 | 1 / 1 | — | +| [gitify `3a843faed`](https://github.com/vite-plus-ecosystem-ci/gitify/tree/3a843faedca74e64dfadb82cfc8355c0f58a22ed) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | — | +| [graph-explorer `7a696be53`](https://github.com/vite-plus-ecosystem-ci/graph-explorer/tree/7a696be53875c4241826b3e55ce1e0b4d721ce62) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | — | +| [hono `61f7c59e5`](https://github.com/vite-plus-ecosystem-ci/hono/tree/61f7c59e50244ab4d2dd83c151928cf7ea448510) | 0 / 0 | 1 / 1 | 1 / 1 | 419 / 421 | 1 / 1 | — | +| [Ivy-Framework `f6b1d065f`](https://github.com/vite-plus-ecosystem-ci/Ivy-Framework/tree/f6b1d065f725d4fc27ec99b69e773750920f4145) | 0 / 0 | 0 / 0 | 1 / 1 | 1 / 1 | 0 / 0 | — | +| [kaneo `ea9eb6d3d`](https://github.com/vite-plus-ecosystem-ci/kaneo/tree/ea9eb6d3d66cb4b216ee9f4314f089ea331b224b) | 0 / 0 | 1 / 1 | 1 / 1 | 1640 / 1640 | 0 / 0 | `typecheck` 1 / 1 | +| [local-studio `f65c8af78`](https://github.com/vite-plus-ecosystem-ci/local-studio/tree/f65c8af78582f741b62546c93d7c66439f0cd5b1) | 1 / 1 | 1 / 1 | 1 / 1 | 34 / 34 | 1 / 1 | `typecheck` 0 / 0 | +| [matterviz `91ce15d7b`](https://github.com/vite-plus-ecosystem-ci/matterviz/tree/91ce15d7b032252ee0939d04148d09e881f1a8b4) | 1 / 1 | 1 / 1 | 1 / 1 | 71 / 80 | 2 / 2 | `typecheck` 1 / 1 | +| [mcp-omnisearch `03bb264b8`](https://github.com/vite-plus-ecosystem-ci/mcp-omnisearch/tree/03bb264b8edd72dac96f45ffdfe582682eca96ee) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | — | +| [media-processors `79c87cd5c`](https://github.com/vite-plus-ecosystem-ci/media-processors/tree/79c87cd5c066a0a9abea979044c38c69603a85e8) | 1 / 1 | 1 / 1 | 1 / 1 | 91 / 91 | 1 / 1 | `typecheck` 1 / 1 | +| [museeks `7163f6019`](https://github.com/vite-plus-ecosystem-ci/museeks/tree/7163f6019a3184043c06352caeec0ed1bf6ccff5) | 1 / 1 | 1 / 1 | 1 / 1 | 73 / 73 | 1 / 1 | `test:typings` 2 / 2 | +| [nostalgist `436e23e4c`](https://github.com/vite-plus-ecosystem-ci/nostalgist/tree/436e23e4cac64e61e71825bcb1c001cf3728b736) | 0 / 1 | 0 / 1 | 0 / 1 | 0 / 1 | 0 / 1 | — | +| [notero `011be2ca9`](https://github.com/vite-plus-ecosystem-ci/notero/tree/011be2ca9ddc6b34326f7e5ce75089266cbf94bc) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 1 / 1 | `typecheck` 0 / 0 | +| [npmx.dev `ef6105e1e`](https://github.com/vite-plus-ecosystem-ci/npmx.dev/tree/ef6105e1e11d93b81f51c78c6ccb8b35fe8c19fe) | 0 / 0 | 1 / 1 | 1 / 1 | 260 / 273 | 1 / 1 | `test:types` 2 / 2 | +| [nshipster.com `ccdd0b5dc`](https://github.com/vite-plus-ecosystem-ci/nshipster.com/tree/ccdd0b5dc49100bdb2fce2fbfbdd12cff4414d8f) | 1 / 1 | 1 / 1 | 1 / 1 | 36 / 36 | 1 / 1 | `check` 1 / 1 | +| [ohbug `261f2807c`](https://github.com/vite-plus-ecosystem-ci/ohbug/tree/261f2807cfe94475bc5094a75a5a2e874dafa715) | 1 / 1 | 1 / 1 | 1 / 1 | 16 / 16 | 1 / 1 | — | +| [OneSignal-Website-SDK `836367e7f`](https://github.com/vite-plus-ecosystem-ci/OneSignal-Website-SDK/tree/836367e7fccf5420d4ef31cf750d21fdad76277a) | 1 / 1 | 1 / 1 | 1 / 1 | 1 / 2 | 1 / 1 | — | +| [oxc-walker `d35924c04`](https://github.com/vite-plus-ecosystem-ci/oxc-walker/tree/d35924c04f3cbd48ae53ba20f28933b508cfd244) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | `test:types` 0 / 0 | +| [oxlint-tailwindcss `7477ab484`](https://github.com/vite-plus-ecosystem-ci/oxlint-tailwindcss/tree/7477ab4849330e46802e8753af2cbb6968c58f3d) | 0 / 0 | 0 / 0 | 1 / 1 | 704 / 704 | 1 / 1 | `typecheck` 0 / 0 | +| [pinia-shared-state `1b1ec6109`](https://github.com/vite-plus-ecosystem-ci/pinia-shared-state/tree/1b1ec6109b6f46079e4ddb5aeb9feff1d7188c24) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | — | +| [pocket-id `8b5784206`](https://github.com/vite-plus-ecosystem-ci/pocket-id/tree/8b5784206b886f484caa98dd306f179b3a128b73) | 1 / 1 | 1 / 1 | 1 / 1 | 19 / 150 | 2 / 2 | — | +| [raindrop-mcp `20161f2ef`](https://github.com/vite-plus-ecosystem-ci/raindrop-mcp/tree/20161f2efeb38311002e5bed0b0ba320800a02b3) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | `type-check` 0 / 0 | +| [ramen-api `9e31be0d3`](https://github.com/vite-plus-ecosystem-ci/ramen-api/tree/9e31be0d3410948f6ee086c068c692d087ee07c7) | 0 / 1 | 0 / 1 | 0 / 1 | 0 / 118 | 0 / 1 | — | +| [react-data-grid `6f6469d88`](https://github.com/vite-plus-ecosystem-ci/react-data-grid/tree/6f6469d88dac7d7263862ef2d449b7b06d98e1f7) | 0 / 0 | 0 / 0 | 1 / 1 | 623 / 622 | 0 / 0 | `typecheck` 2 / 2 | +| [react-hookz-web `35944fbc4`](https://github.com/vite-plus-ecosystem-ci/react-hookz-web/tree/35944fbc4c8d5ab6c05a1c7f0b21a58b75d78fd6) | 1 / 1 | 1 / 1 | 1 / 1 | 22 / 22 | 1 / 1 | `typecheck` 0 / 0 | +| [react-native-onesignal `3fde3921d`](https://github.com/vite-plus-ecosystem-ci/react-native-onesignal/tree/3fde3921d22a030109987b615938ad8b53538586) | 1 / 1 | 1 / 1 | 1 / 1 | 2 / 2 | 1 / 1 | — | +| [react-useanimations `f7af6f53f`](https://github.com/vite-plus-ecosystem-ci/react-useanimations/tree/f7af6f53f6f1a97486ad95756b281a34e9f7c670) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | — | +| [redis-me `2db9d859b`](https://github.com/vite-plus-ecosystem-ci/redis-me/tree/2db9d859ba0a4c6fe2868c7774560f96f321ae90) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | — | +| [regle `c2668d6f0`](https://github.com/vite-plus-ecosystem-ci/regle/tree/c2668d6f02ec414bdf01609f323fbdbd6036c838) | 0 / 0 | 1 / 1 | 1 / 1 | 1165 / 1218 | 1 / 1 | `typecheck` 1 / 1 | +| [remdx `4da4d8f94`](https://github.com/vite-plus-ecosystem-ci/remdx/tree/4da4d8f945cb63056efc9a5cb94b32bf2b0b2777) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 1 / 1 | — | +| [saasyland.com `ca0fd03bf`](https://github.com/vite-plus-ecosystem-ci/saasyland.com/tree/ca0fd03bf6a754b224ff5c2bc75e3cc4132f1079) | 1 / 1 | 1 / 1 | 1 / 1 | 164 / 164 | 1 / 1 | — | +| [sandbox-sdk `61cac81b5`](https://github.com/vite-plus-ecosystem-ci/sandbox-sdk/tree/61cac81b53334b314e4a4daf04486b9cee9b65c2) | 1 / 1 | 1 / 1 | 1 / 1 | 34 / 50 | 1 / 1 | — | +| [sigle `31767a4d8`](https://github.com/vite-plus-ecosystem-ci/sigle/tree/31767a4d8f16e8448f88c3ff9b3d68efb47541c8) | 1 / 1 | 1 / 1 | 1 / 1 | Config blocked | 1 / 1 | — | +| [solid-services `add4b036b`](https://github.com/vite-plus-ecosystem-ci/solid-services/tree/add4b036b72de7e6372b07faf0aed6b3f2e506d4) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 1 / 1 | — | +| [soybean-admin `d2dfefd75`](https://github.com/vite-plus-ecosystem-ci/soybean-admin/tree/d2dfefd757e37d8d6077ea1cea6181fb628d6097) | 0 / 0 | 1 / 1 | 1 / 1 | 117 / 117 | 0 / 0 | `typecheck` 0 / 0 | +| [soybean-admin-antd `02a002d12`](https://github.com/vite-plus-ecosystem-ci/soybean-admin-antd/tree/02a002d120a51bdc3c255b0c6582b00a8bcdddf0) | 0 / 0 | 1 / 1 | 1 / 1 | 130 / 130 | 1 / 1 | `typecheck` 0 / 0 | +| [spark-ui `0436591bc`](https://github.com/vite-plus-ecosystem-ci/spark-ui/tree/0436591bc916bd17e7c5df28c1f58eb55066a3cb) | 1 / 1 | 1 / 1 | 1 / 1 | 6 / 4 | 1 / 1 | `typecheck` 1 / 1 | +| [svelte-meta-tags `57339bc3f`](https://github.com/vite-plus-ecosystem-ci/svelte-meta-tags/tree/57339bc3feefc1035914245dcbc37b08fb131680) | 0 / 0 | 1 / 1 | 1 / 1 | 4 / 4 | 2 / 2 | — | +| [svelte-multiselect `29a64aa5c`](https://github.com/vite-plus-ecosystem-ci/svelte-multiselect/tree/29a64aa5cd8e18867fee9b469882b1a0715f8b5a) | 1 / 1 | 1 / 1 | 1 / 1 | 81 / 84 | 2 / 2 | — | +| [sveltekit-embed `e7818c891`](https://github.com/vite-plus-ecosystem-ci/sveltekit-embed/tree/e7818c891e584070f5b00072e3bb85c070f71a6c) | 1 / 1 | 1 / 1 | 1 / 1 | 3 / 3 | 2 / 2 | — | +| [tiptap `228df820d`](https://github.com/vite-plus-ecosystem-ci/tiptap/tree/228df820d22a807310f7dcd47eb145557027c8e5) | 0 / 0 | 1 / 1 | 1 / 1 | 57 / 57 | 0 / 0 | — | +| [trpc-nuxt `6b2b2ba86`](https://github.com/vite-plus-ecosystem-ci/trpc-nuxt/tree/6b2b2ba86e6b15adb7d5c88a785adba1f050cac5) | 1 / 1 | 1 / 1 | 1 / 1 | 13 / 20 | 1 / 1 | — | +| [typed-xlsx `91ea9a725`](https://github.com/vite-plus-ecosystem-ci/typed-xlsx/tree/91ea9a725631f8d7ec8b6ff70d55fac14bf45dbb) | 0 / 0 | 1 / 1 | 1 / 1 | 39 / 39 | 1 / 1 | `typecheck` 1 / 1 | +| [unplugin-turbo-console `ea1986d13`](https://github.com/vite-plus-ecosystem-ci/unplugin-turbo-console/tree/ea1986d1329a7c1a059e3ae0731384ecfbd9dba1) | 0 / 0 | 1 / 1 | 1 / 1 | 41 / 44 | 0 / 0 | — | +| [urllib `7aba43082`](https://github.com/vite-plus-ecosystem-ci/urllib/tree/7aba43082d8fce3d967e1855ae326627a9ce139c) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | `typecheck` 0 / 0 | +| [v-offline `d0ed7de37`](https://github.com/vite-plus-ecosystem-ci/v-offline/tree/d0ed7de3707f5c43ed5f0b066e22f3347eea413e) | 0 / 0 | 0 / 0 | 1 / 1 | 31 / 29 | 0 / 0 | `typecheck` 0 / 0 | +| [varlet `4f56984db`](https://github.com/vite-plus-ecosystem-ci/varlet/tree/4f56984dbf90036ebdea4167217ff7659f2e4c9f) | 0 / 0 | 0 / 0 | 1 / 1 | 274 / 282 | 0 / 0 | `compile` 1 / 1 | +| [vean-ui `7c5493801`](https://github.com/vite-plus-ecosystem-ci/vean-ui/tree/7c5493801fe91cd2df33dec910bca3436d85a3df) | 0 / 0 | 1 / 1 | 1 / 1 | 2611 / 2605 | 0 / 0 | `typecheck` 1 / 1 | +| [vibe-dashboard `9b0ec30a3`](https://github.com/vite-plus-ecosystem-ci/vibe-dashboard/tree/9b0ec30a39b9d77a44656a7cdb520c15c224b3e1) | 1 / 1 | 1 / 1 | 1 / 1 | 1 / 1 | 1 / 1 | — | +| [vinext `42d940ece`](https://github.com/vite-plus-ecosystem-ci/vinext/tree/42d940eceb8f8199195d374711a14498b248dd61) | 0 / 1 | 0 / 1 | 0 / 1 | 0 / 17 | 0 / 1 | — | +| [vitepress-carbon `b9c0ecf06`](https://github.com/vite-plus-ecosystem-ci/vitepress-carbon/tree/b9c0ecf0685603ee7fae0776b479724539818f89) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 1 / 1 | — | +| [vitepress-plugin-group-icons `602761b05`](https://github.com/vite-plus-ecosystem-ci/vitepress-plugin-group-icons/tree/602761b05108e4b4df2da9f69e08a406cd6540f0) | 0 / 0 | 0 / 0 | 1 / 1 | 3 / 3 | 0 / 0 | — | +| [VRM-Addon-for-Blender `f886e7fe2`](https://github.com/vite-plus-ecosystem-ci/VRM-Addon-for-Blender/tree/f886e7fe283f08035273f5406fadebea6db64d60) | 0 / 0 | 0 / 0 | 1 / 1 | 6 / 6 | 0 / 0 | — | +| [vue-clamp `6f0da3991`](https://github.com/vite-plus-ecosystem-ci/vue-clamp/tree/6f0da3991f804587ba402a7ca22aec4564eab938) | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 1 / 1 | — | +| [vue-core `8f946dc4a`](https://github.com/vite-plus-ecosystem-ci/vue-core/tree/8f946dc4a2f0bb515a1a8070388b9b865a2a2b9d) | 0 / 0 | 0 / 0 | 1 / 1 | 541 / 551 | 1 / 1 | `check` 2 / 2 | +| [vue-devtools `bb6f283f9`](https://github.com/vite-plus-ecosystem-ci/vue-devtools/tree/bb6f283f9eab90bbfbe06376ac92fcc116d43b52) | 1 / 1 | 1 / 1 | 1 / 1 | 70 / 70 | 1 / 1 | `typecheck` 2 / 2 | +| [vueleaflet `254f84e5f`](https://github.com/vite-plus-ecosystem-ci/vueleaflet/tree/254f84e5f8660e914ecbd1f8a0d0763709edf514) | 0 / 0 | 0 / 0 | 1 / 1 | 18 / 18 | 1 / 1 | — | +| [web-app-template `7cc5e4e15`](https://github.com/vite-plus-ecosystem-ci/web-app-template/tree/7cc5e4e152429100f8dc230b2fbe9e516f6a1c8c) | 1 / 1 | 1 / 1 | 1 / 1 | 1 / 1 | 1 / 1 | — | +| [yaak `f7402f5f4`](https://github.com/vite-plus-ecosystem-ci/yaak/tree/f7402f5f4f9752c4722a478cdb51dd54ad87cc34) | 0 / 0 | 0 / 0 | 1 / 1 | 327 / 342 | 1 / 1 | — | +| [yielded-auth `788239010`](https://github.com/vite-plus-ecosystem-ci/yielded-auth/tree/788239010e5f806d28f7fb3151e9b37c1e59a39c) | 0 / 0 | 0 / 0 | 0 / 1 | 0 / 5 | 1 / 1 | — | + +## Diagnostic changes in already-failing projects + +The following projects already fail explicit type checking with Go and have additional error diagnostics with tsrs. Counts compare file, code, severity, first-label position, and message; a changed message or position can count as one addition and one removal. These differences have not all been independently attributed to the upstream compiler. + +| Repository | Added errors | Removed errors | Added diagnostic codes | +| ---------------------- | -----------: | -------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| cloudflare-os | 380 | 373 | `TS2339` × 22, `TS2345` × 2, `TS2550` × 38, `TS2740` × 2, `TS5115` × 2, `TS7031` × 8, `TS7053` × 1, `no-unnecessary-type-assertion` × 114, `no-unsafe-type-assertion` × 191 | +| codiff | 291 | 0 | `TS18046` × 53, `TS18047` × 2, `TS18048` × 1, `TS2304` × 4, `TS2322` × 15, `TS2339` × 51, `TS2345` × 26, `TS2352` × 1, `TS2493` × 2, `TS2538` × 4, `TS2540` × 1, `TS2551` × 1, `TS2554` × 2, `TS2698` × 3, `TS2739` × 1, `TS2741` × 7, `TS2790` × 1, `TS6133` × 4, `TS7005` × 13, `TS7006` × 86, `TS7016` × 1, `TS7022` × 2, `TS7034` × 9, `TS7053` × 1 | +| delta-comic | 1 | 1 | `TS2345` × 1 | +| docx-editor | 31 | 56 | `TS2322` × 7, `TS2339` × 2, `TS2345` × 11, `TS2352` × 1, `TS2551` × 2, `TS2741` × 2, `TS7031` × 4, `TS7053` × 2 | +| dotabod-backend | 5 | 3 | `TS2345` × 1, `TS5115` × 2, `no-unnecessary-type-assertion` × 1, `no-unsafe-type-assertion` × 1 | +| dotabod-frontend | 8 | 8 | `no-unsafe-argument` × 5, `no-unsafe-type-assertion` × 3 | +| f3-app | 2 | 0 | `TS2344` × 1, `TS2538` × 1 | +| faasjs | 1 | 1 | `TS2322` × 1 | +| fate | 1 | 1 | `TS2345` × 1 | +| hono | 5 | 3 | `TS2875` × 3, `TS7031` × 2 | +| kaneo | 18 | 18 | `TS2322` × 10, `TS2339` × 4, `TS2345` × 3, `TS2352` × 1 | +| local-studio | 1 | 1 | `TS2741` × 1 | +| matterviz | 9 | 0 | `TS7031` × 9 | +| npmx.dev | 13 | 0 | `TS2322` × 2, `TS2339` × 5, `TS2552` × 1, `TS2595` × 4, `TS7006` × 1 | +| OneSignal-Website-SDK | 1 | 0 | `TS2741` × 1 | +| pocket-id | 131 | 0 | `TS2304` × 131 | +| react-data-grid | 1 | 2 | `TS2307` × 1 | +| regle | 53 | 0 | `TS2307` × 27, `TS2344` × 10, `TS2552` × 1, `TS2664` × 1, `TS7006` × 6, `TS7031` × 8 | +| sandbox-sdk | 16 | 0 | `TS18046` × 8, `TS2307` × 8 | +| soybean-admin | 1 | 1 | `no-unnecessary-type-assertion` × 1 | +| soybean-admin-antd | 1 | 1 | `no-unnecessary-type-assertion` × 1 | +| svelte-multiselect | 3 | 0 | `TS18046` × 3 | +| tiptap | 3 | 3 | `TS2339` × 2, `TS2741` × 1 | +| trpc-nuxt | 7 | 0 | `TS2307` × 6, `TS2339` × 1 | +| unplugin-turbo-console | 3 | 0 | `TS2307` × 1, `TS2552` × 1, `TS7016` × 1 | +| varlet | 9 | 1 | `TS2307` × 4, `TS2322` × 1, `TS7026` × 4 | +| vean-ui | 41 | 47 | `TS2322` × 1, `no-unnecessary-type-assertion` × 28, `no-unsafe-type-assertion` × 12 | +| vue-core | 12 | 2 | `TS2305` × 3, `TS2322` × 2, `TS2353` × 1, `TS5115` × 1, `TS7006` × 2, `TS7031` × 3 | +| yaak | 16 | 1 | `TS1479` × 1, `TS2300` × 14, `TS2345` × 1 | + +## Method and coverage limits + +Each control runs these four workloads. The lint commands also use +`--format json --threads 4` for structured comparison: ```sh -vp lint --format json --threads 4 -vp lint --type-aware --type-check --format json --threads 4 +vp lint +vp lint --type-aware +vp lint --type-aware --type-check vp check ``` -Hono retained its lint scopes: `src`, `runtime-tests`, `build`, `perf-measures`, -`benchmarks`, and `adapters`. Hono and Vue Core do not enable type-aware linting in -their current configurations; the explicit flags add stress coverage. React Data -Grid enables type-aware rules but does not enable TypeScript diagnostics by default. - -The runner also used `vp run --no-cache` for five existing project tasks: -`oxc-walker`'s `test:types`, `urllib`'s `typecheck`, `media-processors`'s `typecheck`, -`react-data-grid`'s `typecheck`, and `vue-core`'s `check`. These tasks retain their -own compiler selection. A `tsc` task does not automatically become a `tsrs` task. - -The runner compared sorted JSON diagnostics, including codes, severity, messages, -files, spans, and help text. It normalized absolute project paths. Process traces -confirmed the expected native backend in all 42 explicit type-check runs. React -Data Grid and Vue Core required the matching optional `@vitest/browser-playwright@5.1.0` -peer for configuration imports. No browser installation was required. - -## Repository results - -The counts below include errors and warnings from the explicit type-check workload. -Equal counts alone do not establish equality; the comparison also checked diagnostic content. -All source links point to immutable commits in `vite-plus-ecosystem-ci`. - -| Repository / commit | Files | Go diagnostics | tsrs diagnostics | Comparison | -| -------------------------------------------------------------------------------------------------------------------------------------------------- | ----: | -------------: | ---------------: | --------------------------------------------- | -| [oxc-walker `9e520f25d`](https://github.com/vite-plus-ecosystem-ci/oxc-walker/tree/9e520f25d2bf5c3748f51d143c534fedff207147) | 12 | 0 | 0 | Identical diagnostics | -| [eslint-plugin-oxlint `ca4b10897`](https://github.com/vite-plus-ecosystem-ci/eslint-plugin-oxlint/tree/ca4b1089757245fa9a6a6ee46341d94a218d9c05) | 41 | 0 | 0 | Identical diagnostics | -| [solid-services `add4b036b`](https://github.com/vite-plus-ecosystem-ci/solid-services/tree/add4b036b72de7e6372b07faf0aed6b3f2e506d4) | 10 | 0 | 0 | Identical diagnostics | -| [pinia-shared-state `1b1ec6109`](https://github.com/vite-plus-ecosystem-ci/pinia-shared-state/tree/1b1ec6109b6f46079e4ddb5aeb9feff1d7188c24) | 6 | 0 | 0 | Identical diagnostics | -| [urllib `91f32d53f`](https://github.com/vite-plus-ecosystem-ci/urllib/tree/91f32d53faaae34a9f09f38e402e617420bc4d15) | 72 | 6 | 6 | Identical diagnostics | -| [anywidget `443d720b2`](https://github.com/vite-plus-ecosystem-ci/anywidget/tree/443d720b2233fff131a0f0a842b08744c8ffabd6) | 66 | 14 | 14 | Identical diagnostics | -| [css-modules-kit `634608fdd`](https://github.com/vite-plus-ecosystem-ci/css-modules-kit/tree/634608fdd401001d1fb05511a89177acf38b8111) | 131 | 0 | 0 | Identical diagnostics | -| [media-processors `79c87cd5c`](https://github.com/vite-plus-ecosystem-ci/media-processors/tree/79c87cd5c066a0a9abea979044c38c69603a85e8) | 22 | 101 | 91 | 10 fewer diagnostics | -| [web-app-template `f9a09f3d8`](https://github.com/vite-plus-ecosystem-ci/web-app-template/tree/f9a09f3d81002372e839247c6274af43177bbeca) | 8 | 3 | 1 | 2 fewer diagnostics | -| [OneSignal-Website-SDK `5375640c0`](https://github.com/vite-plus-ecosystem-ci/OneSignal-Website-SDK/tree/5375640c001a2dc462ed1cb938520edfb9a23d07) | 298 | 8 | 9 | New DOM-library requirement | -| [react-data-grid `384aedffc`](https://github.com/vite-plus-ecosystem-ci/react-data-grid/tree/384aedffc2cac013666b58b6787d888b9dde3a94) | 127 | 302 | 300 | 2 fewer diagnostics; message changes | -| [vue-clamp `6f0da3991`](https://github.com/vite-plus-ecosystem-ci/vue-clamp/tree/6f0da3991f804587ba402a7ca22aec4564eab938) | 134 | 5 | 1 | 4 fewer recursion errors | -| [hono `c91b60a25`](https://github.com/vite-plus-ecosystem-ci/hono/tree/c91b60a251186fb024557c6d55a11d8e388f179c) | 457 | 474 | 476 | 2 new contextual-typing errors; span changes | -| [vue-core `747e8d668`](https://github.com/vite-plus-ecosystem-ci/vue-core/tree/747e8d668c4a64ae6bd9dcc74c173364f1c1490b) | 905 | 547 | 546 | 7 new source errors; 8 fewer recursion errors | - -Default `vp lint` diagnostics matched in ten repositories. Its four differences -were OneSignal, media-processors, web-app-template, and vue-clamp. Vue Clamp's lint -exit changed from `1` to `0` after four `TS2321` errors disappeared; one warning remained. -The additional Hono, Vue Core, and React Data Grid differences appeared with explicit type-check flags. - -`vp check` passed in oxc-walker, eslint-plugin-oxlint, pinia-shared-state, and -css-modules-kit under all controls. The other ten failed under all controls. -Formatting stopped five of them before lint: solid-services, anywidget, -vue-clamp, Hono, and Vue Core. These failures do not demonstrate backend failure -or backend coverage. - -## Upstream contextual-typing regression - -[Hono's `Style` implementation](https://github.com/vite-plus-ecosystem-ci/hono/blob/c91b60a251186fb024557c6d55a11d8e388f179c/src/helper/css/index.ts#L192) -produces two new `TS7031` errors for `children` and `nonce` with tsrs. This reduced -source reproduces the failure with strict type checking: - -```ts -interface StyleType { - (args?: { children?: Promise; nonce?: string }): string; -} -export const Style: StyleType = ({ children, nonce } = {}) => `${children}${nonce}`; -``` - -TypeScript `6.0.3`, TypeScript `7.0.2`, and both Go lint controls accept this source. -The default preview and standalone tsrs report both errors. An explicit parameter -type annotation makes all controls pass. Hono's production project also passes -`tsc -p tsconfig.build.json --noEmit` but fails the same check with standalone tsrs. -This confirms a compiler-version difference independent of Vite+ backend resolution. - -[Vue Core's `applyTextModel`](https://github.com/vite-plus-ecosystem-ci/vue-core/blob/747e8d668c4a64ae6bd9dcc74c173364f1c1490b/packages/runtime-vapor/src/directives/vModel.ts#L46) -has the same default-destructuring pattern. Its `trim`, `number`, and `lazy` bindings -produce three new `TS7031` errors. A four-line reduction with a mapped optional -parameter passes TypeScript `6.0.3`, TypeScript `7.0.2`, and both Go controls, but fails both tsrs paths. - -The exact `7.1.0-beta` reference reports the same errors. Its -[`checkDeclarationInitializer` / `padObjectLiteralType`](https://github.com/microsoft/TypeScript/blob/3e9236361fa97a9e6c685f4833ac457ae2d8c50a/tsc/internal/checker/checker.go#L17205) -pads missing properties of the `{}` initializer and calls -`getTypeFromBindingElement` with `reportErrors: true`. The -[Rust port follows the same path](https://github.com/oxc-project/tsrs/blob/625f918c85a3e12824ede39cd127d0f866b8d9c8/crates/tsrs_checker/src/checker_08.rs#L2980). -That path can report implicit `any` despite an available contextual parameter type. -TypeScript tracks this as [bug #64974](https://github.com/microsoft/TypeScript/issues/64974). -The rejection is a false positive, while the tsrs implementation conforms to its reference. - -## Vue Core selector types - -[Vue Core's scoped-style transformer](https://github.com/vite-plus-ecosystem-ci/vue-core/blob/747e8d668c4a64ae6bd9dcc74c173364f1c1490b/packages/compiler-sfc/src/style/pluginScoped.ts#L107) -produces a new `TS5115` circular-instantiation error and two `TS7006` callback errors. -This smaller case uses the project's locked `postcss-selector-parser@7.1.6`: - -```ts -import type parser from 'postcss-selector-parser'; -export function inspect(selector: parser.Selector): void { - selector.each((n) => { - if (n.type === 'pseudo') { - n.nodes[0].each((ss) => { - void ss; - }); - } - }); -} -``` - -TypeScript `6.0.3`, TypeScript `7.0.2`, and both Go controls accept the reduced source. The -default preview and standalone tsrs report `TS5115` and `TS7006`. The reduction -uses strict mode, bundler module resolution, and `skipLibCheck: true`. - -The dependency declares `Selector = _Selector`, where `_Selector` -extends `Container>`. `Node` itself includes `Selector`, and -`Diff` is a distributive conditional type. Resolving the base members can -therefore require comparing the type whose base members are still being resolved. -TypeScript [#64372](https://github.com/microsoft/TypeScript/pull/64372) deliberately -stopped allowing base type arguments to depend on partially resolved members. - -Both the [upstream instantiation guard](https://github.com/microsoft/TypeScript/blob/3e9236361fa97a9e6c685f4833ac457ae2d8c50a/tsc/internal/checker/checker.go#L22601) -and [Rust guard](https://github.com/oxc-project/tsrs/blob/625f918c85a3e12824ede39cd127d0f866b8d9c8/crates/tsrs_checker/src/checker_11.rs#L1319) -stop at depth `100` or `5,000,000` instantiations and report the circular type names. -The exact upstream compiler reports the same `Container` / `Diff` error and lost -callback type. This is a dependency compatibility problem under the newer rules, -not evidence of a Rust implementation error. The causal link to the stricter -inheritance rule follows from the source review; this audit did not bisect that upstream change. - -## Vue Core generic conditional return type - -[Vue Core's `toHandlerKey`](https://github.com/vite-plus-ecosystem-ci/vue-core/blob/747e8d668c4a64ae6bd9dcc74c173364f1c1490b/packages/shared/src/general.ts#L147) -produces a new `TS2322` when it assigns a generic function with a conditional -return type. Both an isolated copy of the original implementation and this -reduction reproduce the error: - -```ts -declare function cache string>(fn: T): T; -export const handler: (s: T) => T extends '' ? '' : `on${Capitalize}` = cache( - (s: T) => { - const value = s ? `on${s}` : ''; - return value as T extends '' ? '' : `on${Capitalize}`; - }, -); -``` - -TypeScript `6.0.3`, TypeScript `7.0.2`, and both Go controls accept both cases. The default -preview and standalone tsrs reject both cases with `TS2322`. The reduction uses -the same strict compiler options as the selector case. - -The exact `7.1.0-beta` compiler rejects both cases identically. The -[upstream conditional-type comparison](https://github.com/microsoft/TypeScript/blob/3e9236361fa97a9e6c685f4833ac457ae2d8c50a/tsc/internal/checker/relater.go#L3758) -and [Rust comparison](https://github.com/oxc-project/tsrs/blob/625f918c85a3e12824ede39cd127d0f866b8d9c8/crates/tsrs_checker/src/relater_2.rs#L2541) -compare the conditional types without normalizing both distributed type parameters. -This is consistent with the upstream asymmetry described in -[#64673](https://github.com/microsoft/TypeScript/issues/64673): instantiation can -normalize one side while the other retains a distributed parameter. - -The open [fix #64307](https://github.com/microsoft/TypeScript/pull/64307) normalizes -both sides and is relevant to this failure. That root-cause connection is an -inference from the source and diagnostic chain. The patch was not applied or -validated against this case here. The exact-reference failure is sufficient to -rule out a Rust-only divergence for this reproduction. - -## OneSignal DOM-library difference - -[OneSignal's notification mock](https://github.com/vite-plus-ecosystem-ci/OneSignal-Website-SDK/blob/5375640c001a2dc462ed1cb938520edfb9a23d07/__test__/support/environment/TestEnvironmentHelpers.ts#L46) -produces a new `TS2741`: the constructor lacks `Notification.maxActions`. The tsrs -`lib.dom.d.ts` requires that static property. The tested TypeScript `6.0.3` and -`7.0.2` libraries do not contain it. - -```ts -type HasMaxActions = 'maxActions' extends keyof typeof Notification ? true : false; -export const hasMaxActions: HasMaxActions = false; -``` - -The TypeScript `6.0.3` / `7.0.2` and Go lint controls accept this probe. Both tsrs paths report `TS2322`. -This establishes a library-contract difference; it does not establish that the -new DOM declaration is incorrect. The difference affects OneSignal's existing -configuration because it already enables TypeScript diagnostics. - -The tsrs and TypeScript `7.1.0-beta` `lib.dom.d.ts` files are byte-identical: -SHA256 `2f8c8d9e23d03e00c353412f5b2be3b1fd893290fc0976ad2229681bb7d46a35`. -The [Notifications standard](https://notifications.spec.whatwg.org/#dom-notification-maxactions) -also defines a static read-only `maxActions` member. Assigning `MockNotification` -to the constructor type now correctly requires that static member. Adding an -instance property would not satisfy it. A mock that supports no actions can -supply `static readonly maxActions = 0`. This is a mock adaptation to the updated -standard library, not a compiler false positive. - -## Other differences and limits - -Media-processors, web-app-template, vue-clamp, and React Data Grid report fewer -recursive-comparison or configuration-compatibility errors with tsrs. Vue Core -also loses eight `TS2321` errors but adds seven production-source errors. Its total -therefore falls from 547 to 546 despite the new failures. Fewer diagnostics are -not classified as fixes without a separate correctness check. - -React Data Grid also changes expanded type text in twelve existing diagnostics. -Vue Core changes two existing messages, and Hono moves three existing `TS2875` -spans. These presentation changes are included in the comparison. - -Several errors are shared by both backends. They include urllib's six -`Http2Stream.respond` errors, anywidget's 14 type errors, missing generated -`video-track-processor` declarations in media-processors, and a missing translation -file in web-app-template. The broad Hono workload includes optional benchmark -and runtime fixtures. React Data Grid and Vue Core include test declarations -that conflict with the overlaid toolchain's Vitest types. - -Only oxc-walker's existing type-check task passed. The other four tasks failed -under every control, with matching TypeScript diagnostics after installation-path -normalization. Vue Core initially returned `2` and then `1` as its incremental -cache warmed; fresh build-info files produced exit `2` and identical output in -all three controls. No task compiler was replaced by the default lint backend. - -This method isolates the backend while preserving application dependencies. It -does not reproduce each repository's full build pipeline or a complete dependency -upgrade. Shared setup and source errors must not be counted as new tsrs regressions. -The reduced cases provide independent evidence for the compiler-version differences. -The exact-reference audit attributes the four reproduced categories; it does not -establish exact-upstream parity for every diagnostic in the full sweep. - -The primary sweep used Codespaces job `job-d8ac88c64fc64b11`. Final anywidget, -React Data Grid, and Vue Core results came from `job-f5f12efe4b514e83`, which also -reduced the Hono and OneSignal cases. Vue reductions used `job-cf53f53e0a2d4216` and `job-120ca876783b4213`. -The harness, frozen dependency receipts, process traces, compiler outputs, and -raw JSON remain outside this repository. +Available declared type-check or compile tasks run through `vp run --no-cache` in +34 repositories. Compiler build-info files outside `node_modules` are cleared +before each task control and restored afterwards. Task names and results appear +in the full table. Type-aware lint alone can still type-check when the project +configuration already enables `typeCheck`. + +Process traces observe the expected backend launchers or native processes in both +explicit type-check controls for 82 repositories. Native processes are sampled +under both controls for 80. In three control runs across svelte-meta-tags and +sveltekit-embed, sampling captures only the launcher. The following two shared +configuration failures prevent backend coverage: + +- **brazilian-utils-javascript:** the configuration imports + `vite-plus/test/browser-webdriverio`, which this preview does not export. +- **sigle:** its `effecttsgo` plugin requires `@effect/tsgo` binary patching. + The pinned `@effect/tsgo@0.52.0` supports Oxlint through `1.87.0` and tsgolint + through `7.0.2004`. Both unpatched controls reject the plugin with the tested + `1.93.0` / `7.0.2006` versions. The patched Effect integration remains unverified. + +Formatting stops `vp check` before lint in 30 Go-control runs. The independent +lint and explicit type-check workloads provide coverage for those projects. +No retained command times out or exits from a signal. Every fork remains at its +recorded commit with unchanged tracked sources and lockfile hashes. + +The setup preserves these details: + +- Installs use committed lockfiles where available. Matterviz and + svelte-multiselect have no committed lockfile; both controls reuse the same + one-time installation. Required generators run separately during preparation. +- The preview is linked into workspace importers and nested pnpm peer importers. + CLI and native executable hashes match across the isolated preview copies. +- Notero retains its original `@types/node@25.6.0` peer. Nostalgist reuses its + installed `@types/chai@5.2.3` to avoid introducing duplicate Chai declarations. +- Temporary `vite-plus` dependency declarations are needed for dotabod-backend, + dotabod-frontend, and oxlint-tailwindcss. The two Dotabod projects also need + temporary Vite wrappers around their existing standalone lint/format configs. + All changes are restored. Their custom baseline-filtering scripts are outside + these four primary workloads. +- Hono keeps its six configured lint scopes. Nostalgist uses `src` and `tests`; + docx-editor uses `packages`, `shared`, `scripts`, `apps/cli`, `apps/mcp`, and + `apps/vscode-ext`; digital-signature uses `*.ts`, `*.js`, and `src/`. + Dotabod-frontend disables nested config and ignores `supabase/functions/**`. + Vinext uses sorted checker scheduling under both controls. + +This local sweep does not cover other platforms or every application build and +test. Raw commands, output, dependency receipts, backend traces, diagnostic +differences, and compiler audits are retained outside this repository. + +## Separate Ecosystem CI results + +The PR-head [Ecosystem CI run](https://github.com/voidzero-dev/vite-plus/actions/runs/43187905687) +passes all **51 project/platform jobs across 31 repositories**. This is the +repository's configured Linux/Windows matrix, with its own inputs and workloads. +It does not establish that all 83 catalog forks pass `vp check`. + +The five earlier trust-policy failures now pass: dify on Linux, videojs-v10 on +Linux and Windows, and nuxt-devtools on Linux and Windows. Migration retains +`trustPolicy: no-downgrade` and adds only the reviewed +`tsrs@0.25.0-ts7.1.0-beta` exception. + +## Earlier reduced-case findings + +The [earlier report](https://github.com/voidzero-dev/vite-plus/blob/25a7c006af2db834bfc66f9689b02de1060f580f/bench/tsrs-lint-ecosystem.md#exact-upstream-attribution-2026-11-08) +preserves the original 14-repository experiment and seven reduced reproductions. +It attributes four categories to the pinned TypeScript `7.1` behavior: +default-destructuring contextual typing, conditional return types, selector-type +recursion, and the DOM `Notification.maxActions` requirement. Those historical +compiler audits are distinct from this complete `0.25.0` preview sweep and its +six pass-to-fail cases. diff --git a/bench/tsrs-lint.md b/bench/tsrs-lint.md index 7af495fc69..87c40ab415 100644 --- a/bench/tsrs-lint.md +++ b/bench/tsrs-lint.md @@ -1,15 +1,15 @@ # Type-aware linting with tsrs-lint -The current integration pins `tsrs@0.24.0-ts7.1.0-beta`. The measurements below -remain results for `0.22.0`; they have not been repeated for `0.24.0`. +The current integration pins `tsrs@0.25.0-ts7.1.0-beta`. The measurements below +remain results for `0.22.0`; they have not been repeated for `0.25.0`. This comparison uses the default `tsrs@0.22.0-ts7.1.0-beta` integration and an explicit `oxlint-tsgolint@7.0.2005` override. Both use Oxlint `1.91.0` and the same built CLI. -The separate [ecosystem correctness comparison](./tsrs-lint-ecosystem.md) found -upstream TypeScript `7.1` regressions and library differences in representative forks. -The exact-version audit found no Rust-port divergence in the reduced cases. The performance -results below do not establish diagnostic equivalence. +The separate [ecosystem correctness comparison](./tsrs-lint-ecosystem.md) covers +the full fork catalog with the `0.25.0` preview and links the earlier reduced-case +audits. The examined cases match the pinned compiler but expose compiler-version +and library differences. The performance results below do not establish diagnostic equivalence. ## Elapsed time From 9a42bade828fbb33b405b48db39c0ef155b2be5b Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 13 Nov 2026 01:12:13 +0800 Subject: [PATCH 25/25] docs(bench): refresh local preview timing and memory results --- bench/tsrs-lint.md | 243 ++++++++++++++++++++++----------------------- 1 file changed, 118 insertions(+), 125 deletions(-) diff --git a/bench/tsrs-lint.md b/bench/tsrs-lint.md index 87c40ab415..1cc4d0ea33 100644 --- a/bench/tsrs-lint.md +++ b/bench/tsrs-lint.md @@ -1,134 +1,127 @@ # Type-aware linting with tsrs-lint -The current integration pins `tsrs@0.25.0-ts7.1.0-beta`. The measurements below -remain results for `0.22.0`; they have not been repeated for `0.25.0`. - -This comparison uses the default `tsrs@0.22.0-ts7.1.0-beta` integration and an explicit -`oxlint-tsgolint@7.0.2005` override. Both use Oxlint `1.91.0` and the same built CLI. - -The separate [ecosystem correctness comparison](./tsrs-lint-ecosystem.md) covers -the full fork catalog with the `0.25.0` preview and links the earlier reduced-case -audits. The examined cases match the pinned compiler but expose compiler-version -and library differences. The performance results below do not establish diagnostic equivalence. +The timing and memory results below were measured locally on **2026-11-13** with +the latest published [PR #3352 preview](https://github.com/voidzero-dev/vite-plus/actions/runs/43194215156), +`0.0.0-commit.25a7c006af2db834bfc66f9689b02de1060f580f`. +Both controls use the same published CLI and Oxlint `1.93.0`: the Go control uses +`oxlint-tsgolint@7.0.2006`, and the default preview uses `tsrs@0.25.0-ts7.1.0-beta`. +The subsequent source commit `68eb84008` changes only reports. + +All 12 validation commands and 204 benchmark invocations pass. The backends have +matching checked-file counts and no diagnostics on these two inputs. The separate +[full ecosystem comparison](./tsrs-lint-ecosystem.md) documents compatibility +differences on other projects; these performance results do not establish compiler parity. ## Elapsed time -Each value is the median of ten fresh CLI processes after one warm-up per backend. -The runner alternates the order between pairs. Timing runs do not sample memory. - -| Project | Workload | Go (s) | tsrs (s) | Reduction | -| -------- | -------------------------------- | -----: | -------: | --------: | -| Vite+ | Type-aware rules | 3.712 | 2.212 | 40.4% | -| Vite+ | Rules and TypeScript diagnostics | 4.612 | 2.382 | 48.4% | -| Vite+ | Full `vp check` | 5.842 | 3.554 | 39.2% | -| vinext | Type-aware rules | 52.028 | 45.363 | 12.8% | -| vinext † | Rules and TypeScript diagnostics | 54.192 | 44.794 | 17.3% | -| vinext | Full `vp check` | 62.325 | 50.355 | 19.2% | +Each cell shows the median of **10 runs**, followed by the interquartile range +(25th–75th percentile) in brackets. One warm-up per backend is excluded. +Each run starts a fresh CLI process. Backend order alternates between pairs. +Timing runs do not sample memory, and all benchmark commands run sequentially. -† The clean timing repetition used an EPYC 7763 after a Codespace restart. -All other timing rows used an EPYC 9V74. Both backends within each row used -the same CPU. Compare backend values within a row; the CPU change prevents -a strict comparison of absolute times across workloads. +| Project | Workload | Go (s) | tsrs (s) | Reduction | +| ------- | -------------------------------- | ------------------: | ------------------: | --------: | +| Vite+ | Type-aware rules | 1.518 [1.489–1.654] | 1.371 [1.255–1.399] | 9.7% | +| Vite+ | Rules and TypeScript diagnostics | 1.861 [1.820–1.928] | 1.410 [1.316–1.504] | 24.2% | +| Vite+ | Full `vp check` | 2.112 [2.102–2.243] | 1.674 [1.646–1.795] | 20.8% | +| vinext | Type-aware rules | 2.677 [2.616–2.791] | 1.572 [1.533–1.656] | 41.3% | +| vinext | Rules and TypeScript diagnostics | 3.364 [3.296–3.454] | 1.695 [1.635–1.728] | 49.6% | +| vinext | Full `vp check` | 4.680 [4.563–4.731] | 2.178 [2.095–2.184] | 53.4% | ## Peak memory -Each value is the median of five separate runs after one warm-up per backend. -Memory is the sampled peak of the simultaneous sum of RSS for the CLI and its -descendants, including Node, Oxlint, and the native backend. One GiB is 1,073,741,824 bytes. - -| Project | Workload | Go (GiB) | tsrs (GiB) | Reduction | -| ------- | -------------------------------- | -------: | ---------: | --------: | -| Vite+ | Type-aware rules | 1.175 | 0.930 | 20.9% | -| Vite+ | Rules and TypeScript diagnostics | 1.345 | 0.984 | 26.8% | -| Vite+ | Full `vp check` | 1.438 | 1.017 | 29.3% | -| vinext | Type-aware rules | 4.382 | 3.030 | 30.8% | -| vinext | Rules and TypeScript diagnostics | 4.903 | 3.166 | 35.4% | -| vinext | Full `vp check` | 5.413 | 3.292 | 39.2% | - -The sampler sleeps for 20 ms. The median observed interval across runs was -21.8 ms. Shared pages can count more than once, -and sampling can miss short peaks. These values are process-tree RSS, not total -allocation or container memory. - -## Installed disk footprint - -Clean production installations on Linux x64 with glibc compare published `1.5.0` -with preview `0.0.0-commit.4fcf99edbe42fe3305dcd41dc884a4c4b7f5fd45`. -The preview binary-size comment measures individual executables and native bindings; -it does not measure the installed dependency tree. - -| Scope | Go (MiB) | tsrs (MiB) | Increase (MiB) | Increase | -| -------------------------------- | -------: | ---------: | -------------: | -------: | -| Complete `node_modules`, on disk | 132.93 | 150.87 | 17.94 | 13.5% | -| Complete `node_modules`, files | 123.39 | 139.27 | 15.88 | 12.9% | -| Backend packages, on disk | 21.63 | 39.69 | 18.06 | 83.5% | - -One MiB is 1,048,576 bytes. On-disk values use `du -s -B1 node_modules` and -include directory allocation. File sizes sum regular-file lengths without following -symlinks or counting an inode twice. The filesystem uses 4 KiB blocks. -Stores, download caches, Node.js, pnpm, and the global `vp` executable are excluded. - -The job used Node.js `24.22.2`, pnpm `10.34.7`, and each version's published -`vp-install-lock.yaml`. It reproduced the global installer's manifest and frozen -installation flags. Separate stores and `--config.package-import-method=copy` -prevent shared hard links from affecting the standalone footprint. Each installation -contained 80 packages. Version output and type-aware linting with TypeScript -diagnostics passed for both installations. The preview contained no `oxlint-tsgolint`. - -The backend totals include `oxlint-tsgolint` and `@oxlint-tsgolint/linux-x64`, or -`tsrs` and `@tsrs/linux-x64-gnu`. The Rust distribution includes a larger executable, -TypeScript library declarations, JavaScript APIs, and source maps. Its package -footprint increases despite the runtime and memory improvements. The total -installation comparison also includes other release-to-preview changes; -the backend row separates the backend packages from those changes. - -The measurement ran on 2026-11-06 in Codespaces job `job-2a30c8ec3fb84524`. -Scripts, raw package inventories, lockfiles, and logs remain outside the repository. -The lockfile SHA256 values are -`eb06955c3e76589da771286d3bfc6e6ea940c1a09ac64f597d2c72eb94403ad1` for `1.5.0` -and `cf8fe673142ace0abaa362552bff78281f3b8f00e01bc13fa0eb93c5b1344683` for the preview. - -## Conditions and interpretation - -The jobs used one GitHub Codespace with four vCPUs, 16 GB advertised RAM, and an -AMD EPYC 9V74 processor for the main run. The focused timing repetition used an -AMD EPYC 7763 after the instance restarted. Usable RAM was 15.62 GiB. -The container used Ubuntu 22.04 on Linux x64 with glibc. Vite+ used Node.js -`24.22.2`; vinext used Node.js `26.13.0`. - -The CLI JavaScript came from `22b07bd150aa57b3e04cc52e0b81d43294ffe982`. -Its native binding, core, and prompts artifacts came from the published Vite+ -`1.5.0` packages. The measured Vite+ source also includes the RFC patch at -`da260f86fce1d43810c62e4e6aad011066c88cb9`. Vinext is pinned to -`249088af307c0a852fa6dc59c08fc6ec80e1620e`, from the requested upgrade branch. -This is a comparison of backend routing in the built CLI, not a full native release build. - -Lint uses four threads. Vinext lint sets -`OXLINT_TSGOLINT_CHECKER_SCHEDULING=sorted`; Vite+ lint uses default scheduling. -Full checks use four threads and default backend scheduling. The rules-only -configuration disables TypeScript diagnostics while retaining the project's -original rules and plugins. Setup, builds, validation, and memory sampling are -excluded from the elapsed-time table. - -Both backends passed all 12 validation commands and every benchmark invocation. -Vite+ checked 385 source files with 174 rules and formatted 632 files. Vinext -checked 1,828 source files with 151 rules and formatted 4,673 files. These clean -corpora do not establish complete diagnostic or compiler parity. - -Vinext retains its original JS plugin rules, including -`vinext-utils/prefer-shared-utils`. The backend switch leaves that shared work -in place. This limits the elapsed-time improvement even though the memory -reduction is larger. The measurements do not include that rule's separate optimization. - -## Run selection - -The main job completed 204 invocations, including warm-ups. -A browser connection used to diagnose an SSH -outage started editor extension activity during vinext's type-checking timing -set. That entire timing set is excluded. The table instead uses the 22 records -from a clean timing repetition. -The repetition uses the same retained build and dependencies, with their hashes -checked before execution. All memory values come from the main job. - -Warm-up runs do not contribute to medians. Each reduction is calculated as -`100 * (1 - rust / go)` from the corresponding backend medians. +Each cell shows the median of **5 separate runs** and its interquartile range. +One warm-up per backend is excluded. Memory is the sampled peak of the simultaneous +sum of RSS for the CLI and its descendants, including Node, Oxlint, and the native +backend. The sampler itself is excluded. One GiB is 1,073,741,824 bytes. + +| Project | Workload | Go (GiB) | tsrs (GiB) | Reduction | +| ------- | -------------------------------- | ------------------: | ------------------: | --------: | +| Vite+ | Type-aware rules | 1.103 [1.047–1.153] | 0.633 [0.601–0.647] | 42.6% | +| Vite+ | Rules and TypeScript diagnostics | 1.276 [1.258–1.462] | 0.651 [0.644–0.670] | 49.0% | +| Vite+ | Full `vp check` | 1.325 [1.269–1.332] | 0.706 [0.687–0.715] | 46.7% | +| vinext | Type-aware rules | 3.427 [3.407–3.632] | 1.852 [1.836–1.889] | 46.0% | +| vinext | Rules and TypeScript diagnostics | 3.694 [3.662–3.904] | 1.967 [1.956–1.977] | 46.8% | +| vinext | Full `vp check` | 5.014 [4.756–5.391] | 2.102 [2.090–2.102] | 58.1% | + +The sampler sleeps for 20 ms; the median observed interval is 38.8 ms +(the median of the measured runs' sampling intervals). Shared pages can count more +than once, and sampling can miss short peaks. These are process-tree RSS values, +not total allocations or macOS physical-footprint measurements. + +## Inputs and method + +| Input | Pin / condition | +| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| Machine | Apple M4 Max, 16 CPU cores, 48 GiB RAM, macOS `27.0.1`, ARM64 | +| Vite+ source | [`68eb84008`](https://github.com/voidzero-dev/vite-plus/tree/68eb84008fd27a72a5e9426ad64636432385b0e1), Node.js `24.22.2` | +| vinext source | [`7f1feb134`](https://github.com/fengmk2/vinext/tree/7f1feb134255f9e816e1b03760b97c10148eed3c), Node.js `26.13.0` | +| Lint threads | `4` for both lint workloads | +| Full check threads | Default `16`, confirmed in format and lint output | +| Repetitions per project/workload/backend | 10 timing runs, 5 memory runs, and one warm-up before each set | + +The three workloads are: + +```sh +vp lint --type-aware --threads 4 --format json -c +vp lint --type-aware --type-check --threads 4 --format json +vp check +``` + +The temporary rules-only JSON contains the project's `lint` section with +`options.typeCheck` set to `false`; all rules, plugins, and ignore patterns +are retained. Vinext lint uses sorted checker scheduling under both controls. +Full checks and Vite+ lint use default checker scheduling. + +Both controls use the same source and installed dependencies. Vite+ retains its +own workspace dependency links; the measured CLI, Oxlint, and native backends +come from the published preview. Vinext links the preview CLI and core into its +workspace and pnpm peer importers. These temporary links are restored afterwards. +All commands use isolated temporary `VP_HOME` directories and disable the Node +compile cache. Setup, configuration extraction, and output validation are excluded +from the timed intervals; CLI startup and command execution are included. + +Native executable paths are verified in every validation and memory run. Package +versions, executable hashes, source commits, and lockfile hashes are recorded. +Tracked project sources and lockfiles are unchanged after measurement. + +| Project | Linted files | Rules | Formatted files in `vp check` | +| ------- | -----------: | ----: | ----------------------------: | +| Vite+ | 387 | 174 | 675 | +| vinext | 1,842 | 151 | 4,755 | + +## Interpretation and reproducibility + +This is a local desktop measurement with warm filesystem caches, not a cold-cache +or isolated-server result. CPU use measured for 0.5 seconds before each timed run +has a median of 12.4% across the machine (range 7.2%–23.9%). +The CPU frequency is not fixed. Compare the paired backends within each row; +the interquartile ranges describe sample spread, not confidence intervals. + +The vinext input contains both the Sentry fixture exclusion from its draft PR +and the upstream [`prefer-shared-utils` optimization](https://github.com/cloudflare/vinext/commit/5a6cc15f39ca7e93bb1892efdcf2d3d01aa1b562). +Its original plugin rules remain enabled. These source inputs, the hardware, +thread counts, and tool versions differ from the earlier Codespaces measurements. +Changes in absolute times or reduction percentages cannot be attributed solely +to the tsrs version update. + +All measured samples are retained; there is no outlier filtering. Reductions use +`100 * (1 - tsrs median / Go median)`. Raw samples, commands, memory traces, +dependency receipts, and the local runner remain outside the repository. + +## Historical installed disk footprint + +The following disk measurements retain the **2026-11-06 Linux x64** comparison +of published `1.5.0` and the `0.22.0` preview. They are not measurements of the +current macOS preview. The [original report](https://github.com/voidzero-dev/vite-plus/blob/68eb84008fd27a72a5e9426ad64636432385b0e1/bench/tsrs-lint.md#installed-disk-footprint) +contains the installation method, package inventory scope, and lockfile hashes. + +| Scope | Go (MiB) | tsrs (MiB) | Increase | +| --------------------------------------------- | -------: | ---------: | -------: | +| Complete `node_modules`, on disk | 132.93 | 150.87 | 13.5% | +| Complete `node_modules`, regular-file lengths | 123.39 | 139.27 | 12.9% | +| Backend packages, on disk | 21.63 | 39.69 | 83.5% | + +One MiB is 1,048,576 bytes. The total installation comparison includes other +release-to-preview dependency changes; the backend row isolates the backend +packages. These disk figures do not describe runtime memory use.