diff --git a/.github/workflows/upgrade-deps.yml b/.github/workflows/upgrade-deps.yml index 571d517ee9..2ac25b2181 100644 --- a/.github/workflows/upgrade-deps.yml +++ b/.github/workflows/upgrade-deps.yml @@ -190,21 +190,23 @@ jobs: incorrect source-level fix, fix the source and regenerate until the diff is either gone or intentionally kept. - ### Final validation (this step is complete ONLY when all pass) - 1. `just build` exits 0. - 2. `pnpm bootstrap-cli:ci && pnpm test:unit` exits 0. - 3. `just snapshot-test` exits 0. Snapshot mismatches fail the run with a - unified diff. Re-record with `UPDATE_SNAPSHOTS=1` only for cosmetic - drift from the upgrade (e.g. a bumped version string in help output); - unexpected stack traces, missing output, or diverging CLI behavior are - regressions to fix. - 4. `git status --short` and `git diff --stat` have been inspected, and - every changed file is intentional for the upgrade PR. Pay special - attention to generated NAPI binding files, because later CI runs fail - if a build produces uncommitted binding output changes. - - If any of the four above fails, diagnose the root cause, fix it, and re-run - the final validation. Do not exit with the task marked complete otherwise. + ### Validation during fixups + Rebuild the affected packages and run focused tests for each fix. For + tsdown or declaration-generator changes, run `just snapshot-test + command_pack_dts_compat`, `just snapshot-test command_pack_monorepo`, + and `just snapshot-test migration_from_tsup_monorepo_success`. + A successful internal build does + not prove that `vp pack` works with consumers' TypeScript versions. + + Re-record with `UPDATE_SNAPSHOTS=1` only for cosmetic drift from the + upgrade (e.g. a bumped version string in help output). Unexpected stack + traces, missing output, or diverging CLI behavior are regressions to fix. + Inspect `git status --short` and `git diff --stat` before finishing; + every changed file, including generated NAPI bindings, must be intentional. + + The workflow's separate Final validation step runs the full build, unit + tests, and snapshot suite after this action. Do not launch the full + snapshot suite here or leave any validation command running in the background. ### Running long commands (IMPORTANT) Run every long-running command (`just build`, `pnpm bootstrap-cli:ci`, @@ -245,6 +247,21 @@ jobs: continue-on-error: true run: pnpm fmt + - name: Final validation + id: final-validation + timeout-minutes: 90 + # Keep broken upgrades available for review, but only as draft PRs. + continue-on-error: true + shell: bash + env: + RELEASE_BUILD: 'true' + run: | + set -euo pipefail + pnpm bootstrap-cli + export PATH="$HOME/.vite-plus/bin:$PATH" + pnpm test:unit + just snapshot-test + - name: Enhance PR description with Kimi K3 id: enhance-pr-description continue-on-error: true @@ -400,6 +417,7 @@ jobs: base: main branch: deps/upstream-update title: 'feat(deps): upgrade upstream dependencies' + draft: ${{ steps.final-validation.outcome != 'success' }} sign-commits: true token: ${{ steps.app-token.outputs.token }} body: ${{ steps.pr-content.outputs.body }} @@ -411,3 +429,10 @@ jobs: GH_TOKEN: ${{ steps.app-token.outputs.token }} PR_NUMBER: ${{ steps.create-pr.outputs.pull-request-number }} run: gh pr comment "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --body-file "${UPGRADE_DEPS_META_DIR}/cli-help-report.md" + + - name: Report validation failure + if: always() && steps.final-validation.outcome != 'success' + shell: bash + run: | + echo '::error::The upgraded dependencies did not pass final validation.' + exit 1 diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/package.json new file mode 100644 index 0000000000..8d8d3c67dc --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/package.json @@ -0,0 +1,12 @@ +{ + "name": "command-pack-dts-compat", + "version": "1.0.0", + "private": true, + "type": "module", + "types": "./dist/index.d.mts", + "devDependencies": { + "typescript": "7.0.2", + "vite-plus": "latest" + }, + "packageManager": "pnpm@11.24.0" +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/pnpm-workspace.yaml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/pnpm-workspace.yaml new file mode 100644 index 0000000000..6739b220f6 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +hoist: false +minimumReleaseAge: 0 diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/snapshots.toml new file mode 100644 index 0000000000..e282c45345 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/snapshots.toml @@ -0,0 +1,24 @@ +[[case]] +name = "command_pack_dts_typescript_7" +vp = "global" +local-registry = true +comment = "The packed CLI generates inferred declarations with stable TypeScript 7.0.2 and automatic generator selection." +steps = [ + { argv = ["vp", "install", "--ignore-scripts"], snapshot = false }, + ["vp", "pack"], + ["vpt", "print-file", "dist/index.d.mts"], +] + +[[case]] +name = "command_pack_dts_native_preview" +vp = "global" +local-registry = true +env = { PACK_DTS_GENERATOR = "tsgo" } +comment = "An explicit tsgo generator uses native-preview with TypeScript 5.9.3. pnpm hoist:false also verifies core declares the optional native-preview peer." +steps = [ + { argv = ["vpt", "json-edit", "package.json", "devDependencies.typescript", "5.9.3"], snapshot = false }, + { argv = ["vpt", "json-edit", "package.json", "devDependencies.@typescript/native-preview", "7.0.0-dev.20260707.2"], snapshot = false }, + { argv = ["vp", "install", "--ignore-scripts"], snapshot = false }, + ["vp", "pack"], + ["vpt", "print-file", "dist/index.d.mts"], +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/snapshots/command_pack_dts_native_preview.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/snapshots/command_pack_dts_native_preview.md new file mode 100644 index 0000000000..4041b06a68 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/snapshots/command_pack_dts_native_preview.md @@ -0,0 +1,37 @@ +# command_pack_dts_native_preview + +An explicit tsgo generator uses native-preview with TypeScript 5.9.3. pnpm hoist:false also verifies core declares the optional native-preview peer. + +## `vpt json-edit package.json devDependencies.typescript 5.9.3` + + +## `vpt json-edit package.json devDependencies.@typescript/native-preview 7.0.0-dev.20260707.2` + + +## `vp install --ignore-scripts` + + +## `vp pack` + +``` +VITE+ - The Unified Toolchain for the Web + +ℹ entry: src/index.ts +ℹ tsconfig: tsconfig.json +ℹ Build start +warn: TypeScript 7.0 does not yet have a stable API and is experimental. Some options will be unavailable. +ℹ Emit types with @typescript/native-preview@7.0.0-dev.20260707.2 +ℹ dist/index.mjs kB │ gzip: kB +ℹ dist/index.d.mts kB │ gzip: kB +ℹ 2 files, total: kB +✔ Build complete in +``` + +## `vpt print-file dist/index.d.mts` + +``` +export declare function greet(name: string): { + message: string; + length: number; +}; +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/snapshots/command_pack_dts_typescript_7.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/snapshots/command_pack_dts_typescript_7.md new file mode 100644 index 0000000000..3c79d1a935 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/snapshots/command_pack_dts_typescript_7.md @@ -0,0 +1,31 @@ +# command_pack_dts_typescript_7 + +The packed CLI generates inferred declarations with stable TypeScript 7.0.2 and automatic generator selection. + +## `vp install --ignore-scripts` + + +## `vp pack` + +``` +VITE+ - The Unified Toolchain for the Web + +ℹ entry: src/index.ts +ℹ tsconfig: tsconfig.json +ℹ Build start +warn: TypeScript 7.0 does not yet have a stable API and is experimental. Some options will be unavailable. +ℹ Emit types with typescript@7.0.2 +ℹ dist/index.mjs kB │ gzip: kB +ℹ dist/index.d.mts kB │ gzip: kB +ℹ 2 files, total: kB +✔ Build complete in +``` + +## `vpt print-file dist/index.d.mts` + +``` +export declare function greet(name: string): { + message: string; + length: number; +}; +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/src/format.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/src/format.ts new file mode 100644 index 0000000000..72b05501b5 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/src/format.ts @@ -0,0 +1,3 @@ +export function format(name: string) { + return `Hello, ${name}!`; +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/src/index.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/src/index.ts new file mode 100644 index 0000000000..62350dd8d8 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/src/index.ts @@ -0,0 +1,6 @@ +import { format } from './format.js'; + +// The declaration generator must infer exported types across module boundaries. +export function greet(name: string) { + return { message: format(name), length: name.length }; +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/tsconfig.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/tsconfig.json new file mode 100644 index 0000000000..9f829f8194 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "NodeNext", + "strict": true, + "declaration": true + }, + "include": ["src"] +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/vite.config.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/vite.config.ts new file mode 100644 index 0000000000..f5a152ca92 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_dts_compat/vite.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + pack: { + entry: ['src/index.ts'], + dts: process.env.PACK_DTS_GENERATOR === 'tsgo' ? { generator: 'tsgo' } : true, + }, +}); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_script/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_script/snapshots.toml index 07ddd0997e..23660de5d9 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_script/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_script/snapshots.toml @@ -77,6 +77,20 @@ steps = [ { argv = ["vpx", "./src/require-ts-dependency.cts"], comment = "A CommonJS TypeScript dependency loaded with require()" }, ] +[[case]] +name = "vpx_script_async_loader_settings" +vp = ["local", "global"] +env = { VP_NODE_VERSION = "22.18.0" } +comment = "Off-thread ESM hooks receive the CommonJS preload's tsconfig selection and TypeScript dependency policy." +steps = [ + { argv = ["vpx", "--tsconfig", "tsconfig.alt.json", "src/resolution.ts"], comment = "The loader thread honors --tsconfig instead of discovering the default config" }, + { argv = ["vpx", "./src/print-setup.ts"], envs = [["TS_NODE_PROJECT", "missing.json"]], comment = "A missing environment-selected config keeps its TS_NODE_PROJECT diagnostic label" }, + { argv = ["vpt", "mkdir", "-p", "node_modules/ts-dep"], snapshot = false }, + { argv = ["vpt", "write-file", "node_modules/ts-dep/package.json", "{ \"name\": \"ts-dep\", \"type\": \"module\", \"exports\": \"./index.ts\" }\n"], snapshot = false }, + { argv = ["vpt", "write-file", "node_modules/ts-dep/index.ts", "export enum Level { High = 'high' }\nexport const level: Level = Level.High;\n"], snapshot = false }, + { argv = ["vpx", "./src/ts-dependency.ts"], comment = "The loader thread transforms published TypeScript dependencies" }, +] + [[case]] name = "vpx_script_tsconfig_transforms" vp = ["local", "global"] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_script/snapshots/vpx_script_async_loader_settings.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_script/snapshots/vpx_script_async_loader_settings.global.md new file mode 100644 index 0000000000..8859ed48c2 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_script/snapshots/vpx_script_async_loader_settings.global.md @@ -0,0 +1,41 @@ +# vpx_script_async_loader_settings + +Off-thread ESM hooks receive the CommonJS preload's tsconfig selection and TypeScript dependency policy. + +## `vpx --tsconfig tsconfig.alt.json src/resolution.ts` + +The loader thread honors --tsconfig instead of discovering the default config + +``` +hello vpx from alt +4 6 42 cts counter=1 +``` + +## `TS_NODE_PROJECT=missing.json vpx ./src/print-setup.ts` + +A missing environment-selected config keeps its TS_NODE_PROJECT diagnostic label + +``` +oxc-node: warning: the tsconfig TS_NODE_PROJECT names, /missing.json, does not exist; running without a tsconfig +no preload +``` + +## `vpt mkdir -p node_modules/ts-dep` + + +## `vpt write-file node_modules/ts-dep/package.json '{ "name": "ts-dep", "type": "module", "exports": "./index.ts" } +'` + + +## `vpt write-file node_modules/ts-dep/index.ts 'export enum Level { High = '\''high'\'' } +export const level: Level = Level.High; +'` + + +## `vpx ./src/ts-dependency.ts` + +The loader thread transforms published TypeScript dependencies + +``` +ts-dep: high +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_script/snapshots/vpx_script_async_loader_settings.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_script/snapshots/vpx_script_async_loader_settings.local.md new file mode 100644 index 0000000000..8859ed48c2 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_script/snapshots/vpx_script_async_loader_settings.local.md @@ -0,0 +1,41 @@ +# vpx_script_async_loader_settings + +Off-thread ESM hooks receive the CommonJS preload's tsconfig selection and TypeScript dependency policy. + +## `vpx --tsconfig tsconfig.alt.json src/resolution.ts` + +The loader thread honors --tsconfig instead of discovering the default config + +``` +hello vpx from alt +4 6 42 cts counter=1 +``` + +## `TS_NODE_PROJECT=missing.json vpx ./src/print-setup.ts` + +A missing environment-selected config keeps its TS_NODE_PROJECT diagnostic label + +``` +oxc-node: warning: the tsconfig TS_NODE_PROJECT names, /missing.json, does not exist; running without a tsconfig +no preload +``` + +## `vpt mkdir -p node_modules/ts-dep` + + +## `vpt write-file node_modules/ts-dep/package.json '{ "name": "ts-dep", "type": "module", "exports": "./index.ts" } +'` + + +## `vpt write-file node_modules/ts-dep/index.ts 'export enum Level { High = '\''high'\'' } +export const level: Level = Level.High; +'` + + +## `vpx ./src/ts-dependency.ts` + +The loader thread transforms published TypeScript dependencies + +``` +ts-dep: high +``` diff --git a/packages/cli/BUNDLING.md b/packages/cli/BUNDLING.md index 58e854fb7a..6c29cac777 100644 --- a/packages/cli/BUNDLING.md +++ b/packages/cli/BUNDLING.md @@ -312,8 +312,8 @@ A binding built by `pnpm build` contains: `vpx