Repository: voidzero-dev/vite-plus
Branch: main
Commit: a5b2e1f2b425
Files: 1701
Total size: 4.3 MB
Directory structure:
gitextract_0d0lq36w/
├── .cargo/
│ └── config.toml
├── .claude/
│ ├── agents/
│ │ ├── cargo-workspace-merger.md
│ │ └── monorepo-architect.md
│ └── skills/
│ ├── add-ecosystem-ci/
│ │ └── SKILL.md
│ ├── bump-vite-task/
│ │ └── SKILL.md
│ ├── spawn-process/
│ │ └── SKILL.md
│ └── sync-tsdown-cli/
│ └── SKILL.md
├── .clippy.toml
├── .devcontainer/
│ └── devcontainer.json
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ ├── docs.yml
│ │ └── feature_request.yml
│ ├── actions/
│ │ ├── build-upstream/
│ │ │ └── action.yml
│ │ ├── clone/
│ │ │ └── action.yml
│ │ ├── download-rolldown-binaries/
│ │ │ └── action.yml
│ │ └── set-snapshot-version/
│ │ ├── action.yml
│ │ ├── compute-version.mjs
│ │ └── package.json
│ ├── renovate.json
│ ├── scripts/
│ │ └── upgrade-deps.mjs
│ └── workflows/
│ ├── ci.yml
│ ├── claude.yml
│ ├── cleanup-cache.yml
│ ├── deny.yml
│ ├── e2e-test.yml
│ ├── issue-close-require.yml
│ ├── issue-labeled.yml
│ ├── release.yml
│ ├── test-standalone-install.yml
│ ├── upgrade-deps.yml
│ └── zizmor.yml
├── .gitignore
├── .husky/
│ └── pre-commit
├── .node-version
├── .rustfmt.toml
├── .typos.toml
├── .vscode/
│ ├── extensions.json
│ └── settings.json
├── CLAUDE.md
├── CONTRIBUTING.md
├── Cargo.toml
├── LICENSE
├── README.md
├── bench/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── benches/
│ │ └── workspace_load.rs
│ ├── fixtures/
│ │ └── monorepo/
│ │ ├── package.json
│ │ ├── pnpm-workspace.yaml
│ │ └── vite-plus.json
│ ├── generate-monorepo.ts
│ ├── package.json
│ └── tsconfig.json
├── crates/
│ ├── vite_command/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── vite_error/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── vite_global_cli/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── cli.rs
│ │ ├── command_picker.rs
│ │ ├── commands/
│ │ │ ├── add.rs
│ │ │ ├── config.rs
│ │ │ ├── create.rs
│ │ │ ├── dedupe.rs
│ │ │ ├── delegate.rs
│ │ │ ├── dlx.rs
│ │ │ ├── env/
│ │ │ │ ├── bin_config.rs
│ │ │ │ ├── config.rs
│ │ │ │ ├── current.rs
│ │ │ │ ├── default.rs
│ │ │ │ ├── doctor.rs
│ │ │ │ ├── exec.rs
│ │ │ │ ├── global_install.rs
│ │ │ │ ├── list.rs
│ │ │ │ ├── list_remote.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── off.rs
│ │ │ │ ├── on.rs
│ │ │ │ ├── package_metadata.rs
│ │ │ │ ├── packages.rs
│ │ │ │ ├── pin.rs
│ │ │ │ ├── setup.rs
│ │ │ │ ├── unpin.rs
│ │ │ │ ├── use.rs
│ │ │ │ └── which.rs
│ │ │ ├── implode.rs
│ │ │ ├── install.rs
│ │ │ ├── link.rs
│ │ │ ├── migrate.rs
│ │ │ ├── mod.rs
│ │ │ ├── outdated.rs
│ │ │ ├── pm.rs
│ │ │ ├── remove.rs
│ │ │ ├── run_or_delegate.rs
│ │ │ ├── staged.rs
│ │ │ ├── unlink.rs
│ │ │ ├── update.rs
│ │ │ ├── upgrade/
│ │ │ │ ├── install.rs
│ │ │ │ ├── integrity.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── platform.rs
│ │ │ │ └── registry.rs
│ │ │ ├── version.rs
│ │ │ ├── vpx.rs
│ │ │ └── why.rs
│ │ ├── error.rs
│ │ ├── help.rs
│ │ ├── js_executor.rs
│ │ ├── main.rs
│ │ ├── shim/
│ │ │ ├── cache.rs
│ │ │ ├── dispatch.rs
│ │ │ ├── exec.rs
│ │ │ └── mod.rs
│ │ └── tips/
│ │ ├── mod.rs
│ │ ├── short_aliases.rs
│ │ └── use_vpx_or_run.rs
│ ├── vite_install/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ ├── commands/
│ │ │ ├── add.rs
│ │ │ ├── audit.rs
│ │ │ ├── cache.rs
│ │ │ ├── config.rs
│ │ │ ├── dedupe.rs
│ │ │ ├── deprecate.rs
│ │ │ ├── dist_tag.rs
│ │ │ ├── dlx.rs
│ │ │ ├── fund.rs
│ │ │ ├── install.rs
│ │ │ ├── link.rs
│ │ │ ├── list.rs
│ │ │ ├── login.rs
│ │ │ ├── logout.rs
│ │ │ ├── mod.rs
│ │ │ ├── outdated.rs
│ │ │ ├── owner.rs
│ │ │ ├── pack.rs
│ │ │ ├── ping.rs
│ │ │ ├── prune.rs
│ │ │ ├── publish.rs
│ │ │ ├── rebuild.rs
│ │ │ ├── remove.rs
│ │ │ ├── run.rs
│ │ │ ├── search.rs
│ │ │ ├── token.rs
│ │ │ ├── unlink.rs
│ │ │ ├── update.rs
│ │ │ ├── view.rs
│ │ │ ├── whoami.rs
│ │ │ └── why.rs
│ │ ├── config.rs
│ │ ├── lib.rs
│ │ ├── main.rs
│ │ ├── package_manager.rs
│ │ ├── request.rs
│ │ └── shim.rs
│ ├── vite_js_runtime/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── cache.rs
│ │ ├── dev_engines.rs
│ │ ├── download.rs
│ │ ├── error.rs
│ │ ├── lib.rs
│ │ ├── platform.rs
│ │ ├── provider.rs
│ │ ├── providers/
│ │ │ ├── mod.rs
│ │ │ └── node.rs
│ │ └── runtime.rs
│ ├── vite_migration/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── ast_grep.rs
│ │ ├── eslint.rs
│ │ ├── file_walker.rs
│ │ ├── import_rewriter.rs
│ │ ├── lib.rs
│ │ ├── package.rs
│ │ ├── prettier.rs
│ │ ├── script_rewrite.rs
│ │ └── vite_config.rs
│ ├── vite_shared/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── env_config.rs
│ │ ├── env_vars.rs
│ │ ├── header.rs
│ │ ├── home.rs
│ │ ├── lib.rs
│ │ ├── output.rs
│ │ ├── package_json.rs
│ │ ├── path_env.rs
│ │ ├── string_similarity.rs
│ │ └── tracing.rs
│ ├── vite_static_config/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ └── lib.rs
│ └── vite_trampoline/
│ ├── Cargo.toml
│ └── src/
│ └── main.rs
├── deny.toml
├── docs/
│ ├── .gitignore
│ ├── .vitepress/
│ │ ├── config.mts
│ │ ├── env.d.ts
│ │ ├── theme/
│ │ │ ├── Layout.vue
│ │ │ ├── assets/
│ │ │ │ └── animations/
│ │ │ │ ├── 1280_x_580_vite+_masthead.riv
│ │ │ │ ├── 253_x_268_vite+_masthead_mobile.riv
│ │ │ │ ├── 514_x_246_focus_on_shipping_v2.riv
│ │ │ │ └── 561_x_273_stay_fast_at_scale.riv
│ │ │ ├── components/
│ │ │ │ ├── Footer.vue
│ │ │ │ └── home/
│ │ │ │ ├── CoreFeature3Col.vue
│ │ │ │ ├── FeatureCheck.vue
│ │ │ │ ├── FeatureDevBuild.vue
│ │ │ │ ├── FeaturePack.vue
│ │ │ │ ├── FeatureRun.vue
│ │ │ │ ├── FeatureRunTerminal.vue
│ │ │ │ ├── FeatureTest.vue
│ │ │ │ ├── FeatureToolbar.vue
│ │ │ │ ├── Fullstack2Col.vue
│ │ │ │ ├── HeadingSection2.vue
│ │ │ │ ├── HeadingSection3.vue
│ │ │ │ ├── HeadingSection4.vue
│ │ │ │ ├── Hero.vue
│ │ │ │ ├── HeroRive.vue
│ │ │ │ ├── InstallCommand.vue
│ │ │ │ ├── PartnerLogos.vue
│ │ │ │ ├── ProductivityGrid.vue
│ │ │ │ ├── StackedBlock.vue
│ │ │ │ ├── Terminal.vue
│ │ │ │ ├── TerminalTranscript.vue
│ │ │ │ └── Testimonials.vue
│ │ │ ├── data/
│ │ │ │ ├── feature-run-transcripts.ts
│ │ │ │ ├── performance.ts
│ │ │ │ ├── terminal-transcripts.ts
│ │ │ │ └── testimonials.ts
│ │ │ ├── index.ts
│ │ │ ├── layouts/
│ │ │ │ ├── Error404.vue
│ │ │ │ └── Home.vue
│ │ │ └── styles.css
│ │ └── tsconfig.json
│ ├── config/
│ │ ├── build.md
│ │ ├── fmt.md
│ │ ├── index.md
│ │ ├── lint.md
│ │ ├── pack.md
│ │ ├── run.md
│ │ ├── staged.md
│ │ └── test.md
│ ├── guide/
│ │ ├── build.md
│ │ ├── cache.md
│ │ ├── check.md
│ │ ├── ci.md
│ │ ├── commit-hooks.md
│ │ ├── create.md
│ │ ├── dev.md
│ │ ├── env.md
│ │ ├── fmt.md
│ │ ├── ide-integration.md
│ │ ├── implode.md
│ │ ├── index.md
│ │ ├── install.md
│ │ ├── lint.md
│ │ ├── migrate.md
│ │ ├── pack.md
│ │ ├── run.md
│ │ ├── test.md
│ │ ├── troubleshooting.md
│ │ ├── upgrade.md
│ │ ├── vpx.md
│ │ └── why.md
│ ├── index.md
│ ├── package.json
│ ├── pnpm-workspace.yaml
│ └── public/
│ └── _redirects
├── ecosystem-ci/
│ ├── clone.ts
│ ├── patch-project.ts
│ ├── paths.ts
│ ├── repo.json
│ └── verify-install.ts
├── justfile
├── netlify.toml
├── package.json
├── packages/
│ ├── cli/
│ │ ├── .gitignore
│ │ ├── AGENTS.md
│ │ ├── BUNDLING.md
│ │ ├── README.md
│ │ ├── bin/
│ │ │ ├── oxfmt
│ │ │ ├── oxlint
│ │ │ └── vp
│ │ ├── binding/
│ │ │ ├── .gitignore
│ │ │ ├── Cargo.toml
│ │ │ ├── build.rs
│ │ │ ├── index.cjs
│ │ │ ├── index.d.cts
│ │ │ ├── index.d.ts
│ │ │ ├── index.js
│ │ │ └── src/
│ │ │ ├── cli.rs
│ │ │ ├── exec/
│ │ │ │ ├── args.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── workspace.rs
│ │ │ ├── lib.rs
│ │ │ ├── migration.rs
│ │ │ ├── package_manager.rs
│ │ │ └── utils.rs
│ │ ├── build.ts
│ │ ├── install.ps1
│ │ ├── install.sh
│ │ ├── package.json
│ │ ├── publish-native-addons.ts
│ │ ├── rolldown.config.ts
│ │ ├── rules/
│ │ │ ├── vite-prepare.yml
│ │ │ └── vite-tools.yml
│ │ ├── skills/
│ │ │ └── vite-plus/
│ │ │ └── SKILL.md
│ │ ├── snap-tests/
│ │ │ ├── bin-oxfmt-wrapper/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── bin-oxlint-wrapper/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── build-vite-env/
│ │ │ │ ├── index.html
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── cache-clean/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ ├── steps.json
│ │ │ │ ├── subfolder/
│ │ │ │ │ └── .gitkeep
│ │ │ │ └── vite.config.ts
│ │ │ ├── cache-scripts-default/
│ │ │ │ ├── hello.mjs
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── cache-scripts-enabled/
│ │ │ │ ├── hello.mjs
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── change-passthrough-env-config/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── check-all-skipped/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── check-fail-fast/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ └── steps.json
│ │ │ ├── check-fix/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ └── steps.json
│ │ │ ├── check-fix-missing-stderr/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── check-fix-paths/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ └── steps.json
│ │ │ ├── check-fix-reformat/
│ │ │ │ ├── .oxlintrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ └── steps.json
│ │ │ ├── check-fmt-fail/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ └── steps.json
│ │ │ ├── check-lint-fail/
│ │ │ │ ├── .oxlintrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ └── steps.json
│ │ │ ├── check-lint-fail-no-typecheck/
│ │ │ │ ├── .oxlintrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── check-lint-fail-typecheck/
│ │ │ │ ├── .oxlintrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── check-lint-warn/
│ │ │ │ ├── .oxlintrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ └── steps.json
│ │ │ ├── check-no-fmt/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ └── steps.json
│ │ │ ├── check-no-lint/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ └── steps.json
│ │ │ ├── check-oxlint-env/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── check-pass/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ └── steps.json
│ │ │ ├── check-pass-no-typecheck/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── check-pass-typecheck/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── check-pass-typecheck-github-actions/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.js
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── cli-helper-message/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-dev-with-port/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-doc/
│ │ │ │ ├── api-examples.md
│ │ │ │ ├── index.md
│ │ │ │ ├── markdown-examples.md
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── command-exec/
│ │ │ │ ├── package.json
│ │ │ │ ├── setup-bin.js
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-exec-cwd/
│ │ │ │ ├── package.json
│ │ │ │ ├── setup.js
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-exec-monorepo/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app-a/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── app-b/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── lib-c/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-exec-monorepo-filter-v2/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app-a/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── app-b/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── lib-c/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-exec-monorepo-order/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app-mobile/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── app-web/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── cycle-a/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── cycle-b/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── cycle-c/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── cycle-d/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── cycle-e/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── lib-core/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── lib-ui/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── lib-utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-helper/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-init-inline-config/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-init-inline-config-existing/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── command-install-shortcut/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── command-pack/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ ├── hello.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── command-pack-external/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ └── steps.json
│ │ │ ├── command-pack-monorepo/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── array-config/
│ │ │ │ │ │ ├── package.json
│ │ │ │ │ │ ├── src/
│ │ │ │ │ │ │ └── sub/
│ │ │ │ │ │ │ ├── hello.ts
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ └── vite.config.ts
│ │ │ │ │ ├── default-config/
│ │ │ │ │ │ ├── package.json
│ │ │ │ │ │ └── src/
│ │ │ │ │ │ ├── hello.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ └── hello/
│ │ │ │ │ ├── package.json
│ │ │ │ │ ├── src/
│ │ │ │ │ │ ├── hello.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ └── vite.config.ts
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── command-pack-no-input/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-preview/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-run-with-vp-config/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-version/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-vp-alias/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── exit-code/
│ │ │ │ ├── failure.js
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── fingerprint-ignore-test/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── fmt-check-with-vite-config/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── valid.js
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── fmt-ignore-patterns/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ ├── ignored/
│ │ │ │ │ │ └── badly-formatted.js
│ │ │ │ │ └── valid.js
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── ignore_dist/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── lint-ignore-patterns/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ ├── ignored/
│ │ │ │ │ │ └── has-error.js
│ │ │ │ │ └── valid.js
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── lint-vite-config-rules/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ ├── has-console.js
│ │ │ │ │ └── valid.js
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── npm-install-with-options/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── oxlint-typeaware/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ ├── steps.json
│ │ │ │ ├── types.ts
│ │ │ │ └── vite.config.ts
│ │ │ ├── pass-no-color-env/
│ │ │ │ ├── check.js
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── plain-terminal-ui/
│ │ │ │ ├── hello.mjs
│ │ │ │ ├── input.txt
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ ├── subfolder/
│ │ │ │ │ └── hello.mjs
│ │ │ │ └── vite.config.ts
│ │ │ ├── plain-terminal-ui-nested/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── a.ts
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── run-task-command-conflict/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── synthetic-build-cache-disabled/
│ │ │ │ ├── index.html
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── synthetic-dev-cache-disabled/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── task-config-cwd/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ ├── subfolder/
│ │ │ │ │ └── a.js
│ │ │ │ └── vite.config.ts
│ │ │ ├── test-nested-tasks/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── vite-config-task/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── vite-task-path-env-include-pm/
│ │ │ │ ├── main.js
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── vitest-browser-mode/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ ├── bar.js
│ │ │ │ │ ├── foo.js
│ │ │ │ │ └── foo.test.js
│ │ │ │ ├── steps.json
│ │ │ │ ├── vite.config.ts
│ │ │ │ └── vitest.config.ts
│ │ │ ├── vp-run-expansion/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── workspace-lint-subpackage/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ └── app-a/
│ │ │ │ │ ├── package.json
│ │ │ │ │ ├── src/
│ │ │ │ │ │ └── index.js
│ │ │ │ │ └── vite.config.ts
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── workspace-root-vite-config/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app-a/
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── app-b/
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ └── yarn-install-with-options/
│ │ │ ├── package.json
│ │ │ ├── snap.txt
│ │ │ ├── steps.json
│ │ │ └── vite.config.ts
│ │ ├── snap-tests-global/
│ │ │ ├── cli-helper-message/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-add-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-add-npm10-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-add-npm11/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-add-npm11-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-add-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-add-pnpm10-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-add-pnpm9/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-add-pnpm9-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-add-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-add-yarn4-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── admin/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-cache-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-cache-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-cache-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-check-help/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-config-custom-dir-hook-path/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-config-help/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-config-no-agent-writes/
│ │ │ │ ├── CLAUDE.md
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-config-npm10/
│ │ │ │ ├── .npmrc
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-config-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-config-prepare-auto-hooks/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-config-replace-husky-hookspath/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-config-update-agents/
│ │ │ │ ├── AGENTS.md
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-config-yarn1/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-config-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-create-help/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-dedupe-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-dedupe-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-dedupe-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-dlx-no-package-json/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-dlx-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-dlx-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-dlx-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-env-exec/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-env-exec-shim-mode/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-env-install-conflict/
│ │ │ │ ├── conflict-pkg/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-env-install-fail/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-env-install-no-arg/
│ │ │ │ ├── .node-version
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-env-install-no-arg-fail/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-env-install-node-version/
│ │ │ │ ├── command-env-install-node-version-pkg/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-env-install-version-alias/
│ │ │ │ ├── command-env-install-version-alias-pkg/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-env-use/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-env-which/
│ │ │ │ ├── .node-version
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-exec/
│ │ │ │ ├── package.json
│ │ │ │ ├── setup-bin.js
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-fmt-help/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-install-auto-create-package-json/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-install-bug-31/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-link-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-link-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-link-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-lint-help/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-list-no-package-json/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-list-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-list-npm10-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-list-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-list-pnpm10-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-list-yarn1/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-list-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-outdated-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-outdated-npm10-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-outdated-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-outdated-pnpm10-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-outdated-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-owner-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-owner-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-owner-yarn1/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-owner-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-pack-exe/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── command-pack-exe-error/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── command-pack-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-pack-npm10-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-pack-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-pack-pnpm10-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-pack-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-pack-yarn4-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-pm-no-package-json/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-prune-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-prune-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-prune-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-publish-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-publish-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-publish-yarn1/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-publish-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-remove-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-remove-npm10-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-remove-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-remove-pnpm10-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-remove-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-remove-yarn4-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── admin/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-run-without-vite-plus/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-staged-broken-config/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-staged-help/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-staged-no-config/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-staged-with-config/
│ │ │ │ ├── .oxlintrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── command-unlink-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-unlink-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-unlink-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-update-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-update-npm10-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-update-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-update-pnpm10-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-update-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-update-yarn4-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-upgrade-check/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-upgrade-rollback/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-version-no-side-effects/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-version-with-env/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-view-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-view-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-view-yarn1/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-view-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-vpx-no-package-json/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-vpx-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-why-npm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-why-npm10-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-why-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-why-pnpm10-with-workspace/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── command-why-yarn4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── create-from-monorepo-subdir/
│ │ │ │ ├── apps/
│ │ │ │ │ └── website/
│ │ │ │ │ └── package.json
│ │ │ │ ├── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── scripts/
│ │ │ │ │ └── helper/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── create-from-nonworkspace-subdir/
│ │ │ │ ├── package.json
│ │ │ │ ├── scripts/
│ │ │ │ │ └── .keep
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── create-generator-outside-monorepo/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── create-missing-typecheck/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── delegate-respects-default-node-version/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── dev-engines-runtime-pnpm10/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── env-install-binary-conflict/
│ │ │ │ ├── .node-version
│ │ │ │ ├── env-binary-conflict-pkg-a/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── env-binary-conflict-pkg-b/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── fallback-all-invalid-to-user-default/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── fallback-invalid-engines-to-dev-engines/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── global-cli-fallback/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-add-git-hooks/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-agent-claude/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ └── steps.json
│ │ │ ├── migration-already-vite-plus/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-already-vite-plus-with-husky-hookspath/
│ │ │ │ ├── .husky/
│ │ │ │ │ └── pre-commit
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-already-vite-plus-with-husky-lint-staged/
│ │ │ │ ├── .husky/
│ │ │ │ │ └── pre-commit
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-auto-create-vite-config/
│ │ │ │ ├── .oxfmtrc.json
│ │ │ │ ├── .oxlintrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-baseurl-tsconfig/
│ │ │ │ ├── .oxlintrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── tsconfig.json
│ │ │ ├── migration-chained-lint-staged-pre-commit/
│ │ │ │ ├── .husky/
│ │ │ │ │ └── pre-commit
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-check/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-composed-husky-custom-dir/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-composed-husky-prepare/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-env-prefix-lint-staged/
│ │ │ │ ├── .husky/
│ │ │ │ │ └── pre-commit
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-eslint/
│ │ │ │ ├── eslint.config.mjs
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-eslint-legacy/
│ │ │ │ ├── .eslintrc
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-eslint-legacy-already-vite-plus/
│ │ │ │ ├── .eslintrc
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-eslint-lint-staged/
│ │ │ │ ├── eslint.config.mjs
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-eslint-lint-staged-mjs/
│ │ │ │ ├── eslint.config.mjs
│ │ │ │ ├── lint-staged.config.mjs
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-eslint-lintstagedrc/
│ │ │ │ ├── .lintstagedrc.json
│ │ │ │ ├── eslint.config.mjs
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-eslint-monorepo/
│ │ │ │ ├── eslint.config.mjs
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-eslint-monorepo-package-only/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ └── app/
│ │ │ │ │ ├── eslint.config.mjs
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-eslint-npx-wrapper/
│ │ │ │ ├── eslint.config.mjs
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-eslint-rerun/
│ │ │ │ ├── eslint.config.mjs
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-eslint-rerun-dual-config/
│ │ │ │ ├── .eslintrc
│ │ │ │ ├── eslint.config.mjs
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-eslint-rerun-mjs/
│ │ │ │ ├── eslint.config.mjs
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.mjs
│ │ │ ├── migration-existing-husky/
│ │ │ │ ├── .husky/
│ │ │ │ │ └── pre-commit
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-existing-husky-lint-staged/
│ │ │ │ ├── .husky/
│ │ │ │ │ └── pre-commit
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-existing-husky-v8-hooks/
│ │ │ │ ├── .husky/
│ │ │ │ │ └── pre-commit
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-existing-husky-v8-multi-hooks/
│ │ │ │ ├── .husky/
│ │ │ │ │ ├── commit-msg
│ │ │ │ │ └── pre-commit
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-existing-lint-staged-config/
│ │ │ │ ├── .lintstagedrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-existing-pnpm-exec-lint-staged/
│ │ │ │ ├── .husky/
│ │ │ │ │ └── pre-commit
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-existing-pre-commit/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-existing-prepare-script/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-from-tsdown/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ ├── steps.json
│ │ │ │ └── tsdown.config.ts
│ │ │ ├── migration-from-tsdown-json-config/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ ├── steps.json
│ │ │ │ ├── tsdown.config.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── migration-from-vitest-config/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vitest.config.ts
│ │ │ ├── migration-from-vitest-files/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── test/
│ │ │ │ └── hello.ts
│ │ │ ├── migration-hooks-skip-on-existing-hookspath/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-husky-env-skip/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-husky-or-prepare/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-husky-semicolon-prepare/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-husky-v8-preserves-lint-staged/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-lint-staged-in-scripts/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-lint-staged-merge-fail/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── migration-lint-staged-ts-config/
│ │ │ │ ├── lint-staged.config.ts
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-lintstagedrc-json/
│ │ │ │ ├── .lintstagedrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-lintstagedrc-merge-fail/
│ │ │ │ ├── .lintstagedrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── migration-lintstagedrc-not-support/
│ │ │ │ ├── .lintstagedrc
│ │ │ │ ├── .lintstagedrc.yaml
│ │ │ │ ├── lint-staged.config.mjs
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-lintstagedrc-staged-exists/
│ │ │ │ ├── .lintstagedrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── migration-merge-vite-config-js/
│ │ │ │ ├── .oxlintrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.js
│ │ │ ├── migration-merge-vite-config-ts/
│ │ │ │ ├── .oxfmtrc.json
│ │ │ │ ├── .oxlintrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── migration-monorepo-husky-v8-preserves-lint-staged/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ └── app/
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-monorepo-pnpm/
│ │ │ │ ├── .oxfmtrc.json
│ │ │ │ ├── .oxlintrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── only-oxlint/
│ │ │ │ │ │ ├── .oxlintrc.json
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── migration-monorepo-pnpm-overrides-dependency-selector/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ └── app/
│ │ │ │ │ └── package.json
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── migration-monorepo-skip-vite-peer-dependency/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ └── vite-plugin/
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── src/
│ │ │ │ │ └── index.ts
│ │ │ │ ├── pnpm-workspace.yaml
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-monorepo-yarn4/
│ │ │ │ ├── .oxlintrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── utils/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── steps.json
│ │ │ │ └── vite.config.ts
│ │ │ ├── migration-no-agent/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ └── steps.json
│ │ │ ├── migration-no-git-repo/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-no-hooks/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-no-hooks-with-husky/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-not-supported-npm8.2/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-not-supported-pnpm9.4/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-not-supported-vite6/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-not-supported-vitest3/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-other-hook-tool/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-partially-migrated-pre-commit/
│ │ │ │ ├── .husky/
│ │ │ │ │ └── pre-commit
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-pre-commit-env-setup/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-prettier/
│ │ │ │ ├── .prettierrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-prettier-eslint-combo/
│ │ │ │ ├── .prettierrc.json
│ │ │ │ ├── eslint.config.mjs
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-prettier-ignore-unknown/
│ │ │ │ ├── .prettierrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-prettier-lint-staged/
│ │ │ │ ├── .prettierrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-prettier-pkg-json/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-prettier-rerun/
│ │ │ │ ├── .prettierrc.json
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-rewrite-declare-module/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ └── steps.json
│ │ │ ├── migration-rewrite-reference-types/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── env.d.ts
│ │ │ │ └── steps.json
│ │ │ ├── migration-skip-vite-dependency/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ └── steps.json
│ │ │ ├── migration-skip-vite-peer-dependency/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ └── steps.json
│ │ │ ├── migration-standalone-npm/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-subpath/
│ │ │ │ ├── foo/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── migration-vite-version/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── new-check/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── new-create-vite/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── new-create-vite-directory-dot/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── new-create-vite-with-scope-name/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── new-vite-monorepo/
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── npm-global-install-already-linked/
│ │ │ │ ├── .node-version
│ │ │ │ ├── npm-global-linked-pkg/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── npm-global-install-custom-prefix/
│ │ │ │ ├── npm-global-custom-prefix-pkg/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── npm-global-install-custom-prefix-on-path/
│ │ │ │ ├── npm-global-on-path-pkg/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── npm-global-install-dot/
│ │ │ │ ├── npm-global-dot-pkg/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── npm-global-install-hint/
│ │ │ │ ├── npm-global-hint-pkg/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── npm-global-uninstall-link-cleanup/
│ │ │ │ ├── npm-global-uninstall-pkg/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── npm-global-uninstall-preexisting-binary/
│ │ │ │ ├── npm-global-preexist-pkg/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── npm-global-uninstall-prefix/
│ │ │ │ ├── npm-global-prefix-pkg/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── npm-global-uninstall-shared-bin-name/
│ │ │ │ ├── pkg-a/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── pkg-b/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── npm-global-uninstall-vp-managed/
│ │ │ │ ├── npm-global-vp-managed-pkg/
│ │ │ │ │ ├── cli.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── shim-inherits-parent-dev-engines-runtime/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ └── app/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── shim-inherits-parent-engines-node/
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ └── app/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── shim-inherits-parent-node-version/
│ │ │ │ ├── .node-version
│ │ │ │ ├── package.json
│ │ │ │ ├── packages/
│ │ │ │ │ └── app/
│ │ │ │ │ └── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── shim-pnpm-uses-project-node-version/
│ │ │ │ ├── .node-version
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── shim-recursive-npm-run/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ └── shim-recursive-package-binary/
│ │ │ ├── .node-version
│ │ │ ├── recursive-cli-pkg/
│ │ │ │ ├── cli.js
│ │ │ │ └── package.json
│ │ │ ├── snap.txt
│ │ │ └── steps.json
│ │ ├── snap-tests-todo/
│ │ │ ├── command-pack-watch-restart/
│ │ │ │ ├── kill-watch.sh
│ │ │ │ ├── package.json
│ │ │ │ ├── run-watch.sh
│ │ │ │ ├── snap.txt
│ │ │ │ ├── src/
│ │ │ │ │ └── index.ts
│ │ │ │ ├── steps.json
│ │ │ │ ├── vite.config.ts
│ │ │ │ ├── wait-for-dist.sh
│ │ │ │ └── wait-for-dist2.sh
│ │ │ ├── exit-non-zero-on-cmd-not-exists/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ ├── pnpm-install-with-options/
│ │ │ │ ├── package.json
│ │ │ │ ├── snap.txt
│ │ │ │ └── steps.json
│ │ │ └── test-panicked-fix/
│ │ │ ├── package.json
│ │ │ ├── snap.txt
│ │ │ └── steps.json
│ │ ├── src/
│ │ │ ├── __tests__/
│ │ │ │ ├── index.spec.ts
│ │ │ │ ├── init-config.spec.ts
│ │ │ │ ├── pack.spec.ts
│ │ │ │ └── resolve-vite-config.spec.ts
│ │ │ ├── bin.ts
│ │ │ ├── config/
│ │ │ │ ├── __tests__/
│ │ │ │ │ └── hooks.spec.ts
│ │ │ │ ├── bin.ts
│ │ │ │ └── hooks.ts
│ │ │ ├── create/
│ │ │ │ ├── __tests__/
│ │ │ │ │ ├── __snapshots__/
│ │ │ │ │ │ └── utils.spec.ts.snap
│ │ │ │ │ ├── discovery.spec.ts
│ │ │ │ │ ├── initial-template-options.spec.ts
│ │ │ │ │ ├── prompts.spec.ts
│ │ │ │ │ └── utils.spec.ts
│ │ │ │ ├── bin.ts
│ │ │ │ ├── command.ts
│ │ │ │ ├── discovery.ts
│ │ │ │ ├── initial-template-options.ts
│ │ │ │ ├── prompts.ts
│ │ │ │ ├── random-name.ts
│ │ │ │ ├── templates/
│ │ │ │ │ ├── builtin.ts
│ │ │ │ │ ├── generator.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── monorepo.ts
│ │ │ │ │ ├── remote.ts
│ │ │ │ │ └── types.ts
│ │ │ │ └── utils.ts
│ │ │ ├── define-config.ts
│ │ │ ├── index.cts
│ │ │ ├── index.ts
│ │ │ ├── init-config.ts
│ │ │ ├── lint.ts
│ │ │ ├── mcp/
│ │ │ │ └── bin.ts
│ │ │ ├── migration/
│ │ │ │ ├── __tests__/
│ │ │ │ │ ├── __snapshots__/
│ │ │ │ │ │ └── migrator.spec.ts.snap
│ │ │ │ │ ├── compat.spec.ts
│ │ │ │ │ └── migrator.spec.ts
│ │ │ │ ├── bin.ts
│ │ │ │ ├── compat.ts
│ │ │ │ ├── detector.ts
│ │ │ │ ├── migrator.ts
│ │ │ │ └── report.ts
│ │ │ ├── pack-bin.ts
│ │ │ ├── pack.ts
│ │ │ ├── resolve-doc.ts
│ │ │ ├── resolve-fmt.ts
│ │ │ ├── resolve-lint.ts
│ │ │ ├── resolve-pack.ts
│ │ │ ├── resolve-test.ts
│ │ │ ├── resolve-vite-config.ts
│ │ │ ├── resolve-vite.ts
│ │ │ ├── run-config.ts
│ │ │ ├── staged/
│ │ │ │ └── bin.ts
│ │ │ ├── staged-config.ts
│ │ │ ├── types/
│ │ │ │ ├── index.ts
│ │ │ │ ├── package.ts
│ │ │ │ └── workspace.ts
│ │ │ ├── utils/
│ │ │ │ ├── __tests__/
│ │ │ │ │ ├── agent.spec.ts
│ │ │ │ │ ├── editor.spec.ts
│ │ │ │ │ ├── help.spec.ts
│ │ │ │ │ └── package.spec.ts
│ │ │ │ ├── agent.ts
│ │ │ │ ├── command.ts
│ │ │ │ ├── constants.ts
│ │ │ │ ├── editor.ts
│ │ │ │ ├── help.ts
│ │ │ │ ├── json.ts
│ │ │ │ ├── package.ts
│ │ │ │ ├── path.ts
│ │ │ │ ├── prompts.ts
│ │ │ │ ├── skills.ts
│ │ │ │ ├── terminal.ts
│ │ │ │ ├── tsconfig.ts
│ │ │ │ ├── types.ts
│ │ │ │ ├── workspace.ts
│ │ │ │ └── yaml.ts
│ │ │ └── version.ts
│ │ ├── templates/
│ │ │ ├── generator/
│ │ │ │ ├── README.md
│ │ │ │ ├── bin/
│ │ │ │ │ └── index.ts
│ │ │ │ ├── package.json
│ │ │ │ ├── src/
│ │ │ │ │ └── template.ts
│ │ │ │ └── tsconfig.json
│ │ │ └── monorepo/
│ │ │ ├── README.md
│ │ │ ├── _gitignore
│ │ │ ├── _yarnrc.yml
│ │ │ ├── package.json
│ │ │ ├── pnpm-workspace.yaml
│ │ │ ├── tsconfig.json
│ │ │ └── vite.config.ts
│ │ └── tsconfig.json
│ ├── core/
│ │ ├── .gitignore
│ │ ├── BUNDLING.md
│ │ ├── __tests__/
│ │ │ └── build-artifacts.spec.ts
│ │ ├── build-support/
│ │ │ ├── build-cjs-deps.ts
│ │ │ ├── find-create-require.ts
│ │ │ ├── rewrite-imports.ts
│ │ │ └── rewrite-module-specifiers.ts
│ │ ├── build.ts
│ │ ├── package.json
│ │ └── tsconfig.json
│ ├── prompts/
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── __tests__/
│ │ │ │ ├── __snapshots__/
│ │ │ │ │ └── render.spec.ts.snap
│ │ │ │ └── render.spec.ts
│ │ │ ├── autocomplete.ts
│ │ │ ├── box.ts
│ │ │ ├── common.ts
│ │ │ ├── confirm.ts
│ │ │ ├── group-multi-select.ts
│ │ │ ├── group.ts
│ │ │ ├── index.ts
│ │ │ ├── limit-options.ts
│ │ │ ├── log.ts
│ │ │ ├── messages.ts
│ │ │ ├── multi-select.ts
│ │ │ ├── note.ts
│ │ │ ├── password.ts
│ │ │ ├── path.ts
│ │ │ ├── progress-bar.ts
│ │ │ ├── select-key.ts
│ │ │ ├── select.ts
│ │ │ ├── spinner.ts
│ │ │ ├── stream.ts
│ │ │ ├── task-log.ts
│ │ │ ├── task.ts
│ │ │ └── text.ts
│ │ └── tsdown.config.ts
│ ├── test/
│ │ ├── .gitignore
│ │ ├── BUNDLING.md
│ │ ├── __tests__/
│ │ │ └── build-artifacts.spec.ts
│ │ ├── build.ts
│ │ ├── package.json
│ │ └── tsconfig.json
│ └── tools/
│ ├── .upstream-versions.json
│ ├── README.md
│ ├── package.json
│ ├── snap-tests/
│ │ ├── json-sort/
│ │ │ ├── array.json
│ │ │ ├── snap.txt
│ │ │ └── steps.json
│ │ └── replace-file-content/
│ │ ├── foo/
│ │ │ └── example.toml
│ │ ├── snap.txt
│ │ └── steps.json
│ └── src/
│ ├── __tests__/
│ │ ├── __snapshots__/
│ │ │ └── utils.spec.ts.snap
│ │ └── utils.spec.ts
│ ├── bin.js
│ ├── brand-vite.ts
│ ├── index.ts
│ ├── install-global-cli.ts
│ ├── json-edit.ts
│ ├── json-sort.ts
│ ├── merge-peer-deps.ts
│ ├── replace-file-content.ts
│ ├── snap-test.ts
│ ├── sync-remote-deps.ts
│ └── utils.ts
├── pnpm-workspace.yaml
├── rfcs/
│ ├── add-remove-package-commands.md
│ ├── check-command.md
│ ├── cli-output-polish.md
│ ├── cli-tips.md
│ ├── code-generator.md
│ ├── config-and-staged-commands.md
│ ├── dedupe-package-command.md
│ ├── dlx-command.md
│ ├── env-command.md
│ ├── exec-command.md
│ ├── global-cli-rust-binary.md
│ ├── implode-command.md
│ ├── init-editor-configs.md
│ ├── install-command.md
│ ├── js-runtime.md
│ ├── link-unlink-package-commands.md
│ ├── merge-global-and-local-cli.md
│ ├── migration-command.md
│ ├── outdated-package-command.md
│ ├── pack-command.md
│ ├── pm-command-group.md
│ ├── run-without-vite-plus-dependency.md
│ ├── split-global-cli.md
│ ├── trampoline-exe-for-shims.md
│ ├── update-package-command.md
│ ├── upgrade-command.md
│ ├── vpx-command.md
│ └── why-package-command.md
├── rust-toolchain.toml
├── scripts/
│ └── generate-license.ts
├── tmp/
│ └── .gitignore
├── tsconfig.json
└── vite.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .cargo/config.toml
================================================
[env]
# Required by rolldown_workspace crate - points to the rolldown subproject root
WORKSPACE_DIR = { value = "rolldown", relative = true }
[build]
rustflags = ["--cfg", "tokio_unstable"] # also update .github/workflows/ci.yml
# fix sqlite build error on linux
[target.'cfg(target_os = "linux")']
rustflags = ["--cfg", "tokio_unstable", "-C", "link-args=-Wl,--warn-unresolved-symbols"]
# Increase stack size on Windows to avoid stack overflow
[target.'cfg(all(windows, target_env = "msvc"))']
rustflags = ["--cfg", "tokio_unstable", "-C", "link-arg=/STACK:8388608"]
[target.'cfg(all(windows, target_env = "gnu"))']
rustflags = ["--cfg", "tokio_unstable", "-C", "link-arg=-Wl,--stack,8388608"]
[unstable]
bindeps = true
[net]
git-fetch-with-cli = true # use git CLI to authenticate for vite-task git dependencies
================================================
FILE: .claude/agents/cargo-workspace-merger.md
================================================
---
name: cargo-workspace-merger
description: "Use this agent when you need to merge one Cargo workspace into another, specifically when integrating a subproject's crates and dependencies into a root workspace. This includes tasks like: adding crate path references to workspace members, merging workspace dependency definitions while avoiding duplicates, and ensuring only production dependencies (not unnecessary dev dependencies) are included.\\n\\n\\nContext: The user wants to integrate the rolldown project into their existing Cargo workspace.\\nuser: \"I need to merge the rolldown Cargo workspace into our root workspace\"\\nassistant: \"I'll use the cargo-workspace-merger agent to handle this integration. This involves analyzing both Cargo.toml files, identifying the crates to add, and merging the necessary dependencies.\"\\n\\n\\n\\n\\nContext: The user has cloned a Rust project as a subdirectory and wants to integrate it.\\nuser: \"Can you add all the crates from ./external-lib into our workspace?\"\\nassistant: \"I'll launch the cargo-workspace-merger agent to analyze the external library's workspace structure and merge it into your root Cargo.toml.\"\\n\\n"
model: opus
color: yellow
---
You are an expert Rust build system engineer specializing in Cargo workspace management and dependency resolution. You have deep knowledge of Cargo.toml structure, workspace inheritance, and dependency deduplication strategies.
## Your Primary Mission
Merge a child Cargo workspace (located in a subdirectory) into a parent root Cargo workspace. This involves two main tasks:
1. **Adding crate references**: Add all crates from the child workspace to the root workspace's `[workspace.dependencies]` section with proper path references.
2. **Merging workspace dependencies**: Combine the child workspace's `[workspace.dependencies]` with the root's dependencies, ensuring no duplicates and only including dependencies actually used by the crates being merged.
## Step-by-Step Process
### Step 1: Analyze the Child Workspace
- Read the child workspace's `Cargo.toml` (e.g., `./rolldown/Cargo.toml`)
- Identify all workspace members from the `[workspace.members]` section
- Extract all `[workspace.dependencies]` definitions
### Step 2: Identify Crates to Add
- For each workspace member, locate its `Cargo.toml`
- Extract the crate name from `[package].name`
- Build a list of path references in the format: `crate_name = { path = "./child/crates/crate_name" }`
### Step 3: Analyze Dependency Usage
- For each crate in the child workspace, read its `Cargo.toml`
- Collect all dependencies from `[dependencies]`, `[dev-dependencies]`, and `[build-dependencies]`
- Focus on dependencies that reference `workspace = true` - these need the workspace-level definition
- Create a set of actually-used workspace dependencies
### Step 4: Filter and Merge Dependencies
- From the child's `[workspace.dependencies]`, only include those that are actually used by the crates
- Check for conflicts with existing root workspace dependencies:
- Same dependency, same version: Skip (already exists)
- Same dependency, different version: Flag for manual resolution and suggest keeping the newer version
- Exclude dev-only dependencies that aren't needed for the merged crates
### Step 5: Update Root Cargo.toml
- Add all crate path references to `[workspace.dependencies]`
- Add filtered workspace dependencies to `[workspace.dependencies]`
- Maintain alphabetical ordering within sections for cleanliness
- Preserve any existing comments and formatting
## Output Format
Provide:
1. A summary of crates being added
2. A summary of dependencies being merged
3. Any conflicts or issues requiring manual attention
4. The exact additions to make to the root `Cargo.toml`
## Quality Checks
- Verify all paths exist before adding references
- Ensure no duplicate entries are created
- Validate that merged dependencies don't break existing crates
- After modifications, suggest running `cargo check --workspace` to verify the merge
- Use highest compatible semver versions (if not pinned) and merge features in crates
## Important Considerations
- Use `vite_path` types for path operations as per project conventions
- Dependencies with `path` references in the child workspace may need path adjustments
- Feature flags on dependencies must be preserved
- Optional dependencies must maintain their optional status
- If a dependency exists in both workspaces with different features, merge the feature lists
### Workspace Package Inheritance
Child crates may inherit fields from `[workspace.package]` using `field.workspace = true`. Common inherited fields include:
- `homepage`
- `repository`
- `license`
- `edition`
- `authors`
- `rust-version`
**Important**: If the child workspace's `[workspace.package]` defines fields that the root workspace does not, you must add those fields to the root workspace's `[workspace.package]` section. Otherwise, crates that inherit these fields will fail to build with errors like:
```
error inheriting `homepage` from workspace root manifest's `workspace.package.homepage`
Caused by: `workspace.package.homepage` was not defined
```
**Steps to handle this**:
1. Read the child workspace's `[workspace.package]` section
2. Compare with the root workspace's `[workspace.package]` section
3. Add any missing fields to the root workspace (use the root project's own values, not the child's)
## Error Handling
- If a crate path doesn't exist, report it clearly and skip
- If Cargo.toml parsing fails, provide the specific error
- If version conflicts exist, list all conflicts before proceeding and ask for guidance
### Crates with Compile-Time Environment Variables
Some crates use `env!()` macros that require compile-time environment variables set via `.cargo/config.toml`. These crates often have `relative = true` paths that only work when building from their original workspace root.
**Example**: `rolldown_workspace` uses `env!("WORKSPACE_DIR")` which is set in `rolldown/.cargo/config.toml`.
**How to handle**:
1. Check child workspace's `.cargo/config.toml` for `[env]` section
2. If crates use these env vars with `relative = true`, copy those env vars to root `.cargo/config.toml` with paths adjusted to point to the child workspace directory
3. Example: If child has `WORKSPACE_DIR = { value = "", relative = true }`, root should have `WORKSPACE_DIR = { value = "child-dir", relative = true }`
================================================
FILE: .claude/agents/monorepo-architect.md
================================================
---
name: monorepo-architect
description: Use this agent when you need architectural guidance for monorepo tooling, particularly for reviewing code organization, module boundaries, and ensuring proper separation of concerns in Rust/Node.js projects. This agent should be invoked after implementing new features or refactoring existing code to validate architectural decisions and placement of functionality.\n\nExamples:\n- \n Context: The user has just implemented a new caching mechanism for the monorepo task runner.\n user: "I've added a new caching system to handle task outputs"\n assistant: "I'll use the monorepo-architect agent to review the architectural decisions and ensure the caching logic is properly placed within the module structure."\n \n Since new functionality was added, use the monorepo-architect agent to review the code architecture and module boundaries.\n \n\n- \n Context: The user is refactoring the task dependency resolution system.\n user: "I've refactored how we resolve task dependencies across packages"\n assistant: "Let me invoke the monorepo-architect agent to review the refactored code and ensure proper separation of concerns."\n \n After refactoring core functionality, use the monorepo-architect agent to validate architectural decisions.\n \n\n- \n Context: The user is adding cross-package communication features.\n user: "I've implemented a new IPC mechanism for packages to communicate during builds"\n assistant: "I'll use the monorepo-architect agent to review where this IPC logic lives and ensure it doesn't create inappropriate cross-module dependencies."\n \n When adding features that span multiple modules, use the monorepo-architect agent to prevent architectural violations.\n \n
model: opus
color: purple
---
You are a senior software architect with deep expertise in Rust and Node.js ecosystems, specializing in monorepo tooling and build systems. You have extensively studied and analyzed the architectures of nx, Turborepo, Rush, and Lage, understanding their design decisions, trade-offs, and implementation patterns.
Your primary responsibility is to review code architecture and ensure that functionality is properly organized within the codebase. You focus on:
**Core Architectural Principles:**
- Single Responsibility: Each module, file, and function should have one clear purpose
- Separation of Concerns: Business logic, I/O operations, and configuration should be clearly separated
- Module Boundaries: Enforce clean interfaces between modules, preventing tight coupling
- Dependency Direction: Dependencies should flow in one direction, typically from high-level to low-level modules
**When reviewing code, you will:**
1. **Analyze Module Structure**: Examine where new functionality has been placed and determine if it belongs there based on the module's responsibility. Look for code that crosses logical boundaries or mixes concerns.
2. **Identify Architectural Violations**:
- Cross-module responsibilities where one module is doing work that belongs to another
- Circular dependencies or bidirectional coupling
- Business logic mixed with I/O operations
- Configuration logic scattered across multiple modules
- Violation of the dependency inversion principle
3. **Suggest Proper Placement**: When you identify misplaced functionality, provide specific recommendations:
- Identify the correct module/file where the code should reside
- Explain why the current placement violates architectural principles
- Suggest how to refactor without breaking existing functionality
- Consider the impact on testing and maintainability
4. **Reference Industry Standards**: Draw from your knowledge of nx, Turborepo, Rush, and Lage to:
- Compare architectural decisions with proven patterns from these tools
- Highlight when a different approach might be more scalable or maintainable
- Suggest battle-tested patterns for common monorepo challenges
5. **Focus on Rust/Node.js Best Practices**:
- In Rust: Ensure proper use of ownership, traits for abstraction, and module organization
- In Node.js: Validate CommonJS/ESM module patterns, async patterns, and package boundaries
- For interop: Review FFI boundaries and data serialization approaches
**Review Methodology:**
1. Start by understanding the intent of the recent changes
2. Map out the affected modules and their responsibilities
3. Identify any code that seems out of place or creates inappropriate coupling
4. Provide a prioritized list of architectural concerns (critical, important, minor)
5. For each concern, explain the principle being violated and suggest a concrete fix
**Output Format:**
Structure your review as:
- **Summary**: Brief overview of architectural health
- **Critical Issues**: Must-fix architectural violations that will cause problems
- **Recommendations**: Suggested improvements with rationale
- **Positive Patterns**: Acknowledge well-architected decisions
- **Comparison Notes**: When relevant, note how similar problems are solved in nx/Turborepo/Rush/Lage
You are pragmatic and understand that perfect architecture must be balanced with delivery speed. Focus on issues that will genuinely impact maintainability, testability, or scalability. Avoid nitpicking and recognize when 'good enough' is appropriate for the current stage of the project.
When you lack context about the broader system, ask clarifying questions rather than making assumptions. Your goal is to ensure the codebase remains maintainable and follows established architectural patterns while evolving to meet new requirements.
================================================
FILE: .claude/skills/add-ecosystem-ci/SKILL.md
================================================
---
name: add-ecosystem-ci
description: Add a new ecosystem-ci test case for testing real-world projects against vite-plus
allowed-tools: Bash, Read, Edit, Write, WebFetch, AskUserQuestion
---
# Add Ecosystem-CI Test Case
Add a new ecosystem-ci test case following this process:
## Step 1: Get Repository Information
Ask the user for the GitHub repository URL if not provided as argument: $ARGUMENTS
Use GitHub CLI to get repository info:
```bash
gh api repos/OWNER/REPO --jq '.default_branch'
gh api repos/OWNER/REPO/commits/BRANCH --jq '.sha'
```
## Step 2: Auto-detect Project Configuration
### 2.1 Check for Subdirectory
Fetch the repository's root to check if the main package.json is in a subdirectory (like `web/`, `app/`, `frontend/`).
### 2.2 Check if Project Already Uses Vite-Plus
Check the project's root `package.json` for `vite-plus` in `dependencies` or `devDependencies`. If the project already uses vite-plus, set `forceFreshMigration: true` in `repo.json`. This tells `patch-project.ts` to set `VITE_PLUS_FORCE_MIGRATE=1` so `vp migrate` forces full dependency rewriting instead of skipping with "already using Vite+".
### 2.3 Auto-detect Commands from GitHub Workflows
Fetch the project's GitHub workflow files to detect available commands:
```bash
# List workflow files
gh api repos/OWNER/REPO/contents/.github/workflows --jq '.[].name'
# Fetch workflow content (for each .yml/.yaml file)
gh api repos/OWNER/REPO/contents/.github/workflows/ci.yml --jq '.content' | base64 -d
```
Look for common patterns in workflow files:
- `pnpm run ` / `npm run ` / `yarn `
- Commands like: `lint`, `build`, `test`, `type-check`, `typecheck`, `format`, `format:check`
- Map detected commands to `vp` equivalents: `vp run lint`, `vp run build`, etc.
### 2.4 Ask User to Confirm
Present the auto-detected configuration and ask user to confirm or modify:
- Which directory contains the main package.json? (auto-detected or manual)
- What Node.js version to use? (22 or 24, try to detect from workflow)
- Which commands to run? (show detected commands as multi-select options)
- Which OS to run on? (both, ubuntu-only, windows-only) - default: both
## Step 3: Update Files
1. **Add to `ecosystem-ci/repo.json`**:
```json
{
"project-name": {
"repository": "https://github.com/owner/repo.git",
"branch": "main",
"hash": "full-commit-sha",
"directory": "web", // only if subdirectory is needed
"forceFreshMigration": true // only if project already uses vite-plus
}
}
```
2. **Add to `.github/workflows/e2e-test.yml`** matrix:
```yaml
- name: project-name
node-version: 24
directory: web # only if subdirectory is needed
command: |
vp run lint
vp run build
```
## Step 4: Verify
Test the clone locally:
```bash
node ecosystem-ci/clone.ts project-name
```
3. **Add OS exclusion to `.github/workflows/e2e-test.yml`** (if not running on both):
For ubuntu-only:
```yaml
exclude:
- os: windows-latest
project:
name: project-name
```
For windows-only:
```yaml
exclude:
- os: ubuntu-latest
project:
name: project-name
```
## Important Notes
- The `directory` field is optional - only add it if the package.json is not in the project root
- If `directory` is specified in repo.json, it must also be specified in the workflow matrix
- `patch-project.ts` automatically handles running `vp migrate` in the correct directory
- `forceFreshMigration` is required for projects that already have `vite-plus` in their package.json — it sets `VITE_PLUS_FORCE_MIGRATE=1` so `vp migrate` forces full dependency rewriting instead of skipping
- OS exclusions are added to the existing `exclude` section in the workflow matrix
================================================
FILE: .claude/skills/bump-vite-task/SKILL.md
================================================
---
name: bump-vite-task
description: Bump vite-task git dependency to the latest main commit. Use when you need to update the vite-task crates (fspy, vite_glob, vite_path, vite_str, vite_task, vite_workspace) in vite-plus.
allowed-tools: Read, Grep, Glob, Edit, Bash, Agent, WebFetch
---
# Bump vite-task to Latest Main
Update the vite-task git dependency in `Cargo.toml` to the latest commit on the vite-task main branch, fix any breaking changes, and create a PR.
## Steps
### 1. Get current and target commits
- Read `Cargo.toml` and find the current `rev = "..."` for any vite-task git dependency (e.g., `vite_task`, `vite_path`, `fspy`, `vite_glob`, `vite_str`, `vite_workspace`). They all share the same revision.
- Get the latest commit hash on vite-task's main branch:
```bash
git ls-remote https://github.com/voidzero-dev/vite-task.git refs/heads/main
```
### 2. Update Cargo.toml
- Replace **all** occurrences of the old commit hash with the new one in `Cargo.toml`. There are 6 crate entries that reference the same vite-task revision: `fspy`, `vite_glob`, `vite_path`, `vite_str`, `vite_task`, `vite_workspace`.
### 3. Ensure upstream dependencies are cloned
- `cargo check` requires the `./rolldown` and `./vite` directories to exist (many workspace path dependencies point to `./rolldown/crates/...`).
- Locally, clone them using the commit hashes from `packages/tools/.upstream-versions.json`.
- CI handles this automatically via the `.github/actions/clone` action.
### 4. Verify compilation
- Run `cargo check` to ensure the new vite-task compiles without errors.
- If there are compilation errors, these are **breaking changes** from vite-task. Fix them in the vite-plus codebase (the consuming side), not in vite-task.
- Common breaking changes include: renamed functions/methods, changed function signatures, new required fields in structs, removed public APIs.
### 5. Run tests
- Run `cargo test -p vite_command -p vite_error -p vite_install -p vite_js_runtime -p vite_migration -p vite_shared -p vite_static_config -p vite-plus-cli -p vite_global_cli` to run the vite-plus crate tests.
- Note: Some tests require network access (e.g., `vite_install::package_manager` tests, `vite_global_cli::commands::env` tests). These may fail in sandboxed environments. Verify they also fail on the main branch before dismissing them.
- Note: `cargo test -p vite_task` will NOT work because vite_task is a git dependency, not a workspace member.
### 6. Update snap tests
vite-task changes often affect CLI output, which means snap tests need updating. Common output changes:
- **Status icons**: e.g., cache hit/miss indicators may change
- **New CLI options**: e.g., new flags added to `vp run` that show up in help output
- **Cache behavior messages**: e.g., new summary lines about cache status
- **Task output formatting**: e.g., step numbering, separator lines
To update snap tests:
1. Push your changes and let CI run the snap tests.
2. CI will show the diff in the E2E test logs if snap tests fail.
3. Extract the diff from CI logs and apply it locally.
4. Check all three platforms (Linux, Mac, Windows) since they may have slightly different snap test coverage.
5. Watch for trailing newline issues - ensure snap files end consistently.
Snap test files are at `packages/cli/snap-tests/*/snap.txt` and `packages/cli/snap-tests-global/*/snap.txt`.
### 7. Create the PR
- Commit message: `chore: bump vite-task to `
- PR title: `chore: bump vite-task to `
- PR body: Link to vite-task CHANGELOG.md diff between old and new commits:
```
https://github.com/voidzero-dev/vite-task/compare/...#diff-06572a96a58dc510037d5efa622f9bec8519bc1beab13c9f251e97e657a9d4ed
```
### 8. Verify CI
Wait for CI and ensure the `done` check passes. Key checks to monitor:
- **Lint**: Clippy and format checks
- **Test** (Linux, Mac, Windows): Rust unit tests
- **CLI E2E test** (Linux, Mac, Windows): Snap tests - most likely to fail on a vite-task bump
- **Run task**: Task runner integration tests
- **Cargo Deny**: License/advisory checks (may have pre-existing failures unrelated to bump)
The only **required** status check for merging is `done`, which aggregates the other checks (excluding Cargo Deny).
## Notes
- Building the full CLI locally (`pnpm bootstrap-cli`) requires the rolldown Node.js package to be built first, which is complex. Prefer relying on CI for snap test generation.
- `Cargo.lock` is automatically updated by cargo when you change the revision in `Cargo.toml`.
================================================
FILE: .claude/skills/spawn-process/SKILL.md
================================================
---
name: spawn-process
description: Guide for writing subprocess execution code using the vite_command crate
allowed-tools: Read, Grep, Glob, Edit, Write, Bash
---
# Add Subprocess Execution Code
When writing Rust code that needs to spawn subprocesses (resolve binaries, build commands, execute programs), always use the `vite_command` crate. Never use `which`, `tokio::process::Command::new`, or `std::process::Command::new` directly.
## Available APIs
### `vite_command::resolve_bin(name, path_env, cwd)` — Resolve a binary name to an absolute path
Handles PATHEXT (`.cmd`/`.bat`) on Windows. Pass `None` for `path_env` to search the current process PATH.
```rust
// Resolve using current PATH
let bin = vite_command::resolve_bin("node", None, &cwd)?;
// Resolve using a custom PATH
let custom_path = std::ffi::OsString::from(&path_env_str);
let bin = vite_command::resolve_bin("eslint", Some(&custom_path), &cwd)?;
```
### `vite_command::build_command(bin_path, cwd)` — Build a command for a pre-resolved binary
Returns `tokio::process::Command` with cwd, inherited stdio, and `fix_stdio_streams` on Unix already configured. Add args, envs, or override stdio as needed.
```rust
let bin = vite_command::resolve_bin("eslint", None, &cwd)?;
let mut cmd = vite_command::build_command(&bin, &cwd);
cmd.args(&[".", "--fix"]);
cmd.env("NODE_ENV", "production");
let mut child = cmd.spawn()?;
let status = child.wait().await?;
```
### `vite_command::build_shell_command(shell_cmd, cwd)` — Build a shell command
Uses `/bin/sh -c` on Unix, `cmd.exe /C` on Windows. Same stdio and `fix_stdio_streams` setup as `build_command`.
```rust
let mut cmd = vite_command::build_shell_command("echo hello && ls", &cwd);
let mut child = cmd.spawn()?;
let status = child.wait().await?;
```
### `vite_command::run_command(bin_name, args, envs, cwd)` — Resolve + build + run in one call
Combines resolve_bin, build_command, and status().await. The `envs` HashMap must include `"PATH"` if you want custom PATH resolution.
```rust
let envs = HashMap::from([("PATH".to_string(), path_value)]);
let status = vite_command::run_command("node", &["--version"], &envs, &cwd).await?;
```
## Dependency Setup
Add `vite_command` to the crate's `Cargo.toml`:
```toml
[dependencies]
vite_command = { workspace = true }
```
Do NOT add `which` as a direct dependency — binary resolution goes through `vite_command::resolve_bin`.
## Exception
`crates/vite_global_cli/src/shim/exec.rs` uses synchronous `std::process::Command` with Unix `exec()` for process replacement. This is the only place that bypasses `vite_command`.
================================================
FILE: .claude/skills/sync-tsdown-cli/SKILL.md
================================================
---
name: sync-tsdown-cli
description: Compare tsdown CLI options with vp pack and sync any new or removed options. Use when tsdown is upgraded or when you need to check for CLI option drift between tsdown and vp pack.
allowed-tools: Read, Grep, Glob, Edit, Bash
---
# Sync tsdown CLI Options with vp pack
Compare the upstream `tsdown` CLI options with `vp pack` (defined in `packages/cli/src/pack-bin.ts`) and sync any differences.
## Steps
1. Run `npx tsdown --help` from `packages/cli/` to get tsdown's current CLI options
2. Read `packages/cli/src/pack-bin.ts` to see vp pack's current options
3. Compare and add any new tsdown options to `pack-bin.ts` using the existing cac `.option()` pattern
4. If tsdown removed options, do NOT remove them from `pack-bin.ts` -- instead add a code comment like `// NOTE: removed from tsdown CLI in vX.Y.Z` above the option so reviewers can decide whether to follow up
5. Preserve intentional differences:
- `-c, --config` is intentionally commented out (vp pack uses vite.config.ts)
- `--env-prefix` has a different default (`['VITE_PACK_', 'TSDOWN_']`)
6. Verify with `pnpm --filter vite-plus build-ts` and `vp pack -h`
7. If new parameters were added, add a corresponding snap test under `packages/cli/snap-tests/` to verify the new option works correctly
================================================
FILE: .clippy.toml
================================================
avoid-breaking-exported-api = false
disallowed-methods = [
{ path = "str::to_ascii_lowercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_ascii_lowercase` instead." },
{ path = "str::to_ascii_uppercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_ascii_uppercase` instead." },
{ path = "str::to_lowercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_lowercase` instead." },
{ path = "str::to_uppercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_uppercase` instead." },
{ path = "str::replace", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_replace` instead." },
{ path = "str::replacen", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_replacen` instead." },
{ path = "std::env::current_dir", reason = "To get an `AbsolutePathBuf`, Use `vite_path::current_dir` instead." },
]
disallowed-types = [
{ path = "std::collections::HashMap", reason = "Use `rustc_hash::FxHashMap` instead, which is typically faster." },
{ path = "std::collections::HashSet", reason = "Use `rustc_hash::FxHashSet` instead, which is typically faster." },
{ path = "std::path::Path", reason = "Use `vite_path::RelativePath` or `vite_path::AbsolutePath` instead" },
{ path = "std::path::PathBuf", reason = "Use `vite_path::RelativePathBuf` or `vite_path::AbsolutePathBuf` instead" },
{ path = "std::string::String", reason = "Use `vite_str::Str` for small strings. For large strings, prefer `Box/Rc/Arc` if mutation is not needed." },
]
disallowed-macros = [
{ path = "std::format", reason = "Use `vite_str::format` for small strings." },
{ path = "std::println", reason = "Use `vite_shared::output` functions (`info`, `note`, `success`) instead." },
{ path = "std::print", reason = "Use `vite_shared::output` functions (`info`, `note`, `success`) instead." },
{ path = "std::eprintln", reason = "Use `vite_shared::output` functions (`warn`, `error`) instead." },
{ path = "std::eprint", reason = "Use `vite_shared::output` functions (`warn`, `error`) instead." },
]
================================================
FILE: .devcontainer/devcontainer.json
================================================
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/rust
{
"name": "Rust",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/vscode/devcontainers/base:ubuntu-22.04",
"updateContentCommand": {
"rustToolchain": "rustup show"
},
"containerEnv": {
"CARGO_TARGET_DIR": "/tmp/target"
},
"features": {
"ghcr.io/devcontainers/features/rust:1": {},
"ghcr.io/devcontainers-extra/features/fish-apt-get:1": {}
},
"customizations": {
"vscode": {
"extensions": ["rust-lang.rust-analyzer", "tamasfe.even-better-toml", "fill-labs.dependi"],
"settings": {
"terminal.integrated.defaultProfile.linux": "fish",
"terminal.integrated.profiles.linux": {
"fish": {
"path": "/usr/bin/fish"
}
}
}
}
},
"postCreateCommand": "curl -fsSL https://vite.plus | bash"
// Use 'mounts' to make the cargo cache persistent in a Docker Volume.
// "mounts": [
// {
// "source": "devcontainer-cargo-cache-${devcontainerId}",
// "target": "/usr/local/cargo",
// "type": "volume"
// }
// ]
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "rustc --version",
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
================================================
FILE: .gitattributes
================================================
* text=auto eol=lf
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: "\U0001F41E Bug report"
description: Report an issue with Vite+
labels: [pending triage]
type: Bug
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report.
Please only file issues here if the bug is in Vite+ itself.
If the bug belongs to an underlying tool, report it in that project's tracker (linked in the "Create new issue" page).
- type: textarea
id: bug-description
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description.
placeholder: I am doing ... What I expect is ... What is actually happening is ...
validations:
required: true
- type: input
id: reproduction
attributes:
label: Reproduction
description: Please provide a link to a minimal reproduction repository or a runnable stackblitz/sandbox. If a report is vague and has no reproduction, it may be closed.
placeholder: Reproduction URL
validations:
required: true
- type: textarea
id: reproduction-steps
attributes:
label: Steps to reproduce
description: Provide any required steps, commands, or setup details.
placeholder: Run `pnpm install` followed by `vp dev`
- type: textarea
id: system-info
attributes:
label: System Info
description: |
Paste the full output of both commands:
- `vp env current`
- `vp --version`
render: shell
placeholder: Paste `vp env current` and `vp --version` output here
validations:
required: true
- type: dropdown
id: package-manager
attributes:
label: Used Package Manager
description: Select the package manager used in your project
options:
- npm
- yarn
- pnpm
- bun
validations:
required: true
- type: textarea
id: logs
attributes:
label: Logs
description: |
Optional when a reproduction is provided. Please copy-paste text logs instead of screenshots.
If relevant, run your command with `--debug` and include the output.
render: shell
- type: checkboxes
id: checkboxes
attributes:
label: Validations
description: Before submitting the issue, please confirm the following
options:
- label: Read the [Contributing Guidelines](https://github.com/voidzero-dev/vite-plus/blob/main/CONTRIBUTING.md).
required: true
- label: Check that there isn't [already an issue](https://github.com/voidzero-dev/vite-plus/issues) for the same bug.
required: true
- label: Confirm this is a Vite+ issue and not an upstream issue (Vite, Vitest, tsdown, Rolldown, or Oxc).
required: true
- label: The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example).
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Vite Issues
url: https://github.com/vitejs/vite/issues/new/choose
about: Report issues specific to Vite core in the Vite repository.
- name: Vitest Issues
url: https://github.com/vitest-dev/vitest/issues/new/choose
about: Report issues specific to Vitest in the Vitest repository.
- name: tsdown Issues
url: https://github.com/rolldown/tsdown/issues/new/choose
about: Report issues specific to tsdown in the tsdown repository.
- name: Rolldown Issues
url: https://github.com/rolldown/rolldown/issues/new/choose
about: Report issues specific to Rolldown in the Rolldown repository.
- name: Oxc (Oxlint/Oxfmt) Issues
url: https://github.com/oxc-project/oxc/issues/new/choose
about: Report Oxlint/Oxfmt issues in the Oxc repository.
================================================
FILE: .github/ISSUE_TEMPLATE/docs.yml
================================================
name: "\U0001F4DA Documentation"
description: Suggest a documentation improvement for Vite+
labels: [documentation]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this issue.
- type: checkboxes
id: documentation_is
attributes:
label: Documentation is
options:
- label: Missing
- label: Outdated
- label: Confusing
- label: Not sure
- type: textarea
id: description
attributes:
label: Explain in Detail
description: A clear and concise description of your suggestion. If you intend to submit a PR for this issue, mention it here.
placeholder: The description of ... is not clear. I thought it meant ... but it wasn't.
validations:
required: true
- type: textarea
id: suggestion
attributes:
label: Your Suggestion for Changes
validations:
required: true
- type: input
id: reference
attributes:
label: Relevant Page
description: Link to the relevant doc page, section, or file.
placeholder: https://github.com/voidzero-dev/vite-plus/blob/main/docs/...
- type: input
id: reproduction
attributes:
label: Reproduction (Optional)
description: If the docs issue is tied to behavior, share a minimal reproduction link.
placeholder: Reproduction URL
- type: textarea
id: reproduction-steps
attributes:
label: Steps to reproduce (Optional)
description: Add steps if the docs issue is about incorrect behavior guidance.
placeholder: Run `pnpm install` followed by `vp dev`
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: "\U0001F680 New feature proposal"
description: Propose a new feature to be added to Vite+
labels: [pending triage]
type: Feature
body:
- type: markdown
attributes:
value: |
Thanks for your interest in Vite+ and taking the time to fill out this feature report.
Please only open feature proposals here for Vite+ itself.
If the proposal belongs to an underlying tool, use that project's tracker (linked in the "Create new issue" page).
- type: textarea
id: feature-description
attributes:
label: Description
description: 'Clear and concise description of the problem. Explain use cases and motivation. If you intend to submit a PR for this issue, mention it here.'
placeholder: As a developer using Vite+ I want [goal / wish] so that [benefit].
validations:
required: true
- type: textarea
id: suggested-solution
attributes:
label: Suggested solution
description: 'Describe a possible API, behavior, or implementation direction.'
validations:
required: true
- type: textarea
id: alternative
attributes:
label: Alternative
description: Describe any alternative solutions or features you've considered.
- type: textarea
id: additional-context
attributes:
label: Additional context
description: Any other context, links, or screenshots about the feature request.
- type: checkboxes
id: checkboxes
attributes:
label: Validations
description: Before submitting the issue, please confirm the following
options:
- label: Read the [Contributing Guidelines](https://github.com/voidzero-dev/vite-plus/blob/main/CONTRIBUTING.md).
required: true
- label: Confirm this request is for Vite+ itself and not for Vite, Vitest, tsdown, Rolldown, or Oxc.
required: true
- label: Check that there isn't already an issue requesting the same feature.
required: true
================================================
FILE: .github/actions/build-upstream/action.yml
================================================
name: 'Build with Upstream Repositories'
description: 'Builds Vite+ with the upstream repositories'
inputs:
target:
description: 'The target platform'
required: true
print-after-build:
description: 'Print the output after the build'
required: false
default: 'false'
runs:
using: 'composite'
steps:
- uses: ./.github/actions/download-rolldown-binaries
with:
github-token: ${{ github.token }}
target: ${{ inputs.target }}
upload: 'false'
# Compute cache key once before any builds modify files
# (packages/cli/package.json is modified by syncTestPackageExports during build-ts)
# Include env vars (RELEASE_BUILD, DEBUG, VERSION) to ensure cache miss on release builds
- name: Compute NAPI binding cache key
id: cache-key
shell: bash
run: |
echo "key=napi-binding-v3-${{ inputs.target }}-${{ env.RELEASE_BUILD }}-${{ env.DEBUG }}-${{ env.VERSION }}-${{ env.NPM_TAG }}-${{ hashFiles('packages/tools/.upstream-versions.json', 'Cargo.lock', 'crates/**/*.rs', 'crates/*/Cargo.toml', 'packages/cli/binding/**/*.rs', 'packages/cli/binding/Cargo.toml', 'Cargo.toml', '.cargo/config.toml', 'packages/cli/package.json', 'packages/cli/build.ts') }}" >> $GITHUB_OUTPUT
# Cache NAPI bindings and Rust CLI binary (the slow parts, especially on Windows)
- name: Restore NAPI binding cache
id: cache-restore
uses: actions/cache/restore@94b89442628ad1d101e352b7ee38f30e1bef108e # v5
with:
path: |
packages/cli/binding/*.node
packages/cli/binding/index.js
packages/cli/binding/index.d.ts
packages/cli/binding/index.cjs
packages/cli/binding/index.d.cts
target/${{ inputs.target }}/release/vp
target/${{ inputs.target }}/release/vp.exe
target/${{ inputs.target }}/release/vp-shim.exe
key: ${{ steps.cache-key.outputs.key }}
# Apply Vite+ branding patches to vite source (CI checks out
# upstream vite which doesn't have branding patches)
- name: Brand vite
shell: bash
run: pnpm exec tool brand-vite
# Build upstream TypeScript packages first (don't depend on native bindings)
- name: Build upstream TypeScript packages
shell: bash
run: |
pnpm --filter @rolldown/pluginutils build
pnpm --filter rolldown build-node
pnpm --filter vite build-types
pnpm --filter "@voidzero-dev/*" build
pnpm --filter vite-plus build-ts
# NAPI builds - only run on cache miss (slow, especially on Windows)
# Must run before vite-plus TypeScript builds which depend on the bindings
- name: Build NAPI bindings (x86_64-linux)
shell: bash
if: steps.cache-restore.outputs.cache-hit != 'true' && inputs.target == 'x86_64-unknown-linux-gnu'
run: |
pnpm --filter=vite-plus build-native --target ${{ inputs.target }} --use-napi-cross
env:
TARGET_CC: clang
DEBUG: napi:*
- name: Build NAPI bindings (aarch64-linux)
shell: bash
if: steps.cache-restore.outputs.cache-hit != 'true' && inputs.target == 'aarch64-unknown-linux-gnu'
run: |
pnpm --filter=vite-plus build-native --target ${{ inputs.target }} --use-napi-cross
env:
TARGET_CC: clang
TARGET_CFLAGS: '-D_BSD_SOURCE'
DEBUG: napi:*
- name: Build NAPI bindings (non-Linux targets)
shell: bash
if: steps.cache-restore.outputs.cache-hit != 'true' && !contains(inputs.target, 'linux')
run: |
pnpm --filter=vite-plus build-native --target ${{ inputs.target }}
env:
DEBUG: napi:*
- name: Build Rust CLI binary (x86_64-linux)
if: steps.cache-restore.outputs.cache-hit != 'true' && inputs.target == 'x86_64-unknown-linux-gnu'
shell: bash
run: |
pnpm exec napi build --use-napi-cross --target ${{ inputs.target }} --release -p vite_global_cli
env:
TARGET_CC: clang
DEBUG: napi:*
- name: Build Rust CLI binary (aarch64-linux)
if: steps.cache-restore.outputs.cache-hit != 'true' && inputs.target == 'aarch64-unknown-linux-gnu'
shell: bash
run: |
pnpm exec napi build --use-napi-cross --target ${{ inputs.target }} --release -p vite_global_cli
env:
TARGET_CC: clang
TARGET_CFLAGS: '-D_BSD_SOURCE'
DEBUG: napi:*
- name: Build Rust CLI binary (non-Linux targets)
if: steps.cache-restore.outputs.cache-hit != 'true' && !contains(inputs.target, 'linux')
shell: bash
run: cargo build --release --target ${{ inputs.target }} -p vite_global_cli
- name: Build trampoline shim binary (Windows only)
if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'windows')
shell: bash
run: cargo build --release --target ${{ inputs.target }} -p vite_trampoline
- name: Save NAPI binding cache
if: steps.cache-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@94b89442628ad1d101e352b7ee38f30e1bef108e # v5
with:
path: |
packages/cli/binding/*.node
packages/cli/binding/index.js
packages/cli/binding/index.d.ts
packages/cli/binding/index.cjs
packages/cli/binding/index.d.cts
target/${{ inputs.target }}/release/vp
target/${{ inputs.target }}/release/vp.exe
target/${{ inputs.target }}/release/vp-shim.exe
key: ${{ steps.cache-key.outputs.key }}
# Build vite-plus TypeScript after native bindings are ready
- name: Build vite-plus TypeScript packages
shell: bash
run: |
pnpm --filter=vite-plus build-ts
- name: Print output after build
shell: bash
if: inputs.print-after-build == 'true'
run: |
pnpm vp -h
pnpm vp run -h
pnpm vp lint -h
pnpm vp test -h
pnpm vp build -h
pnpm vp fmt -h
================================================
FILE: .github/actions/clone/action.yml
================================================
name: 'Clone Repositories'
description: 'Clone self and upstream repositories'
inputs:
ecosystem-ci-project:
description: 'The ecosystem ci project to clone'
required: false
default: ''
outputs:
ecosystem-ci-project-path:
description: 'The path where the ecosystem ci project was cloned'
value: ${{ steps.ecosystem-ci-project-hash.outputs.ECOSYSTEM_CI_PROJECT_PATH }}
runs:
using: 'composite'
steps:
- name: Output rolldown and vite hash
shell: bash
id: upstream-versions
run: |
node -e "console.log('ROLLDOWN_HASH=' + require('./packages/tools/.upstream-versions.json').rolldown.hash)" >> $GITHUB_OUTPUT
node -e "console.log('ROLLDOWN_VITE_HASH=' + require('./packages/tools/.upstream-versions.json')['vite'].hash)" >> $GITHUB_OUTPUT
- name: Output ecosystem ci project hash
shell: bash
id: ecosystem-ci-project-hash
if: ${{ inputs.ecosystem-ci-project != '' }}
run: |
node -e "console.log('ECOSYSTEM_CI_PROJECT_HASH=' + require('./ecosystem-ci/repo.json')['${{ inputs.ecosystem-ci-project }}'].hash)" >> $GITHUB_OUTPUT
node -e "console.log('ECOSYSTEM_CI_PROJECT_REPOSITORY=' + require('./ecosystem-ci/repo.json')['${{ inputs.ecosystem-ci-project }}'].repository.replace('https://github.com/', '').replace('.git', ''))" >> $GITHUB_OUTPUT
echo "ECOSYSTEM_CI_PROJECT_PATH=${{ runner.temp }}/vite-plus-ecosystem-ci/${{ inputs.ecosystem-ci-project }}" >> $GITHUB_OUTPUT
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
repository: rolldown/rolldown
path: rolldown
ref: ${{ steps.upstream-versions.outputs.ROLLDOWN_HASH }}
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
repository: vitejs/vite
path: vite
ref: ${{ steps.upstream-versions.outputs.ROLLDOWN_VITE_HASH }}
# Disable autocrlf to preserve LF line endings on Windows
# This prevents prettier/eslint from failing with "Delete ␍" errors
- name: Configure git for LF line endings
if: ${{ inputs.ecosystem-ci-project != '' }}
shell: bash
run: git config --global core.autocrlf false
- name: Clone ecosystem ci project
if: ${{ inputs.ecosystem-ci-project != '' }}
shell: bash
run: npx tsx ecosystem-ci/clone.ts ${{ inputs.ecosystem-ci-project }}
================================================
FILE: .github/actions/download-rolldown-binaries/action.yml
================================================
name: 'Download Rolldown Binaries'
description: 'Download previous release rolldown binaries and upload as artifact'
inputs:
github-token:
description: 'GitHub token for accessing GitHub Package Registry'
required: true
target:
description: 'The target platform'
default: 'x86_64-unknown-linux-gnu'
required: false
upload:
description: 'Upload the rolldown binaries as artifact'
required: false
default: 'true'
runs:
using: 'composite'
steps:
- name: Install previous release
shell: bash
run: |
if ${{ runner.os == 'Windows' }}; then
export TARGET="win32-x64-msvc"
elif ${{ runner.os == 'Linux' }}; then
export TARGET="linux-x64-gnu"
elif ${{ runner.os == 'macOS' }}; then
export TARGET="darwin-arm64"
fi
# Pin to the version from checked-out rolldown source to avoid mismatch
# between JS code (built from source) and native binary (downloaded from npm).
# Falls back to npm latest only when rolldown source isn't cloned yet
# (e.g., the standalone download-previous-rolldown-binaries job).
if [ -f "./rolldown/packages/rolldown/package.json" ]; then
export VERSION=$(node -p "require('./rolldown/packages/rolldown/package.json').version")
echo "Using rolldown version from source: ${VERSION}"
else
export VERSION=$(npm view --json rolldown | jq -r '.version')
echo "Warning: rolldown source not found, using npm latest: ${VERSION}"
fi
npm pack "@rolldown/binding-${TARGET}@${VERSION}"
tar -xzf "rolldown-binding-${TARGET}-${VERSION}.tgz"
if [ -d "./rolldown/packages/rolldown/src" ]; then
cp "./package/rolldown-binding.${TARGET}.node" ./rolldown/packages/rolldown/src
ls ./rolldown/packages/rolldown/src
fi
env:
GITHUB_TOKEN: ${{ inputs.github-token }}
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: ${{ inputs.upload == 'true' }}
with:
name: rolldown-binaries
path: ./package/rolldown-binding.*.node
if-no-files-found: error
- name: Clean up
shell: bash
run: |
rm -rf package
rm *.tgz
================================================
FILE: .github/actions/set-snapshot-version/action.yml
================================================
name: Compute Release Version
description: Get latest tag from GitHub and increment the patch version
inputs:
npm_tag:
description: 'npm tag (latest or alpha)'
required: true
default: 'latest'
outputs:
version:
description: The computed version string
value: ${{ steps.version.outputs.version }}
runs:
using: composite
steps:
- name: Compute next patch version
id: version
shell: bash
run: |
git fetch --tags --quiet
npm install --prefix ${{ github.action_path }} semver > /dev/null 2>&1
VERSION_OUTPUT=$(node ${{ github.action_path }}/compute-version.mjs "${{ inputs.npm_tag }}")
echo "$VERSION_OUTPUT"
echo "$VERSION_OUTPUT" | tail -n 1 >> $GITHUB_OUTPUT
================================================
FILE: .github/actions/set-snapshot-version/compute-version.mjs
================================================
import { execSync } from 'node:child_process';
import semver from 'semver';
const npmTag = process.argv[2] || 'latest';
// Get all version tags
const tagsOutput = execSync('git tag -l "v*"', { encoding: 'utf-8' }).trim();
const tags = tagsOutput ? tagsOutput.split('\n') : [];
// Parse and filter to valid semver, then find latest stable (no prerelease)
const stableTags = tags
.map((tag) => semver.parse(tag.replace(/^v/, '')))
.filter((v) => v !== null && v.prerelease.length === 0);
let nextVersion;
if (stableTags.length === 0) {
nextVersion = '0.1.0';
} else {
stableTags.sort(semver.rcompare);
const latest = stableTags[0];
nextVersion = semver.inc(latest, 'patch');
}
let version;
if (npmTag === 'alpha') {
// Find existing alpha tags for this version
const alphaPrefix = `v${nextVersion}-alpha.`;
const alphaTags = tags
.filter((tag) => tag.startsWith(alphaPrefix))
.map((tag) => semver.parse(tag.replace(/^v/, '')))
.filter((v) => v !== null);
let alphaNum = 0;
if (alphaTags.length > 0) {
alphaTags.sort(semver.rcompare);
alphaNum = alphaTags[0].prerelease[1] + 1;
}
version = `${nextVersion}-alpha.${alphaNum}`;
} else {
version = nextVersion;
}
const latestStable = stableTags.length > 0 ? `v${stableTags[0].version}` : 'none';
console.log(`Computed version: ${version} (latest stable tag: ${latestStable})`);
console.log(`version=${version}`);
================================================
FILE: .github/actions/set-snapshot-version/package.json
================================================
{
"private": true,
"type": "module"
}
================================================
FILE: .github/renovate.json
================================================
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>Boshen/renovate"],
"ignorePaths": [
"packages/cli/snap-tests/**",
"packages/cli/snap-tests-global/**",
"packages/cli/snap-tests-todo/**",
"bench/fixtures/**",
"rolldown/**",
"vite/**"
],
"packageRules": [
{
"matchPackageNames": ["vitest-dev"],
"enabled": false
},
{
"matchPackageNames": [
"fspy",
"vite_glob",
"vite_path",
"vite_str",
"vite_task",
"vite_workspace",
"https://github.com/voidzero-dev/vite-task"
],
"enabled": false
}
]
}
================================================
FILE: .github/scripts/upgrade-deps.mjs
================================================
import fs from 'node:fs';
import path from 'node:path';
const ROOT = process.cwd();
// ============ GitHub API ============
async function getLatestTagCommit(owner, repo) {
const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/tags`, {
headers: {
Authorization: `token ${process.env.GITHUB_TOKEN}`,
Accept: 'application/vnd.github.v3+json',
},
});
if (!res.ok) {
throw new Error(`Failed to fetch tags for ${owner}/${repo}: ${res.status} ${res.statusText}`);
}
const tags = await res.json();
if (!Array.isArray(tags) || !tags.length) {
throw new Error(`No tags found for ${owner}/${repo}`);
}
if (!tags[0]?.commit?.sha) {
throw new Error(`Invalid tag structure for ${owner}/${repo}: missing commit SHA`);
}
console.log(`${repo} -> ${tags[0].name}`);
return tags[0].commit.sha;
}
// ============ npm Registry ============
async function getLatestNpmVersion(packageName) {
const res = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
if (!res.ok) {
throw new Error(
`Failed to fetch npm version for ${packageName}: ${res.status} ${res.statusText}`,
);
}
const data = await res.json();
if (!data?.version) {
throw new Error(`Invalid npm response for ${packageName}: missing version field`);
}
return data.version;
}
// ============ Update .upstream-versions.json ============
async function updateUpstreamVersions() {
const filePath = path.join(ROOT, 'packages/tools/.upstream-versions.json');
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
// rolldown -> rolldown/rolldown
data.rolldown.hash = await getLatestTagCommit('rolldown', 'rolldown');
// vite -> vitejs/vite
data['vite'].hash = await getLatestTagCommit('vitejs', 'vite');
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
console.log('Updated .upstream-versions.json');
}
// ============ Update pnpm-workspace.yaml ============
async function updatePnpmWorkspace(versions) {
const filePath = path.join(ROOT, 'pnpm-workspace.yaml');
let content = fs.readFileSync(filePath, 'utf8');
// Update vitest-dev override (handle pre-release versions like -beta.1, -rc.0)
// Handle both quoted ('npm:vitest@^...') and unquoted (npm:vitest@^...) forms
content = content.replace(
/vitest-dev: '?npm:vitest@\^[\d.]+(-[\w.]+)?'?/,
`vitest-dev: 'npm:vitest@^${versions.vitest}'`,
);
// Update tsdown in catalog (handle pre-release versions)
content = content.replace(/tsdown: \^[\d.]+(-[\w.]+)?/, `tsdown: ^${versions.tsdown}`);
// Update @oxc-node/cli in catalog
content = content.replace(
/'@oxc-node\/cli': \^[\d.]+(-[\w.]+)?/,
`'@oxc-node/cli': ^${versions.oxcNodeCli}`,
);
// Update @oxc-node/core in catalog
content = content.replace(
/'@oxc-node\/core': \^[\d.]+(-[\w.]+)?/,
`'@oxc-node/core': ^${versions.oxcNodeCore}`,
);
// Update oxfmt in catalog
content = content.replace(/oxfmt: =[\d.]+(-[\w.]+)?/, `oxfmt: =${versions.oxfmt}`);
// Update oxlint in catalog (but not oxlint-tsgolint)
content = content.replace(/oxlint: =[\d.]+(-[\w.]+)?\n/, `oxlint: =${versions.oxlint}\n`);
// Update oxlint-tsgolint in catalog
content = content.replace(
/oxlint-tsgolint: =[\d.]+(-[\w.]+)?/,
`oxlint-tsgolint: =${versions.oxlintTsgolint}`,
);
fs.writeFileSync(filePath, content);
console.log('Updated pnpm-workspace.yaml');
}
// ============ Update packages/test/package.json ============
async function updateTestPackage(vitestVersion) {
const filePath = path.join(ROOT, 'packages/test/package.json');
const pkg = JSON.parse(fs.readFileSync(filePath, 'utf8'));
// Update all @vitest/* devDependencies
for (const dep of Object.keys(pkg.devDependencies)) {
if (dep.startsWith('@vitest/')) {
pkg.devDependencies[dep] = vitestVersion;
}
}
// Update vitest-dev devDependency
if (pkg.devDependencies['vitest-dev']) {
pkg.devDependencies['vitest-dev'] = `^${vitestVersion}`;
}
// Update @vitest/ui peerDependency if present
if (pkg.peerDependencies?.['@vitest/ui']) {
pkg.peerDependencies['@vitest/ui'] = vitestVersion;
}
fs.writeFileSync(filePath, JSON.stringify(pkg, null, 2) + '\n');
console.log('Updated packages/test/package.json');
}
// ============ Update packages/core/package.json ============
async function updateCorePackage(devtoolsVersion) {
const filePath = path.join(ROOT, 'packages/core/package.json');
const pkg = JSON.parse(fs.readFileSync(filePath, 'utf8'));
// Update @vitejs/devtools in devDependencies
if (pkg.devDependencies?.['@vitejs/devtools']) {
pkg.devDependencies['@vitejs/devtools'] = `^${devtoolsVersion}`;
}
fs.writeFileSync(filePath, JSON.stringify(pkg, null, 2) + '\n');
console.log('Updated packages/core/package.json');
}
console.log('Fetching latest versions…');
const [
vitestVersion,
tsdownVersion,
devtoolsVersion,
oxcNodeCliVersion,
oxcNodeCoreVersion,
oxfmtVersion,
oxlintVersion,
oxlintTsgolintVersion,
] = await Promise.all([
getLatestNpmVersion('vitest'),
getLatestNpmVersion('tsdown'),
getLatestNpmVersion('@vitejs/devtools'),
getLatestNpmVersion('@oxc-node/cli'),
getLatestNpmVersion('@oxc-node/core'),
getLatestNpmVersion('oxfmt'),
getLatestNpmVersion('oxlint'),
getLatestNpmVersion('oxlint-tsgolint'),
]);
console.log(`vitest: ${vitestVersion}`);
console.log(`tsdown: ${tsdownVersion}`);
console.log(`@vitejs/devtools: ${devtoolsVersion}`);
console.log(`@oxc-node/cli: ${oxcNodeCliVersion}`);
console.log(`@oxc-node/core: ${oxcNodeCoreVersion}`);
console.log(`oxfmt: ${oxfmtVersion}`);
console.log(`oxlint: ${oxlintVersion}`);
console.log(`oxlint-tsgolint: ${oxlintTsgolintVersion}`);
await updateUpstreamVersions();
await updatePnpmWorkspace({
vitest: vitestVersion,
tsdown: tsdownVersion,
oxcNodeCli: oxcNodeCliVersion,
oxcNodeCore: oxcNodeCoreVersion,
oxfmt: oxfmtVersion,
oxlint: oxlintVersion,
oxlintTsgolint: oxlintTsgolintVersion,
});
await updateTestPackage(vitestVersion);
await updateCorePackage(devtoolsVersion);
console.log('Done!');
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
permissions:
# Doing it explicitly because the default permission only includes metadata: read.
contents: read
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize, labeled]
push:
branches:
- main
paths-ignore:
- '**/*.md'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.ref_name != 'main' }}
defaults:
run:
shell: bash
jobs:
optimize-ci:
runs-on: ubuntu-latest # or whichever runner you use for your CI
outputs:
skip: ${{ steps.check_skip.outputs.skip }}
steps:
- name: Optimize CI
id: check_skip
uses: withgraphite/graphite-ci-action@ee395f3a78254c006d11339669c6cabddf196f72
with:
graphite_token: ${{ secrets.GRAPHITE_CI_OPTIMIZER_TOKEN }}
detect-changes:
runs-on: ubuntu-latest
needs: optimize-ci
if: needs.optimize-ci.outputs.skip == 'false'
permissions:
contents: read
pull-requests: read
outputs:
code-changed: ${{ steps.filter.outputs.code }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
with:
filters: |
code:
- '!**/*.md'
download-previous-rolldown-binaries:
needs: detect-changes
if: needs.detect-changes.outputs.code-changed == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/actions/download-rolldown-binaries
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
test:
needs: detect-changes
if: needs.detect-changes.outputs.code-changed == 'true'
name: Test
strategy:
fail-fast: false
matrix:
include:
- os: namespace-profile-linux-x64-default
target: x86_64-unknown-linux-gnu
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: namespace-profile-mac-default
target: aarch64-apple-darwin
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/actions/clone
- name: Setup Dev Drive
if: runner.os == 'Windows'
uses: samypr100/setup-dev-drive@30f0f98ae5636b2b6501e181dfb3631b9974818d # v4.0.0
with:
drive-size: 12GB
drive-format: ReFS
env-mapping: |
CARGO_HOME,{{ DEV_DRIVE }}/.cargo
RUSTUP_HOME,{{ DEV_DRIVE }}/.rustup
- uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: test
target-dir: ${{ runner.os == 'Windows' && format('{0}/target', env.DEV_DRIVE) || '' }}
- run: rustup target add x86_64-unknown-linux-musl
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
- run: cargo check --all-targets --all-features
env:
RUSTFLAGS: '-D warnings --cfg tokio_unstable' # also update .cargo/config.toml
# Test all crates/* packages. New crates are automatically included.
# Also test vite-plus-cli (lives outside crates/) to catch type sync issues.
- run: cargo test $(for d in crates/*/; do echo -n "-p $(basename $d) "; done) -p vite-plus-cli
env:
RUST_MIN_STACK: 8388608
lint:
needs: detect-changes
if: needs.detect-changes.outputs.code-changed == 'true'
name: Lint
runs-on: namespace-profile-linux-x64-default
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/actions/clone
- uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: lint
tools: cargo-shear
components: clippy rust-docs rustfmt
- run: |
cargo shear
cargo fmt --check
# cargo clippy --all-targets --all-features -- -D warnings
# RUSTDOCFLAGS='-D warnings' cargo doc --no-deps --document-private-items
- uses: crate-ci/typos@631208b7aac2daa8b707f55e7331f9112b0e062d # v1.44.0
with:
files: .
- uses: oxc-project/setup-node@fdbf0dfd334c4e6d56ceeb77d91c76339c2a0885 # v1.0.4
- name: Install docs dependencies
run: pnpm -C docs install --frozen-lockfile
- name: Deduplicate dependencies
run: pnpm dedupe --check
run:
name: Run task
runs-on: namespace-profile-linux-x64-default
needs:
- download-previous-rolldown-binaries
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/actions/clone
- uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: run
- uses: oxc-project/setup-node@fdbf0dfd334c4e6d56ceeb77d91c76339c2a0885 # v1.0.4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rolldown-binaries
path: ./rolldown/packages/rolldown/src
merge-multiple: true
- name: Build with upstream
uses: ./.github/actions/build-upstream
with:
target: x86_64-unknown-linux-gnu
- name: Install Global CLI vp
run: |
pnpm bootstrap-cli:ci
echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH
- name: Print help for built-in commands
run: |
which vp
vp -h
vp run -h
vp lint -h
vp test -h
vp build -h
vp fmt -h
cli-e2e-test:
name: CLI E2E test
needs:
- download-previous-rolldown-binaries
strategy:
fail-fast: false
matrix:
include:
- os: namespace-profile-linux-x64-default
- os: namespace-profile-mac-default
- os: windows-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/actions/clone
- name: Setup Dev Drive
if: runner.os == 'Windows'
uses: samypr100/setup-dev-drive@30f0f98ae5636b2b6501e181dfb3631b9974818d # v4.0.0
with:
drive-size: 12GB
drive-format: ReFS
env-mapping: |
CARGO_HOME,{{ DEV_DRIVE }}/.cargo
RUSTUP_HOME,{{ DEV_DRIVE }}/.rustup
- uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: cli-e2e-test
target-dir: ${{ runner.os == 'Windows' && format('{0}/target', env.DEV_DRIVE) || '' }}
- uses: oxc-project/setup-node@fdbf0dfd334c4e6d56ceeb77d91c76339c2a0885 # v1.0.4
- name: Install docs dependencies
run: pnpm -C docs install --frozen-lockfile
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rolldown-binaries
path: ./rolldown/packages/rolldown/src
merge-multiple: true
- name: Build with upstream
uses: ./.github/actions/build-upstream
with:
target: ${{ matrix.os == 'namespace-profile-linux-x64-default' && 'x86_64-unknown-linux-gnu' || matrix.os == 'windows-latest' && 'x86_64-pc-windows-msvc' || 'aarch64-apple-darwin' }}
- name: Check TypeScript types
if: ${{ matrix.os == 'namespace-profile-linux-x64-default' }}
run: pnpm tsgo
- name: Install Global CLI vp
run: |
pnpm bootstrap-cli:ci
if [[ "$RUNNER_OS" == "Windows" ]]; then
echo "$USERPROFILE\.vite-plus\bin" >> $GITHUB_PATH
else
echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH
fi
- name: Verify vp installation
run: |
which vp
vp --version
vp -h
- name: Run vp check
run: vp check
- name: Test global package install (powershell)
if: ${{ matrix.os == 'windows-latest' }}
shell: pwsh
run: |
echo "PATH: $env:Path"
where.exe node
where.exe npm
where.exe npx
where.exe vp
vp env doctor
# Test 1: Install a JS-based CLI (typescript)
vp install -g typescript
tsc --version
where.exe tsc
# Test 2: Verify the package was installed correctly
Get-ChildItem "$env:USERPROFILE\.vite-plus\packages\typescript\"
Get-ChildItem "$env:USERPROFILE\.vite-plus\bin\"
# Test 3: Uninstall
vp uninstall -g typescript
# Test 4: Verify uninstall removed shim
Write-Host "Checking bin dir after uninstall:"
Get-ChildItem "$env:USERPROFILE\.vite-plus\bin\"
$shimPath = "$env:USERPROFILE\.vite-plus\bin\tsc.cmd"
if (Test-Path $shimPath) {
Write-Error "tsc shim file still exists at $shimPath"
exit 1
}
Write-Host "tsc shim removed successfully"
# Test 5: use session
vp env use 18
node --version
vp env doctor
vp env use --unset
node --version
- name: Test global package install (cmd)
if: ${{ matrix.os == 'windows-latest' }}
shell: cmd
run: |
echo "PATH: %PATH%"
where.exe node
where.exe npm
where.exe npx
where.exe vp
vp env use 18
node --version
vp env use --unset
node --version
vp env doctor
REM Test 1: Install a JS-based CLI (typescript)
vp install -g typescript
tsc --version
where.exe tsc
REM Test 2: Verify the package was installed correctly
dir "%USERPROFILE%\.vite-plus\packages\typescript\"
dir "%USERPROFILE%\.vite-plus\bin\"
REM Test 3: Uninstall
vp uninstall -g typescript
REM Test 4: Verify uninstall removed shim (.cmd wrapper)
echo Checking bin dir after uninstall:
dir "%USERPROFILE%\.vite-plus\bin\"
if exist "%USERPROFILE%\.vite-plus\bin\tsc.cmd" (
echo Error: tsc.cmd shim file still exists
exit /b 1
)
echo tsc.cmd shim removed successfully
REM Test 5: Verify shell script was also removed (for Git Bash)
if exist "%USERPROFILE%\.vite-plus\bin\tsc" (
echo Error: tsc shell script still exists
exit /b 1
)
echo tsc shell script removed successfully
REM Test 6: use session
vp env use 18
node --version
vp env doctor
vp env use --unset
node --version
- name: Test global package install (bash)
run: |
echo "PATH: $PATH"
ls -la ~/.vite-plus/
ls -la ~/.vite-plus/bin/
which node
which npm
which npx
which vp
vp env doctor
# Test 1: Install a JS-based CLI (typescript)
vp install -g typescript
tsc --version
which tsc
# Test 2: Verify the package was installed correctly
ls -la ~/.vite-plus/packages/typescript/
ls -la ~/.vite-plus/bin/
# Test 3: Uninstall
vp uninstall -g typescript
# Test 4: Verify uninstall removed shim
echo "Checking bin dir after uninstall:"
ls -la ~/.vite-plus/bin/
if [ -f ~/.vite-plus/bin/tsc ]; then
echo "Error: tsc shim file still exists at ~/.vite-plus/bin/tsc"
exit 1
fi
echo "tsc shim removed successfully"
# Test 5: use session
vp env use 18
node --version
vp env doctor
vp env use --unset
node --version
- name: Install Playwright browsers
run: pnpx playwright install chromium
- name: Run CLI snapshot tests
run: |
RUST_BACKTRACE=1 pnpm test
if ! git diff --exit-code; then
echo "::error::Snapshot diff detected. Run 'pnpm -F vite-plus snap-test' locally and commit the updated snap.txt files."
git diff --stat
git diff
exit 1
fi
env:
RUST_MIN_STACK: 8388608
# Upgrade tests (merged from separate job to avoid duplicate build)
- name: Test upgrade (bash)
shell: bash
run: |
# Helper to read the installed CLI version from package.json
get_cli_version() {
node -p "require(require('path').resolve(process.env.USERPROFILE || process.env.HOME, '.vite-plus', 'current', 'node_modules', 'vite-plus', 'package.json')).version"
}
# Save initial (dev build) version
INITIAL_VERSION=$(get_cli_version)
echo "Initial version: $INITIAL_VERSION"
# --check queries npm registry and prints update status
vp upgrade --check
# full upgrade: download, extract, swap
vp upgrade --force
vp --version
vp env doctor
ls -la ~/.vite-plus/
# Verify version changed after update
UPDATED_VERSION=$(get_cli_version)
echo "Updated version: $UPDATED_VERSION"
if [ "$UPDATED_VERSION" == "$INITIAL_VERSION" ]; then
echo "Error: version should have changed after upgrade (still $INITIAL_VERSION)"
exit 1
fi
# rollback to the previous version
vp upgrade --rollback
vp --version
vp env doctor
# Verify version restored after rollback
ROLLBACK_VERSION=$(get_cli_version)
echo "Rollback version: $ROLLBACK_VERSION"
if [ "$ROLLBACK_VERSION" != "$INITIAL_VERSION" ]; then
echo "Error: version should have been restored after rollback (expected $INITIAL_VERSION, got $ROLLBACK_VERSION)"
exit 1
fi
- name: Test upgrade (powershell)
if: ${{ matrix.os == 'windows-latest' }}
shell: pwsh
run: |
Get-ChildItem "$env:USERPROFILE\.vite-plus\"
# Helper to read the installed CLI version from package.json
function Get-CliVersion {
node -p "require(require('path').resolve(process.env.USERPROFILE, '.vite-plus', 'current', 'node_modules', 'vite-plus', 'package.json')).version"
}
# Save initial (dev build) version
$initialVersion = Get-CliVersion
Write-Host "Initial version: $initialVersion"
# --check queries npm registry and prints update status
vp upgrade --check
# full upgrade: download, extract, swap
vp upgrade --force
vp --version
vp env doctor
Get-ChildItem "$env:USERPROFILE\.vite-plus\"
# Verify version changed after update
$updatedVersion = Get-CliVersion
Write-Host "Updated version: $updatedVersion"
if ($updatedVersion -eq $initialVersion) {
Write-Error "Error: version should have changed after upgrade (still $initialVersion)"
exit 1
}
# rollback to the previous version
vp upgrade --rollback
vp --version
vp env doctor
# Verify version restored after rollback
$rollbackVersion = Get-CliVersion
Write-Host "Rollback version: $rollbackVersion"
if ($rollbackVersion -ne $initialVersion) {
Write-Error "Error: version should have been restored after rollback (expected $initialVersion, got $rollbackVersion)"
exit 1
}
- name: Test upgrade (cmd)
if: ${{ matrix.os == 'windows-latest' }}
shell: cmd
run: |
REM Save initial (dev build) version
for /f "usebackq delims=" %%v in (`node -p "require(require('path').resolve(process.env.USERPROFILE, '.vite-plus', 'current', 'node_modules', 'vite-plus', 'package.json')).version"`) do set INITIAL_VERSION=%%v
echo Initial version: %INITIAL_VERSION%
REM --check queries npm registry and prints update status
vp upgrade --check
REM full upgrade: download, extract, swap
vp upgrade --force
vp --version
vp env doctor
dir "%USERPROFILE%\.vite-plus\"
REM Verify version changed after update
for /f "usebackq delims=" %%v in (`node -p "require(require('path').resolve(process.env.USERPROFILE, '.vite-plus', 'current', 'node_modules', 'vite-plus', 'package.json')).version"`) do set UPDATED_VERSION=%%v
echo Updated version: %UPDATED_VERSION%
if "%UPDATED_VERSION%"=="%INITIAL_VERSION%" (
echo Error: version should have changed after upgrade, still %INITIAL_VERSION%
exit /b 1
)
REM rollback to the previous version
vp upgrade --rollback
vp --version
vp env doctor
REM Verify version restored after rollback
for /f "usebackq delims=" %%v in (`node -p "require(require('path').resolve(process.env.USERPROFILE, '.vite-plus', 'current', 'node_modules', 'vite-plus', 'package.json')).version"`) do set ROLLBACK_VERSION=%%v
echo Rollback version: %ROLLBACK_VERSION%
if not "%ROLLBACK_VERSION%"=="%INITIAL_VERSION%" (
echo Error: version should have been restored after rollback, expected %INITIAL_VERSION%, got %ROLLBACK_VERSION%
exit /b 1
)
- name: Test implode (bash)
shell: bash
run: |
vp implode --yes
ls -la ~/
VP_HOME="${USERPROFILE:-$HOME}/.vite-plus"
if [ -d "$VP_HOME" ]; then
echo "Error: $VP_HOME still exists after implode"
exit 1
fi
# Reinstall
pnpm bootstrap-cli:ci
vp --version
- name: Test implode (powershell)
if: ${{ matrix.os == 'windows-latest' }}
shell: pwsh
run: |
vp implode --yes
Start-Sleep -Seconds 5
dir "$env:USERPROFILE\"
if (Test-Path "$env:USERPROFILE\.vite-plus") {
Write-Error "~/.vite-plus still exists after implode"
exit 1
}
pnpm bootstrap-cli:ci
vp --version
- name: Test implode (cmd)
if: ${{ matrix.os == 'windows-latest' }}
shell: cmd
run: |
REM vp.exe renames its own parent directory; cmd.exe may report
REM "The system cannot find the path specified" on exit — ignore it.
vp implode --yes || ver >NUL
timeout /T 5 /NOBREAK >NUL
dir "%USERPROFILE%\"
if exist "%USERPROFILE%\.vite-plus" (
echo Error: .vite-plus still exists after implode
exit /b 1
)
pnpm bootstrap-cli:ci
vp --version
install-e2e-test:
name: Local CLI `vp install` E2E test
needs:
- download-previous-rolldown-binaries
runs-on: namespace-profile-linux-x64-default
# Run if: not a PR, OR PR has 'test: install-e2e' label
if: >-
github.event_name != 'pull_request' ||
contains(github.event.pull_request.labels.*.name, 'test: install-e2e')
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/actions/clone
- uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: install-e2e-test
- uses: oxc-project/setup-node@fdbf0dfd334c4e6d56ceeb77d91c76339c2a0885 # v1.0.4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rolldown-binaries
path: ./rolldown/packages/rolldown/src
merge-multiple: true
- name: Build with upstream
uses: ./.github/actions/build-upstream
with:
target: x86_64-unknown-linux-gnu
- name: Build CLI
run: |
pnpm bootstrap-cli:ci
echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH
- name: Run local CLI `vp install`
run: |
export PATH=$PWD/node_modules/.bin:$PATH
vp -h
# Test vp install on various repositories with different package managers
repos=(
# pnpm workspace
"pnpm/pnpm:pnpm"
"vitejs/vite:vite"
# yarn workspace
"napi-rs/napi-rs:napi-rs"
"toeverything/AFFiNE:AFFiNE"
# npm workspace
"npm/cli:npm"
"redhat-developer/vscode-extension-tester:vscode-extension-tester"
)
for repo_info in "${repos[@]}"; do
IFS=':' read -r repo dir_name <<< "$repo_info"
echo "Testing vp install on $repo…"
# remove the directory if it exists
if [ -d "$RUNNER_TEMP/$dir_name" ]; then
rm -rf "$RUNNER_TEMP/$dir_name"
fi
git clone --depth 1 "https://github.com/$repo.git" "$RUNNER_TEMP/$dir_name"
cd "$RUNNER_TEMP/$dir_name"
vp install
# run again to show install cache increase by time
time vp install
echo "✓ Successfully installed dependencies for $repo"
echo ""
done
done:
runs-on: ubuntu-latest
if: always()
needs:
- test
- lint
- run
- cli-e2e-test
steps:
- run: exit 1
# Thank you, next https://github.com/vercel/next.js/blob/canary/.github/workflows/build_and_test.yml#L379
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
================================================
FILE: .github/workflows/claude.yml
================================================
name: Claude Code
on:
issues:
types: [assigned]
jobs:
analyze:
if: github.repository == 'voidzero-dev/vite-plus' && github.event.action == 'assigned' && github.event.assignee.login == 'boshen'
runs-on: ubuntu-slim
permissions:
contents: read
issues: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 100
persist-credentials: true
- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@26ec041249acb0a944c0a47b6c0c13f05dbc5b44 # v1.0.70
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
assignee_trigger: 'boshen'
claude_args: --allowedTools "Edit,Write,Read,Glob,Grep,Bash(gh:*),Bash(cargo:*),Bash(git:*),Bash(just:*),WebFetch,TodoWrite"
prompt: |
Analyze issue #${{ github.event.issue.number }} in ${{ github.repository }} and determine if it can be fixed.
First, use `gh issue view ${{ github.event.issue.number }}` to read the issue details.
Then:
1. Search the codebase to gather relevant context (related files, existing implementations, tests)
2. Determine if the issue is fixable and estimate the complexity
Finally, post a comment on the issue with:
- A brief summary of your understanding of the issue
- Relevant files/code you found
- Whether this issue is fixable (yes/no/needs clarification)
- If the issue is unclear, ask for more context
- If fixable, provide a concrete implementation plan with specific steps
- Any potential concerns or blockers
- name: Unassign boshen
if: always()
env:
GH_TOKEN: ${{ github.token }}
run: gh issue edit ${{ github.event.issue.number }} --remove-assignee Boshen
================================================
FILE: .github/workflows/cleanup-cache.yml
================================================
name: Cleanup github runner caches on closed pull requests
on:
pull_request:
types:
- closed
jobs:
cleanup:
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- name: Cleanup
run: |
echo "Fetching list of cache keys"
cacheKeysForPR=$(gh cache list --ref $BRANCH --limit 100 --json id --jq '.[].id')
## Setting this to not fail the workflow while deleting cache keys.
set +e
echo "Deleting caches…"
for cacheKey in $cacheKeysForPR
do
gh cache delete $cacheKey
done
echo "Done"
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge
================================================
FILE: .github/workflows/deny.yml
================================================
name: Cargo Deny
permissions: {}
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize]
paths:
- 'Cargo.lock'
- 'deny.toml'
- '.github/workflows/deny.yml'
push:
branches:
- main
paths:
- 'Cargo.lock'
- 'deny.toml'
- '.github/workflows/deny.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.ref_name != 'main' }}
jobs:
deny:
name: Cargo Deny
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- name: Output rolldown hash
id: upstream-versions
run: node -e "console.log('ROLLDOWN_HASH=' + require('./packages/tools/.upstream-versions.json').rolldown.hash)" >> $GITHUB_OUTPUT
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
repository: rolldown/rolldown
path: rolldown
ref: ${{ steps.upstream-versions.outputs.ROLLDOWN_HASH }}
- uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0
with:
restore-cache: false
# Pinned to 0.18.6+ for CVSS 4.0 support (EmbarkStudios/cargo-deny#805)
tools: cargo-deny@0.19.0
- run: cargo deny check
================================================
FILE: .github/workflows/e2e-test.yml
================================================
name: E2E Test
permissions: {}
on:
workflow_dispatch:
schedule:
# Run every day at 0:00 GMT (8:00 AM Singapore time)
- cron: '0 0 * * *'
push:
branches:
- main
paths-ignore:
- '**/*.md'
pull_request:
types: [opened, synchronize, labeled]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.ref_name != 'main' }}
defaults:
run:
shell: bash
jobs:
detect-changes:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
outputs:
related-files-changed: ${{ steps.filter.outputs.related-files }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
with:
filters: |
related-files:
- 'packages/**/build.ts'
- .github/workflows/e2e-test.yml
- 'ecosystem-ci/*'
download-previous-rolldown-binaries:
needs: detect-changes
runs-on: ubuntu-latest
# Run if: not a PR, OR PR has 'test: e2e' label, OR PR is from deps/upstream-update branch, OR build.ts files changed
if: >-
github.event_name != 'pull_request' ||
contains(github.event.pull_request.labels.*.name, 'test: e2e') ||
github.head_ref == 'deps/upstream-update' ||
needs.detect-changes.outputs.related-files-changed == 'true'
permissions:
contents: read
packages: read
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/actions/download-rolldown-binaries
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
build:
name: Build vite-plus packages (${{ matrix.os }})
runs-on: ${{ matrix.os }}
permissions:
contents: read
packages: read
needs:
- download-previous-rolldown-binaries
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: windows-latest
target: x86_64-pc-windows-msvc
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/actions/clone
# Disable Windows Defender real-time scanning to speed up I/O-heavy builds (~30-50% faster)
- name: Disable Windows Defender
if: runner.os == 'Windows'
shell: powershell
run: Set-MpPreference -DisableRealtimeMonitoring $true
- uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: e2e-build-${{ matrix.os }}
- uses: oxc-project/setup-node@fdbf0dfd334c4e6d56ceeb77d91c76339c2a0885 # v1.0.4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rolldown-binaries
path: ./rolldown/packages/rolldown/src
merge-multiple: true
- name: Build with upstream
uses: ./.github/actions/build-upstream
with:
target: ${{ matrix.target }}
- name: Pack packages into tgz
run: |
mkdir -p tmp/tgz
cd packages/core && pnpm pack --pack-destination ../../tmp/tgz && cd ../..
cd packages/test && pnpm pack --pack-destination ../../tmp/tgz && cd ../..
cd packages/cli && pnpm pack --pack-destination ../../tmp/tgz && cd ../..
# Copy vp binary for e2e-test job (findVpBinary expects it in target/)
cp target/${{ matrix.target }}/release/vp tmp/tgz/vp 2>/dev/null || cp target/${{ matrix.target }}/release/vp.exe tmp/tgz/vp.exe 2>/dev/null || true
cp target/${{ matrix.target }}/release/vp-shim.exe tmp/tgz/vp-shim.exe 2>/dev/null || true
ls -la tmp/tgz
- name: Upload tgz artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: vite-plus-packages-${{ matrix.os }}
path: tmp/tgz/
retention-days: 1
e2e-test:
name: ${{ matrix.project.name }} E2E test (${{ matrix.os }})
env:
# For packing manager install from github package registry
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
permissions:
contents: read
packages: read
needs:
- build
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- windows-latest
project:
- name: vibe-dashboard
node-version: 24
command: |
npx playwright install chromium
# FIXME: Failed to load JS plugin: ./plugins/debugger.js
# vp run ready
vp fmt
vp test
vp run build
# FIXME: TypeError: Failed to fetch dynamically imported module
# - name: skeleton
# node-version: 24
# command: |
# vp run format
# vp run lint:check
# vp run check
# npx playwright install chromium
# vp run test
- name: rollipop
node-version: 22
command: |
vp run -r build
# FIXME: typescript-eslint(no-redundant-type-constituents): 'rolldownExperimental.DevEngine' is an 'error' type that acts as 'any' and overrides all other types in this union type.
vp run lint || true
# FIXME: src/bundler-pool.ts(8,8): error TS2307: Cannot find module '@rollipop/core' or its corresponding type declarations.
vp run -r typecheck || true
vp run format
vp run @rollipop/common#test
vp run @rollipop/core#test
vp run @rollipop/dev-server#test
- name: frm-stack
node-version: 24
command: |
vp run lint:check
vp run format:check
vp run typecheck
vp run @yourcompany/api#test
vp run @yourcompany/backend-core#test
- name: vue-mini
node-version: 24
command: |
# FIXME: skip format for now, will re-enable after prettier migration support
# vp run format
vp run lint
vp run type
vp run test -- --coverage
# SKIP: vite-plugin-react - vite-task config loading incompatibility
# vite-task needs to load vite.config.js for all workspace packages to build the task graph,
# but the vite-plus process starts with workspace root as cwd.
# The plugin-react-swc playgrounds use SWC plugins (e.g., @swc/plugin-emotion) which
# cannot be resolved when loading the config from workspace root.
#
# Minimal reproduction:
# git clone https://github.com/vitejs/vite-plugin-react /tmp/vite-plugin-react-test
# cd /tmp/vite-plugin-react-test && pnpm install && pnpm run build
# node packages/plugin-react-swc/playground/emotion-plugin/vite.config.js
# # Error: Cannot find module '@swc/plugin-emotion'
#
# This works when running from within the playground directory (pnpm run build)
# because pnpm's symlink structure allows resolution, but fails when loading from workspace root.
# - name: vite-plugin-react
# node-version: 22
# command: |
# vp run format
# vp run lint -- --fix
# # TODO(fengmk2): run all builds and tests after tsdown version upgrade
# vp run @vitejs/plugin-rsc#build
# vp run @vitejs/plugin-rsc#test
- name: vitepress
node-version: 24
command: |
npx playwright install chromium
vp run format
vp run build
vp test run -r __tests__/unit
vp run tests-e2e#test
VITE_TEST_BUILD=1 vp run tests-e2e#test
vp run tests-init#test
- name: tanstack-start-helloworld
node-version: 24
command: |
npx playwright install chromium
vp run test
vp run build
- name: oxlint-plugin-complexity
node-version: 22
command: |
vp run format
vp run format:check
vp run build
vp run lint
vp run test:run
npx tsc --noEmit
- name: vite-vue-vercel
node-version: 24
command: |
npx playwright install chromium
vp run test
vp run build
- name: dify
node-version: 24
directory: web
command: |
vp run type-check:tsgo
vp run build
vp run test navigation-utils.test.ts real-browser-flicker.test.tsx workflow-parallel-limit.test.tsx
- name: viteplus-ws-repro
node-version: 24
command: |
vp test run
- name: vp-config
node-version: 22
command: |
vp check
vp pack
vp test
- name: vinext
node-version: 24
command: |
vp run build
vp check --fix
vp run check
vp run test
- name: reactive-resume
node-version: 24
command: |
vp fmt
vp lint --type-aware
vp build
vp test
- name: yaak
node-version: 24
command: |
vp fmt --ignore-path .oxfmtignore
# FIXME: type-aware lint fails with "Invalid tsconfig" without full Rust/wasm bootstrap
vp lint || true
vp test
- name: npmx.dev
node-version: 24
command: |
vp fmt
vp run lint
vp run test:types
vp test --project unit
- name: vite-plus-jest-dom-repro
node-version: 24
command: |
vp test run
exclude:
# frm-stack uses Docker (testcontainers) which doesn't work the same way on Windows
- os: windows-latest
project:
name: frm-stack
# dify only runs on Linux for now
- os: windows-latest
project:
name: dify
# vinext uses workerd native deps that don't build on Windows
- os: windows-latest
project:
name: vinext
# yaak is a Tauri app with Rust/wasm deps
- os: windows-latest
project:
name: yaak
# npmx.dev is a Nuxt app, ubuntu-only for now
- os: windows-latest
project:
name: npmx.dev
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/actions/clone
with:
ecosystem-ci-project: ${{ matrix.project.name }}
# Disable Windows Defender real-time scanning to speed up I/O-heavy operations
- name: Disable Windows Defender
if: runner.os == 'Windows'
shell: powershell
run: Set-MpPreference -DisableRealtimeMonitoring $true
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version: ${{ matrix.project.node-version }}
package-manager-cache: false
- name: Download vite-plus packages
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: vite-plus-packages-${{ matrix.os }}
path: tmp/tgz
- name: Install vp CLI
shell: bash
run: |
# Place vp binary where install-global-cli.ts expects it (target/release/)
mkdir -p target/release
cp tmp/tgz/vp target/release/vp 2>/dev/null || cp tmp/tgz/vp.exe target/release/vp.exe 2>/dev/null || true
cp tmp/tgz/vp-shim.exe target/release/vp-shim.exe 2>/dev/null || true
chmod +x target/release/vp 2>/dev/null || true
node $GITHUB_WORKSPACE/packages/tools/src/install-global-cli.ts --tgz $GITHUB_WORKSPACE/tmp/tgz/vite-plus-0.0.0.tgz
# Use USERPROFILE (native Windows path) instead of HOME (Git Bash path /c/Users/...)
# so cmd.exe and Node.js execSync can resolve binaries in PATH
echo "${USERPROFILE:-$HOME}/.vite-plus/bin" >> $GITHUB_PATH
- name: Migrate in ${{ matrix.project.name }}
working-directory: ${{ runner.temp }}/vite-plus-ecosystem-ci/${{ matrix.project.name }}${{ matrix.project.directory && format('/{0}', matrix.project.directory) || '' }}
shell: bash
run: |
node $GITHUB_WORKSPACE/ecosystem-ci/patch-project.ts ${{ matrix.project.name }}
vp install --no-frozen-lockfile
- name: Verify local tgz packages installed
working-directory: ${{ runner.temp }}/vite-plus-ecosystem-ci/${{ matrix.project.name }}${{ matrix.project.directory && format('/{0}', matrix.project.directory) || '' }}
shell: bash
run: node $GITHUB_WORKSPACE/ecosystem-ci/verify-install.ts
- name: Run vite-plus commands in ${{ matrix.project.name }}
working-directory: ${{ runner.temp }}/vite-plus-ecosystem-ci/${{ matrix.project.name }}${{ matrix.project.directory && format('/{0}', matrix.project.directory) || '' }}
run: ${{ matrix.project.command }}
notify-failure:
name: Notify on failure
runs-on: ubuntu-latest
needs: e2e-test
if: ${{ failure() && github.event_name == 'schedule' }}
permissions:
contents: read
issues: write
steps:
- name: Create or update GitHub issue on failure
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
run: |
ISSUE_TITLE="E2E Test Scheduled Run Failed"
ISSUE_LABEL="e2e-failure"
RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
# Create label if it doesn't exist
if ! gh label list --json name --jq '.[].name' | grep -q "^${ISSUE_LABEL}$"; then
CREATE_LABEL_OUTPUT=$(gh label create "$ISSUE_LABEL" --color "d73a4a" --description "E2E test scheduled run failure" 2>&1)
if [ $? -eq 0 ]; then
echo "Created label: $ISSUE_LABEL"
elif echo "$CREATE_LABEL_OUTPUT" | grep -qi "already exists"; then
echo "Label '$ISSUE_LABEL' already exists, continuing."
else
echo "Error: Failed to create label '$ISSUE_LABEL':"
echo "$CREATE_LABEL_OUTPUT" >&2
exit 1
fi
fi
# Search for existing open issue with the label
EXISTING_ISSUE=$(gh issue list --label "$ISSUE_LABEL" --state open --json number --jq '.[0].number')
if [ -z "$EXISTING_ISSUE" ]; then
# Create new issue if none exists
gh issue create \
--title "$ISSUE_TITLE" \
--label "$ISSUE_LABEL" \
--body "The scheduled E2E test run has failed.
**Failed Run:** $RUN_URL
**Time:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')
Please investigate the failure and fix any issues."
echo "Created new issue"
else
# Add comment to existing issue
gh issue comment "$EXISTING_ISSUE" \
--body "The scheduled E2E test run has failed again.
**Failed Run:** $RUN_URL
**Time:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
echo "Added comment to issue #$EXISTING_ISSUE"
fi
================================================
FILE: .github/workflows/issue-close-require.yml
================================================
name: Issue Close Require
on:
schedule:
- cron: '0 0 * * *'
jobs:
close-issues:
if: github.repository == 'voidzero-dev/vite-plus'
runs-on: ubuntu-slim
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
steps:
- name: needs reproduction
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3
with:
actions: 'close-issues'
token: ${{ secrets.GITHUB_TOKEN }}
labels: 'needs reproduction'
inactive-day: 3
================================================
FILE: .github/workflows/issue-labeled.yml
================================================
name: Issue Labeled
on:
issues:
types: [labeled]
jobs:
reply-labeled:
if: github.repository == 'voidzero-dev/vite-plus'
runs-on: ubuntu-slim
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
steps:
- name: contribution welcome
if: github.event.label.name == 'contribution welcome' || github.event.label.name == 'help wanted'
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3
with:
actions: 'remove-labels'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
labels: 'pending triage, needs reproduction'
- name: needs reproduction
if: github.event.label.name == 'needs reproduction'
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3
with:
actions: 'create-comment, remove-labels'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
labels: 'pending triage'
body: |
Hello @${{ github.event.issue.user.login }} 👋
Please provide a [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) using a GitHub repository. This helps us understand and resolve your issue much faster.
**A good reproduction should be:**
- **Minimal** – include only the code necessary to demonstrate the issue
- **Complete** – contain everything needed to run and observe the problem
- **Reproducible** – consistently show the issue with clear steps
If no reproduction is provided, issues labeled `needs reproduction` will be closed after 3 days of inactivity.
For more context on why this is required, please read: https://antfu.me/posts/why-reproductions-are-required
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
workflow_dispatch:
inputs:
npm_tag:
description: 'npm tag for publish'
required: true
default: 'latest'
type: choice
options:
- latest
- alpha
version:
description: 'Override version (leave empty to auto-compute). Use when retrying a failed publish.'
required: false
default: ''
type: string
permissions: {}
env:
RELEASE_BUILD: 'true'
DEBUG: 'napi:*'
NPM_TAG: ${{ inputs.npm_tag }}
jobs:
prepare:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
fetch-tags: true
- uses: ./.github/actions/set-snapshot-version
if: ${{ inputs.version == '' }}
id: computed
with:
npm_tag: ${{ inputs.npm_tag }}
- name: Set final version
id: version
run: echo "version=${{ inputs.version || steps.computed.outputs.version }}" >> $GITHUB_OUTPUT
build-rust:
runs-on: ${{ matrix.settings.os }}
needs: prepare
permissions:
contents: read
env:
VERSION: ${{ needs.prepare.outputs.version }}
strategy:
fail-fast: false
matrix:
settings:
- target: aarch64-apple-darwin
os: macos-latest
- target: x86_64-apple-darwin
os: macos-latest
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
- target: x86_64-pc-windows-msvc
os: windows-latest
- target: aarch64-pc-windows-msvc
os: windows-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/actions/clone
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
- uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.2
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: release
- name: Rustup Adds Target
run: rustup target add ${{ matrix.settings.target }}
- uses: oxc-project/setup-node@fdbf0dfd334c4e6d56ceeb77d91c76339c2a0885 # v1.0.4
- name: Set binding version
shell: bash
run: |
pnpm exec tool replace-file-content packages/cli/binding/Cargo.toml 'version = "0.0.0"' 'version = "${{ env.VERSION }}"'
pnpm exec tool replace-file-content crates/vite_global_cli/Cargo.toml 'version = "0.0.0"' 'version = "${{ env.VERSION }}"'
cat crates/vite_global_cli/Cargo.toml
- name: Verify version replacement
shell: bash
run: |
if grep -q 'version = "0.0.0"' crates/vite_global_cli/Cargo.toml; then
echo "ERROR: Version replacement failed for crates/vite_global_cli/Cargo.toml"
head -5 crates/vite_global_cli/Cargo.toml
exit 1
fi
if grep -q 'version = "0.0.0"' packages/cli/binding/Cargo.toml; then
echo "ERROR: Version replacement failed for packages/cli/binding/Cargo.toml"
head -5 packages/cli/binding/Cargo.toml
exit 1
fi
echo "Version replacement verified successfully"
- name: Build
uses: ./.github/actions/build-upstream
with:
target: ${{ matrix.settings.target }}
- name: Upload Vite+ native artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: vite-plus-native-${{ matrix.settings.target }}
path: ./packages/cli/binding/*.node
if-no-files-found: error
- name: Upload Rust CLI binary artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: vite-global-cli-${{ matrix.settings.target }}
path: |
./target/${{ matrix.settings.target }}/release/vp
./target/${{ matrix.settings.target }}/release/vp.exe
./target/${{ matrix.settings.target }}/release/vp-shim.exe
if-no-files-found: error
- name: Remove .node files before upload dist
if: ${{ matrix.settings.target == 'x86_64-unknown-linux-gnu' }}
run: |
rm ./packages/core/dist/**/*.node
- name: Upload core dist
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: ${{ matrix.settings.target == 'x86_64-unknown-linux-gnu' }}
with:
name: core
path: ./packages/core/dist
if-no-files-found: error
- name: Upload cli dist
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: ${{ matrix.settings.target == 'x86_64-unknown-linux-gnu' }}
with:
name: cli
path: ./packages/cli/dist
if-no-files-found: error
- name: Upload cli skills (docs for agent integration)
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: ${{ matrix.settings.target == 'x86_64-unknown-linux-gnu' }}
with:
name: cli-skills
path: ./packages/cli/skills
if-no-files-found: error
Release:
runs-on: ubuntu-latest
needs: [prepare, build-rust]
permissions:
contents: write
packages: write
id-token: write # Required for OIDC
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/actions/clone
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: .node-version
package-manager-cache: false
registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Download cli dist
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
path: packages/cli/dist
pattern: cli
merge-multiple: true
- name: Download cli skills
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
path: packages/cli/skills
pattern: cli-skills
merge-multiple: true
- name: Download cli binding
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
path: packages/cli/artifacts
pattern: vite-plus-native-*
- name: Download core dist
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
path: packages/core/dist
pattern: core
merge-multiple: true
- uses: ./.github/actions/download-rolldown-binaries
with:
github-token: ${{ github.token }}
target: x86_64-unknown-linux-gnu
upload: 'false'
- name: Download Rust CLI binaries
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
path: rust-cli-artifacts
pattern: vite-global-cli-*
- name: Move Rust CLI binaries to target directories
run: |
# Move each artifact's binary to the correct target directory
for artifact_dir in rust-cli-artifacts/vite-global-cli-*/; do
if [ -d "$artifact_dir" ]; then
# Extract target name from directory (e.g., vite-global-cli-x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
dir_name=$(basename "$artifact_dir")
target_name=${dir_name#vite-global-cli-}
# Create target directory and copy binary
mkdir -p "target/${target_name}/release"
cp -r "$artifact_dir"* "target/${target_name}/release/"
fi
done
# Show what we have (fail if no binaries found)
vp_files=$(find target -name "vp*" -type f 2>/dev/null || echo "")
if [ -z "$vp_files" ]; then
echo "Error: No vp binaries found in target directory"
echo "Artifact contents:"
find rust-cli-artifacts -type f || true
exit 1
fi
echo "Found binaries:"
echo "$vp_files"
- name: Set npm packages version
run: |
sed -i 's/"version": "0.0.0"/"version": "${{ env.VERSION }}"/' packages/core/package.json
sed -i 's/"version": "0.0.0"/"version": "${{ env.VERSION }}"/' packages/test/package.json
sed -i 's/"version": "0.0.0"/"version": "${{ env.VERSION }}"/' packages/cli/package.json
- name: Build test
run: pnpm --filter=@voidzero-dev/vite-plus-test build
- name: 'Setup npm'
run: |
npm install -g npm@latest
- name: Publish native addons
run: |
node ./packages/cli/publish-native-addons.ts
- name: Publish
run: |
pnpm publish --filter=./packages/core --tag ${{ inputs.npm_tag }} --access public --no-git-checks
pnpm publish --filter=./packages/test --tag ${{ inputs.npm_tag }} --access public --no-git-checks
pnpm publish --filter=./packages/cli --tag ${{ inputs.npm_tag }} --access public --no-git-checks
- name: Create release body
run: |
if [[ "${{ inputs.npm_tag }}" == "latest" ]]; then
INSTALL_BASH="curl -fsSL https://vite.plus | bash"
INSTALL_PS1="irm https://vite.plus/ps1 | iex"
else
INSTALL_BASH="curl -fsSL https://vite.plus | VITE_PLUS_VERSION=${{ env.VERSION }} bash"
INSTALL_PS1="\\\$env:VITE_PLUS_VERSION=\\\"${{ env.VERSION }}\\\"; irm https://vite.plus/ps1 | iex"
fi
cat > ./RELEASE_BODY.md <> $GITHUB_PATH
- name: Verify bin setup
run: |
# Verify bin directory was created by vp env --setup
BIN_PATH="$HOME/.vite-plus/bin"
ls -al "$BIN_PATH"
if [ ! -d "$BIN_PATH" ]; then
echo "Error: Bin directory not found: $BIN_PATH"
exit 1
fi
# Verify shim executables exist
for shim in node npm npx; do
if [ ! -f "$BIN_PATH/$shim" ]; then
echo "Error: Shim not found: $BIN_PATH/$shim"
exit 1
fi
echo "Found shim: $BIN_PATH/$shim"
done
# Verify vp env doctor works
vp env doctor
vp env run --node 24 -- node -p "process.versions"
which node
which npm
which npx
which vp
- name: Verify upgrade
run: |
# --check queries npm registry and prints update status
vp upgrade --check
vp upgrade 0.0.0-gbe8891a5.20260227-1615
vp --version
# rollback to the previous version (should succeed after a real update)
vp upgrade --rollback
vp --version
test-install-sh-readonly-config:
name: Test install.sh (readonly shell config)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Make shell config files read-only
run: |
# Simulate Nix-managed or read-only shell configs
touch ~/.bashrc ~/.bash_profile ~/.profile
chmod 444 ~/.bashrc ~/.bash_profile ~/.profile
- name: Run install.sh
run: |
output=$(cat packages/cli/install.sh | bash 2>&1) || {
echo "$output"
echo "Install script exited with non-zero status"
exit 1
}
echo "$output"
# Verify installation succeeds (not a fatal error)
echo "$output" | grep -q "successfully installed"
# Verify fallback message shows binary location
echo "$output" | grep -q "vp was installed to:"
# Verify fallback message shows manual instructions
echo "$output" | grep -q "Or run vp directly:"
# Verify the permission warning was shown
echo "$output" | grep -qi "permission denied"
- name: Verify vp works via direct path
run: |
~/.vite-plus/bin/vp --version
test-install-sh-arm64:
name: Test install.sh (Linux ARM64 glibc via QEMU)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
with:
platforms: arm64
- name: Run install.sh in ARM64 container
run: |
docker run --rm --platform linux/arm64 \
-v "${{ github.workspace }}:/workspace" \
-e VITE_PLUS_VERSION=alpha \
ubuntu:20.04 bash -c "
ls -al ~/
apt-get update && apt-get install -y curl ca-certificates
cat /workspace/packages/cli/install.sh | bash
if [ -f ~/.profile ]; then
source ~/.profile
elif [ -f ~/.bashrc ]; then
source ~/.bashrc
else
export PATH="$HOME/.vite-plus/bin:$PATH"
fi
vp --version
vp --help
vp dlx print-current-version
# Verify bin setup
BIN_PATH=\"\$HOME/.vite-plus/bin\"
if [ ! -d \"\$BIN_PATH\" ]; then
echo \"Error: Bin directory not found: \$BIN_PATH\"
exit 1
fi
for shim in node npm npx; do
if [ ! -f \"\$BIN_PATH/\$shim\" ]; then
echo \"Error: Shim not found: \$BIN_PATH/\$shim\"
exit 1
fi
echo \"Found shim: \$BIN_PATH/\$shim\"
done
vp env doctor
export VITE_LOG=trace
vp env run --node 24 -- node -p \"process.versions\"
# Verify upgrade
vp upgrade --check
vp upgrade 0.0.0-gbe8891a5.20260227-1615
vp --version
vp upgrade --rollback
vp --version
# FIXME: qemu: uncaught target signal 11 (Segmentation fault) - core dumped
# vp create vite --no-interactive --no-agent -- hello --no-interactive -t vanilla
# cd hello && vp run build
"
test-install-ps1-v5:
name: Test install.ps1 (Windows x64, PowerShell 5.1)
runs-on: windows-latest
permissions:
contents: read
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Assert PowerShell 5.x
shell: powershell
run: |
Write-Host "PowerShell version: $($PSVersionTable.PSVersion)"
if ($PSVersionTable.PSVersion.Major -ne 5) {
Write-Error "Expected PowerShell 5.x but got $($PSVersionTable.PSVersion)"
exit 1
}
- name: Run install.ps1
shell: powershell
run: |
& ./packages/cli/install.ps1
- name: Run install.ps1 via irm simulation (catches BOM issues)
shell: powershell
run: |
$ErrorActionPreference = "Stop"
Get-Content ./packages/cli/install.ps1 -Raw | Invoke-Expression
- name: Set PATH
shell: bash
run: |
echo "$USERPROFILE\.vite-plus\bin" >> $GITHUB_PATH
- name: Verify installation
shell: powershell
working-directory: ${{ runner.temp }}
run: |
Write-Host "PATH: $env:Path"
vp --version
vp --help
vp create vite --no-interactive --no-agent -- hello --no-interactive -t vanilla
cd hello
vp run build
vp --version
- name: Verify bin setup
shell: powershell
run: |
$binPath = "$env:USERPROFILE\.vite-plus\bin"
Get-ChildItem -Force $binPath
if (-not (Test-Path $binPath)) {
Write-Error "Bin directory not found: $binPath"
exit 1
}
$expectedShims = @("node.exe", "npm.exe", "npx.exe")
foreach ($shim in $expectedShims) {
$shimFile = Join-Path $binPath $shim
if (-not (Test-Path $shimFile)) {
Write-Error "Shim not found: $shimFile"
exit 1
}
Write-Host "Found shim: $shimFile"
}
where.exe node
where.exe npm
where.exe npx
where.exe vp
$env:Path = "$env:USERPROFILE\.vite-plus\bin;$env:Path"
vp env doctor
vp env run --node 24 -- node -p "process.versions"
- name: Verify upgrade
shell: powershell
run: |
vp upgrade --check
vp upgrade 0.0.0-gbe8891a5.20260227-1615
vp --version
vp upgrade --rollback
vp --version
test-install-ps1-arm64:
name: Test install.ps1 (Windows ARM64)
runs-on: windows-11-arm
permissions:
contents: read
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Run install.ps1
shell: pwsh
run: |
& ./packages/cli/install.ps1
- name: Set PATH
shell: bash
run: |
echo "$USERPROFILE\.vite-plus\bin" >> $GITHUB_PATH
- name: Verify installation
shell: pwsh
working-directory: ${{ runner.temp }}
run: |
Write-Host "PATH: $env:Path"
vp --version
vp --help
vp create vite --no-interactive --no-agent -- hello --no-interactive -t vanilla
cd hello
vp run build
vp --version
- name: Verify bin setup
shell: pwsh
run: |
$binPath = "$env:USERPROFILE\.vite-plus\bin"
Get-ChildItem -Force $binPath
if (-not (Test-Path $binPath)) {
Write-Error "Bin directory not found: $binPath"
exit 1
}
$expectedShims = @("node.exe", "npm.exe", "npx.exe")
foreach ($shim in $expectedShims) {
$shimFile = Join-Path $binPath $shim
if (-not (Test-Path $shimFile)) {
Write-Error "Shim not found: $shimFile"
exit 1
}
Write-Host "Found shim: $shimFile"
}
where.exe node
where.exe npm
where.exe npx
where.exe vp
$env:Path = "$env:USERPROFILE\.vite-plus\bin;$env:Path"
vp env doctor
vp env run --node 24 -- node -p "process.versions"
- name: Verify upgrade
shell: pwsh
run: |
vp upgrade --check
vp upgrade 0.0.0-gbe8891a5.20260227-1615
vp --version
vp upgrade --rollback
vp --version
test-install-ps1:
name: Test install.ps1 (Windows x64)
runs-on: windows-latest
permissions:
contents: read
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Run install.ps1
shell: pwsh
run: |
& ./packages/cli/install.ps1
- name: Set PATH
shell: bash
run: |
echo "$USERPROFILE\.vite-plus\bin" >> $GITHUB_PATH
- name: Verify upgrade
shell: pwsh
run: |
# --check queries npm registry and prints update status
vp upgrade --check
vp upgrade 0.0.0-gbe8891a5.20260227-1615
vp --version
# rollback to the previous version (should succeed after a real update)
vp upgrade --rollback
vp --version
- name: Verify installation on powershell
shell: pwsh
working-directory: ${{ runner.temp }}
run: |
# Print PATH from environment
echo "PATH: $env:Path"
vp --version
vp --help
# $env:VITE_LOG = "trace"
# test create command
vp create vite --no-interactive --no-agent -- hello --no-interactive -t vanilla
cd hello && vp run build && vp --version
- name: Verify bin setup on powershell
shell: pwsh
run: |
# Verify bin directory was created by vp env --setup
$binPath = "$env:USERPROFILE\.vite-plus\bin"
Get-ChildItem -Force $binPath
if (-not (Test-Path $binPath)) {
Write-Error "Bin directory not found: $binPath"
exit 1
}
# Verify shim executables exist (trampoline .exe files on Windows)
$expectedShims = @("node.exe", "npm.exe", "npx.exe")
foreach ($shim in $expectedShims) {
$shimFile = Join-Path $binPath $shim
if (-not (Test-Path $shimFile)) {
Write-Error "Shim not found: $shimFile"
exit 1
}
Write-Host "Found shim: $shimFile"
}
where.exe node
where.exe npm
where.exe npx
where.exe vp
# Verify vp env doctor works
$env:Path = "$env:USERPROFILE\.vite-plus\bin;$env:Path"
vp env doctor
vp env run --node 24 -- node -p "process.versions"
- name: Verify installation on cmd
shell: cmd
working-directory: ${{ runner.temp }}
run: |
echo PATH: %PATH%
dir "%USERPROFILE%\.vite-plus"
dir "%USERPROFILE%\.vite-plus\bin"
REM test create command
vp create vite --no-interactive --no-agent -- hello-cmd --no-interactive -t vanilla
cd hello-cmd && vp run build && vp --version
- name: Verify bin setup on cmd
shell: cmd
run: |
REM Verify bin directory was created by vp env --setup
set "BIN_PATH=%USERPROFILE%\.vite-plus\bin"
dir "%BIN_PATH%"
REM Verify shim executables exist (Windows uses trampoline .exe files)
for %%s in (node.exe npm.exe npx.exe vp.exe) do (
if not exist "%BIN_PATH%\%%s" (
echo Error: Shim not found: %BIN_PATH%\%%s
exit /b 1
)
echo Found shim: %BIN_PATH%\%%s
)
where node
where npm
where npx
where vp
REM Verify vp env doctor works
vp env doctor
vp env run --node 24 -- node -p "process.versions"
- name: Verify installation on bash
shell: bash
working-directory: ${{ runner.temp }}
run: |
echo "PATH: $PATH"
ls -al ~/.vite-plus
ls -al ~/.vite-plus/bin
vp --version
vp --help
# test create command
vp create vite --no-interactive --no-agent -- hello-bash --no-interactive -t vanilla
cd hello-bash && vp run build && vp --version
- name: Verify bin setup on bash
shell: bash
run: |
# Verify bin directory was created by vp env --setup
BIN_PATH="$HOME/.vite-plus/bin"
ls -al "$BIN_PATH"
if [ ! -d "$BIN_PATH" ]; then
echo "Error: Bin directory not found: $BIN_PATH"
exit 1
fi
# Verify trampoline .exe files exist
for shim in node.exe npm.exe npx.exe vp.exe; do
if [ ! -f "$BIN_PATH/$shim" ]; then
echo "Error: Trampoline shim not found: $BIN_PATH/$shim"
exit 1
fi
echo "Found trampoline shim: $BIN_PATH/$shim"
done
# Verify vp env doctor works
vp env doctor
vp env run --node 24 -- node -p "process.versions"
which node
which npm
which npx
which vp
================================================
FILE: .github/workflows/upgrade-deps.yml
================================================
name: Upgrade Upstream Dependencies
on:
schedule:
- cron: '0 0 * * *' # Daily at midnight UTC
workflow_dispatch: # Manual trigger
permissions: {}
jobs:
upgrade:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
actions: read
id-token: write
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/actions/clone
- uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: upgrade-deps
tools: just,cargo-shear
- uses: oxc-project/setup-node@fdbf0dfd334c4e6d56ceeb77d91c76339c2a0885 # v1.0.4
- name: Rustup Adds Target
run: rustup target add x86_64-unknown-linux-gnu
- name: Rustup Adds Target for rolldown
working-directory: rolldown
run: rustup target add x86_64-unknown-linux-gnu
- name: Upgrade dependencies
id: upgrade
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node .github/scripts/upgrade-deps.mjs
- name: Sync remote and build
id: build
continue-on-error: true # Create PR even if build fails
run: |
pnpm install --no-frozen-lockfile
pnpm tool sync-remote
pnpm install --no-frozen-lockfile
- name: Build
uses: ./.github/actions/build-upstream
id: build-upstream
continue-on-error: true
with:
target: x86_64-unknown-linux-gnu
print-after-build: 'true'
env:
RELEASE_BUILD: 'true'
- uses: anthropics/claude-code-action@eb99fb38f09dedf69f423f1315d6c0272ace56a0 # Claude Code to 2.1.72
env:
RELEASE_BUILD: 'true'
with:
claude_code_oauth_token: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
show_full_output: 'true'
prompt: |
Check if the build-upstream steps failed and fix them.
### Background
- The build-upstream steps are at ./.github/actions/build-upstream/action.yml
- The deps upgrade script is at ./.github/scripts/upgrade-deps.mjs
### Instructions
- We are using `pnpm` as the package manager
- We are aiming to upgrade all dependencies to the latest versions in this workflow, so don't downgrade any dependencies.
- Compare tsdown CLI options with `vp pack` and sync any new or removed options. Follow the instructions in `.claude/skills/sync-tsdown-cli/SKILL.md`.
- Check `.claude/agents/cargo-workspace-merger.md` if rolldown hash is changed.
- Run the steps in `build-upstream` action.yml after your fixing. If no errors are found, you can safe to exit.
- Install global CLI after the build-upstream steps are successful, by running the following commands:
- `pnpm bootstrap-cli:ci`
- `echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH`
- Run `pnpm run lint` to check if there are any issues after the build, if has, deep investigate it and fix it. You need to run `just build` before you can run `pnpm run lint`.
- Run `pnpm run test` after `just build` to ensure all tests are successful.
- The snapshot tests in `pnpm run test` are always successful, you need to check the snapshot diffs in git to see if there is anything wrong after our deps upgrade.
- If deps in our `Cargo.toml` need to be upgraded, you can refer to the `./.claude/agents/cargo-workspace-merger.md`
- If `Cargo.toml` has been modified, you need to run `cargo shear` to ensure there is nothing wrong with our dependencies.
- Run `cargo check --all-targets --all-features` to ensure everything works fine if any Rust related codes are modified.
- Run the following commands to ensure everything works fine:
vp -h
vp run -h
vp lint -h
vp test -h
vp build -h
vp fmt -h
vp pack -h
- Your final step is to run `just build` to ensure all builds are successful.
Help me fix the errors in `build-upstream` steps if exists.
No need to commit changes after your fixing we have a following step to commit all file changes.
claude_args: |
--model opus --allowedTools "Bash,Edit,Replace,NotebookEditCell"
additional_permissions: |
actions: read
- name: Update lockfile
run: |
pnpm install --no-frozen-lockfile
pnpm dedupe
- name: Checkout binding files
run: |
git checkout packages/cli/binding/index.cjs
git checkout packages/cli/binding/index.d.cts
- name: Format code
run: pnpm fmt
- name: Close and delete previous PR
env:
GH_TOKEN: ${{ secrets.AUTO_UPDATE_BRANCH_TOKEN }}
run: |
# Find PR with the deps/upstream-update branch
PR_NUMBER=$(gh pr list --head deps/upstream-update --json number --jq '.[0].number')
if [ -n "$PR_NUMBER" ]; then
echo "Found existing PR #$PR_NUMBER, closing and deleting branch…"
gh pr close "$PR_NUMBER" --delete-branch
else
echo "No existing PR found with branch deps/upstream-update"
fi
- name: Create/Update PR
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11
with:
base: main
branch: deps/upstream-update
title: 'feat(deps): upgrade upstream dependencies'
sign-commits: true
token: ${{ secrets.AUTO_UPDATE_BRANCH_TOKEN }}
branch-token: ${{ secrets.GITHUB_TOKEN }}
body: |
Automated daily upgrade of upstream dependencies:
- rolldown (latest tag)
- vite (latest tag)
- vitest (latest npm version)
- tsdown (latest npm version)
Build status: ${{ steps.build.outcome }}
commit-message: 'feat(deps): upgrade upstream dependencies'
================================================
FILE: .github/workflows/zizmor.yml
================================================
name: Zizmor
permissions: {}
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize]
paths:
- '.github/workflows/**'
push:
branches:
- main
- 'renovate/**'
paths:
- '.github/workflows/**'
jobs:
zizmor:
name: zizmor
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
persist-credentials: false
submodules: true
- uses: taiki-e/install-action@ae97ff9daf1cd2e216671a047d80ff48461e30bb # v2.49.1
with:
tool: zizmor
- name: Run zizmor
run: zizmor --format sarif . > results.sarif
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3
with:
sarif_file: results.sarif
category: zizmor
================================================
FILE: .gitignore
================================================
/target
node_modules
dist
.claude/settings.local.json
*.tsbuildinfo
.DS_Store
rolldown
rolldown-vite
vite
/crates/vite_global_cli/vp
================================================
FILE: .husky/pre-commit
================================================
pnpm lint-staged
================================================
FILE: .node-version
================================================
22.18.0
================================================
FILE: .rustfmt.toml
================================================
style_edition = "2024"
# Make Rust more readable given most people have wide screens nowadays.
# This is also the setting used by [rustc](https://github.com/rust-lang/rust/blob/master/rustfmt.toml)
use_small_heuristics = "Max"
# Use field initialize shorthand if possible
use_field_init_shorthand = true
reorder_modules = true
# All unstable features that we wish for
# unstable_features = true
# Provide a cleaner impl order
reorder_impl_items = true
# Provide a cleaner import sort order
group_imports = "StdExternalCrate"
# Group "use" statements by crate
imports_granularity = "Crate"
================================================
FILE: .typos.toml
================================================
[default.extend-words]
ratatui = "ratatui"
PUNICODE = "PUNICODE"
Jod = "Jod" # Node.js v22 LTS codename
[files]
extend-exclude = [
"**/snap-tests/**/snap.txt",
"crates/fspy_detours_sys/detours",
"crates/fspy_detours_sys/src/generated_bindings.rs",
"packages/cli/src/oxfmt-config.ts",
]
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": ["VoidZero.vite-plus-extension-pack"]
}
================================================
FILE: .vscode/settings.json
================================================
{
"editor.formatOnSave": true,
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"[javascript]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"[json]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"typescript.preferences.importModuleSpecifierEnding": "js",
"typescript.reportStyleChecksAsWarnings": false,
"typescript.updateImportsOnFileMove.enabled": "always",
"typescript.experimental.useTsgo": true
}
================================================
FILE: CLAUDE.md
================================================
# Vite-Plus
A monorepo task runner (like nx/turbo) with intelligent caching and dependency resolution.
## Core Concept
**Task Execution**: Run tasks across monorepo packages with automatic dependency ordering.
```bash
# Built-in commands
vp build # Run Vite build (dedicated command)
vp test # Run Vitest (dedicated command)
vp lint # Run oxlint (dedicated command)
# Run tasks across packages (explicit mode)
vp run build -r # recursive with topological ordering
vp run app#build web#build # specific packages
vp run build -r --no-topological # recursive without implicit deps
# Run task in current package (implicit mode - for non-built-in tasks)
vp run dev # runs dev script from package.json
```
## Key Architecture
- **Entry**: `crates/vite_task/src/lib.rs` - CLI parsing and main logic
- **Workspace**: `crates/vite_task/src/config/workspace.rs` - Loads packages, creates task graph
- **Task Graph**: `crates/vite_task/src/config/task_graph_builder.rs` - Builds dependency graph
- **Execution**: `crates/vite_task/src/schedule.rs` - Executes tasks in dependency order
## Task Dependencies
1. **Explicit** (always applied): Defined in `vite-task.json`
```json
{
"tasks": {
"test": {
"command": "jest",
"dependsOn": ["build", "lint"]
}
}
}
```
2. **Implicit** (when `--topological`): Based on package.json dependencies
- If A depends on B, then A#build depends on B#build automatically
## Key Features
- **Topological Flag**: Controls implicit dependencies from package relationships
- Default: ON for `--recursive`, OFF otherwise
- Toggle with `--no-topological` to disable
- **Boolean Flags**: All support `--no-*` pattern for explicit disable
- Example: `--recursive` vs `--no-recursive`
- Conflicts handled by clap
- If you want to add a new boolean flag, follow this pattern
## Path Type System
- **Type Safety**: All paths use typed `vite_path` instead of `std::path` for better safety
- **Absolute Paths**: `vite_path::AbsolutePath` / `AbsolutePathBuf`
- **Relative Paths**: `vite_path::RelativePath` / `RelativePathBuf`
- **Usage Guidelines**:
- Use methods such as `strip_prefix`/`join` provided in `vite_path` for path operations instead of converting to std paths
- Only convert to std paths when interfacing with std library functions, and this should be implicit in most cases thanks to `AsRef` implementations
- Add necessary methods in `vite_path` instead of falling back to std path types
- **Converting from std paths** (e.g., `TempDir::path()`):
```rust
let temp_path = AbsolutePathBuf::new(temp_dir.path().to_path_buf()).unwrap();
```
- **Function signatures**: Prefer `&AbsolutePath` over `&std::path::Path`
- **Passing to std functions**: `AbsolutePath` implements `AsRef`, use `.as_path()` when explicit `&Path` is required
## Clippy Rules
All **new** Rust code must follow the custom clippy rules defined in `.clippy.toml` (disallowed types, macros, and methods). Existing code may not fully comply due to historical reasons.
## CLI Output
All user-facing output must go through shared output modules instead of raw print calls.
- **Rust**: Use `vite_shared::output` functions (`info`, `warn`, `error`, `note`, `success`) — never raw `println!`/`eprintln!` (enforced by clippy `disallowed-macros`)
- **TypeScript**: Use `packages/cli/src/utils/terminal.ts` functions (`infoMsg`, `warnMsg`, `errorMsg`, `noteMsg`, `log`) — never raw `console.log`/`console.error`
## Git Workflow
- Run `vp check --fix` before committing to format and lint code
## Quick Reference
- **Compound Commands**: `"build": "tsc && rollup"` splits into subtasks
- **Task Format**: `package#task` (e.g., `app#build`)
- **Path Types**: Use `vite_path` types instead of `std::path` types for type safety
- **Tests**: Run `cargo test -p vite_task` to verify changes
- **Debug**: Use `--debug` to see cache operations
## Tests
- Run `cargo test` to execute all tests
- You never need to run `pnpm install` in the test fixtures dir, vite-plus should able to load and parse the workspace without `pnpm install`.
## Build
- Run `pnpm bootstrap-cli` from the project root to build all packages and install the global CLI
- This builds all `@voidzero-dev/*` and `vite-plus` packages
- Compiles the Rust NAPI bindings and the `vp` Rust binary
- Installs the CLI globally to `~/.vite-plus/`
## Snap Tests
Snap tests are located in `packages/cli/snap-tests/` (local CLI) and `packages/cli/snap-tests-global/` (global CLI). Each test case is a directory containing:
- `package.json` - Package configuration for the test
- `steps.json` - Commands to run and environment variables
- `src/` - Source files for the test
- `snap.txt` - Expected output (generated/updated by running the test)
```bash
# Run all snap tests (local + global)
pnpm -F vite-plus snap-test
# Run only local CLI snap tests
pnpm -F vite-plus snap-test-local
pnpm -F vite-plus snap-test-local
# Run only global CLI snap tests
pnpm -F vite-plus snap-test-global
pnpm -F vite-plus snap-test-global
```
The snap test will automatically generate/update the `snap.txt` file with the command outputs. It exits with zero status even if there are output differences; you need to manually check the diffs(`git diff`) to verify correctness.
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing Guide
## Initial Setup
### macOS / Linux
You'll need the following tools installed on your system:
```
brew install pnpm node just cmake
```
Install Rust & Cargo using rustup:
```
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo install cargo-binstall
```
Initial setup to install dependencies for Vite+:
```
just init
```
### Windows
You'll need the following tools installed on your system. You can use [winget](https://learn.microsoft.com/en-us/windows/package-manager/).
```powershell
winget install pnpm.pnpm OpenJS.NodeJS.LTS Casey.Just Kitware.CMake
```
Install Rust & Cargo from [rustup.rs](https://rustup.rs/), then install `cargo-binstall`:
```powershell
cargo install cargo-binstall
```
Initial setup to install dependencies for Vite+:
```powershell
just init
```
**Note:** Run commands in PowerShell or Windows Terminal. Some commands may require elevated permissions.
## Build Vite+ and upstream dependencies
To create a release build of Vite+ and all upstream dependencies, run:
```
just build
```
## Install the Vite+ Global CLI from source code
```
pnpm bootstrap-cli
vp --version
```
This builds all packages, compiles the Rust `vp` binary, and installs the CLI to `~/.vite-plus`.
## Workflow for build and test
You can run this command to build, test and check if there are any snapshot changes:
```
pnpm bootstrap-cli && pnpm test && git status
```
## Running Snap Tests
Snap tests verify CLI output. They are located in `packages/cli/snap-tests/` (local CLI) and `packages/cli/snap-tests-global/` (global CLI).
```bash
# Run all snap tests (local + global)
pnpm -F vite-plus snap-test
# Run only local CLI snap tests
pnpm -F vite-plus snap-test-local
pnpm -F vite-plus snap-test-local
# Run only global CLI snap tests
pnpm -F vite-plus snap-test-global
pnpm -F vite-plus snap-test-global
```
Snap tests auto-generate `snap.txt` files. Check `git diff` to verify output changes are correct.
## Verified Commits
All commits in PR branches should be GitHub-verified so reviewers can confirm commit authenticity.
Set up local commit signing and GitHub verification first:
- Follow GitHub's guide for GPG commit signature verification: https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification#gpg-commit-signature-verification
- If you use Graphite, add the Graphite GPG key to your GitHub account from the Graphite UI as well, otherwise commits updated by Graphite won't show as verified.
After setup, re-sign any existing commits in your branch so the full branch is verified:
```bash
# Re-sign each commit on your branch (replace origin/main with your branch base if needed)
git rebase -i origin/main
# At each stop:
git commit --amend --date=now --no-edit -S
# Then continue:
git rebase --continue
```
When done, force-push the updated branch history:
```bash
git push --force-with-lease
```
## Pull upstream dependencies
> [!NOTE]
>
> Upstream dependencies only need to be updated when an ["upgrade upstream dependencies"](https://github.com/voidzero-dev/vite-plus/pulls?q=is%3Apr+feat%28deps%29%3A+upgrade+upstream+dependencies+merged) pull request is merged.
To sync the latest upstream dependencies such as Rolldown and Vite, run:
```
pnpm tool sync-remote
just build
```
## macOS Performance Tip
If you are using macOS, add your terminal app (Ghostty, iTerm2, Terminal, …) to the approved "Developer Tools" apps in the Privacy panel of System Settings and restart your terminal app. Your Rust builds will be about ~30% faster.
================================================
FILE: Cargo.toml
================================================
[workspace]
resolver = "3"
members = ["bench", "crates/*", "packages/cli/binding"]
[workspace.metadata.cargo-shear]
ignored = [
# These workspace dependencies are used by rolldown crates, not our local crates
"css-module-lexer",
"html5gum",
"rolldown_filter_analyzer",
"rolldown_plugin_vite_asset",
"rolldown_plugin_vite_asset_import_meta_url",
"rolldown_plugin_vite_css",
"rolldown_plugin_vite_css_post",
"rolldown_plugin_vite_html",
"rolldown_plugin_vite_html_inline_proxy",
"string_cache",
]
[workspace.package]
authors = ["Vite+ Authors"]
edition = "2024"
homepage = "https://github.com/voidzero-dev/vite-plus"
license = "MIT"
repository = "https://github.com/voidzero-dev/vite-plus"
rust-version = "1.92.0"
[workspace.lints.rust]
absolute_paths_not_starting_with_crate = "warn"
non_ascii_idents = "warn"
unit-bindings = "warn"
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage)', 'cfg(coverage_nightly)'] }
unsafe_op_in_unsafe_fn = "warn"
unused_unsafe = "warn"
[workspace.lints.clippy]
all = { level = "warn", priority = -1 }
# restriction
dbg_macro = "warn"
todo = "warn"
unimplemented = "warn"
print_stdout = "warn"
print_stderr = "warn"
allow_attributes = "warn"
pedantic = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }
cargo = { level = "warn", priority = -1 }
cargo_common_metadata = "allow"
[workspace.dependencies]
anyhow = "1.0.98"
append-only-vec = "0.1.7"
arcstr = { version = "1.2.0", default-features = false }
ariadne = { package = "rolldown-ariadne", version = "0.5.3" }
ast-grep-config = "0.40.1"
ast-grep-core = "0.40.1"
ast-grep-language = { version = "0.40.1", default-features = false, features = [
"tree-sitter-bash",
"tree-sitter-typescript",
] }
async-channel = "2.3.1"
async-scoped = "0.9.0"
async-trait = "0.1.89"
backon = "1.3.0"
base-encode = "0.3.1"
base64-simd = "0.8.0"
bincode = "2.0.1"
bstr = { version = "1.12.0", default-features = false, features = ["alloc", "std"] }
bitflags = "2.9.1"
brush-parser = "0.3.0"
blake3 = "1.8.2"
chrono = { version = "0.4", features = ["serde"] }
clap = "4.5.40"
clap_complete = "4.6.0"
commondir = "1.0.0"
cow-utils = "0.1.3"
criterion = { version = "0.7", features = ["html_reports"] }
criterion2 = { version = "3.0.0", default-features = false }
crossterm = { version = "0.29.0", features = ["event-stream"] }
css-module-lexer = "0.0.15"
dashmap = "6.1.0"
derive_more = { version = "2.0.1", features = ["debug"] }
directories = "6.0.0"
dunce = "1.0.5"
fast-glob = "1.0.0"
flate2 = { version = "=1.1.9", features = ["zlib-rs"] }
form_urlencoded = "1.2.1"
fspy = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "69cc6eba95a3b7f25f7d4d32c3f29b1386995907" }
futures = "0.3.31"
futures-util = "0.3.31"
glob = "0.3.2"
heck = "0.5.0"
hex = "0.4.3"
html5gum = "0.8.1"
httpmock = "0.7"
ignore = "0.4"
indicatif = "0.18"
indexmap = "2.9.0"
indoc = "2.0.5"
infer = "0.19.0"
insta = "1.43.1"
itertools = "0.14.0"
itoa = "1.0.15"
json-escape-simd = "3"
json-strip-comments = "3"
jsonschema = { version = "0.45.0", default-features = false }
junction = "1.4.1"
memchr = "2.7.4"
mimalloc-safe = "0.1.52"
mime = "0.3.17"
napi = { version = "3.0.0", default-features = false, features = [
"async",
"error_anyhow",
"anyhow",
"tracing",
"object_indexmap",
] }
napi-build = "2"
napi-derive = { version = "3.0.0", default-features = false, features = [
"type-def",
"strict",
"tracing",
] }
nix = { version = "0.30.1", features = ["dir"] }
nodejs-built-in-modules = "1.0.0"
nom = "8.0.0"
num-bigint = "0.4.6"
num-format = "0.4"
num_cpus = "1.17"
owo-colors = "4.2.2"
parking_lot = "0.12.5"
pathdiff = "0.2.3"
pnp = "0.12.7"
percent-encoding = "2.3.1"
petgraph = "0.8.2"
pretty_assertions = "1.4.1"
phf = "0.13.0"
rayon = "1.10.0"
regex = "1.11.1"
regress = "0.11.0"
reqwest = { version = "0.12", default-features = false }
rolldown-notify = "10.2.0"
rolldown-notify-debouncer-full = "0.7.5"
ropey = "1.6.1"
rusqlite = { version = "0.37.0", features = ["bundled"] }
rustc-hash = "2.1.1"
schemars = "1.0.0"
self_cell = "1.2.0"
node-semver = "2.2.0"
semver = "1.0.26"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"
serde_yaml = "0.9.34"
serde_yml = "0.0.12"
serial_test = "3.2.0"
sha1 = "0.10.6"
sha2 = "0.10.9"
simdutf8 = "0.1.5"
smallvec = "1.15.0"
string_cache = "0.9.0"
sugar_path = { version = "2.0.1", features = ["cached_current_dir"] }
tar = "0.4.43"
tempfile = "3.14.0"
terminal_size = "0.4.2"
test-log = { version = "0.2.18", features = ["trace"] }
testing_macros = "1.0.0"
thiserror = "2"
tokio = { version = "1.48.0", default-features = false }
tracing = "0.1.41"
tracing-chrome = "0.7.2"
tracing-subscriber = { version = "0.3.19", default-features = false, features = [
"env-filter",
"fmt",
"json",
"serde",
"std",
] }
ts-rs = "12.0"
typedmap = "0.6.0"
url = "2.5.4"
urlencoding = "2.1.3"
uuid = "1.17.0"
vfs = "0.13.0"
vite_command = { path = "crates/vite_command" }
vite_error = { path = "crates/vite_error" }
vite_js_runtime = { path = "crates/vite_js_runtime" }
vite_glob = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "69cc6eba95a3b7f25f7d4d32c3f29b1386995907" }
vite_install = { path = "crates/vite_install" }
vite_migration = { path = "crates/vite_migration" }
vite_shared = { path = "crates/vite_shared" }
vite_static_config = { path = "crates/vite_static_config" }
vite_path = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "69cc6eba95a3b7f25f7d4d32c3f29b1386995907" }
vite_str = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "69cc6eba95a3b7f25f7d4d32c3f29b1386995907" }
vite_task = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "69cc6eba95a3b7f25f7d4d32c3f29b1386995907" }
vite_workspace = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "69cc6eba95a3b7f25f7d4d32c3f29b1386995907" }
walkdir = "2.5.0"
wax = "0.6.0"
which = "8.0.0"
xxhash-rust = "0.8.15"
zip = "7.2"
# oxc crates with the same version
oxc = { version = "0.121.0", features = [
"ast_visit",
"transformer",
"minifier",
"mangler",
"semantic",
"codegen",
"serialize",
"isolated_declarations",
"regular_expression",
"cfg",
] }
oxc_allocator = { version = "0.121.0", features = ["pool"] }
oxc_ast = "0.121.0"
oxc_ecmascript = "0.121.0"
oxc_parser = "0.121.0"
oxc_span = "0.121.0"
oxc_napi = "0.121.0"
oxc_minify_napi = "0.121.0"
oxc_parser_napi = "0.121.0"
oxc_transform_napi = "0.121.0"
oxc_traverse = "0.121.0"
# oxc crates in their own repos
oxc_index = { version = "4", features = ["rayon", "serde"] }
oxc_resolver = { version = "11.19.1", features = ["yarn_pnp"] }
oxc_resolver_napi = { version = "11.19.1", default-features = false, features = ["yarn_pnp"] }
oxc_sourcemap = "6"
# rolldown crates
rolldown = { path = "./rolldown/crates/rolldown" }
rolldown_binding = { path = "./rolldown/crates/rolldown_binding" }
rolldown_common = { path = "./rolldown/crates/rolldown_common" }
rolldown_dev = { path = "./rolldown/crates/rolldown_dev" }
rolldown_dev_common = { path = "./rolldown/crates/rolldown_dev_common" }
rolldown_devtools = { path = "./rolldown/crates/rolldown_devtools" }
rolldown_devtools_action = { path = "./rolldown/crates/rolldown_devtools_action" }
rolldown_ecmascript = { path = "./rolldown/crates/rolldown_ecmascript" }
rolldown_ecmascript_utils = { path = "./rolldown/crates/rolldown_ecmascript_utils" }
rolldown_error = { path = "./rolldown/crates/rolldown_error" }
rolldown_filter_analyzer = { path = "./rolldown/crates/rolldown_filter_analyzer" }
rolldown_fs = { path = "./rolldown/crates/rolldown_fs" }
rolldown_fs_watcher = { path = "./rolldown/crates/rolldown_fs_watcher" }
rolldown_plugin = { path = "./rolldown/crates/rolldown_plugin" }
rolldown_plugin_asset_module = { path = "./rolldown/crates/rolldown_plugin_asset_module" }
rolldown_plugin_bundle_analyzer = { path = "./rolldown/crates/rolldown_plugin_bundle_analyzer" }
rolldown_plugin_chunk_import_map = { path = "./rolldown/crates/rolldown_plugin_chunk_import_map" }
rolldown_plugin_copy_module = { path = "./rolldown/crates/rolldown_plugin_copy_module" }
rolldown_plugin_data_url = { path = "./rolldown/crates/rolldown_plugin_data_url" }
rolldown_plugin_esm_external_require = { path = "./rolldown/crates/rolldown_plugin_esm_external_require" }
rolldown_plugin_hmr = { path = "./rolldown/crates/rolldown_plugin_hmr" }
rolldown_plugin_isolated_declaration = { path = "./rolldown/crates/rolldown_plugin_isolated_declaration" }
rolldown_plugin_lazy_compilation = { path = "./rolldown/crates/rolldown_plugin_lazy_compilation" }
rolldown_plugin_oxc_runtime = { path = "./rolldown/crates/rolldown_plugin_oxc_runtime" }
rolldown_plugin_replace = { path = "./rolldown/crates/rolldown_plugin_replace" }
rolldown_plugin_utils = { path = "./rolldown/crates/rolldown_plugin_utils" }
rolldown_plugin_vite_alias = { path = "./rolldown/crates/rolldown_plugin_vite_alias" }
rolldown_plugin_vite_asset = { path = "./rolldown/crates/rolldown_plugin_vite_asset" }
rolldown_plugin_vite_asset_import_meta_url = { path = "./rolldown/crates/rolldown_plugin_vite_asset_import_meta_url" }
rolldown_plugin_vite_build_import_analysis = { path = "./rolldown/crates/rolldown_plugin_vite_build_import_analysis" }
rolldown_plugin_vite_css = { path = "./rolldown/crates/rolldown_plugin_vite_css" }
rolldown_plugin_vite_css_post = { path = "./rolldown/crates/rolldown_plugin_vite_css_post" }
rolldown_plugin_vite_dynamic_import_vars = { path = "./rolldown/crates/rolldown_plugin_vite_dynamic_import_vars" }
rolldown_plugin_vite_html = { path = "./rolldown/crates/rolldown_plugin_vite_html" }
rolldown_plugin_vite_html_inline_proxy = { path = "./rolldown/crates/rolldown_plugin_vite_html_inline_proxy" }
rolldown_plugin_vite_import_glob = { path = "./rolldown/crates/rolldown_plugin_vite_import_glob" }
rolldown_plugin_vite_json = { path = "./rolldown/crates/rolldown_plugin_vite_json" }
rolldown_plugin_vite_load_fallback = { path = "./rolldown/crates/rolldown_plugin_vite_load_fallback" }
rolldown_plugin_vite_manifest = { path = "./rolldown/crates/rolldown_plugin_vite_manifest" }
rolldown_plugin_vite_module_preload_polyfill = { path = "./rolldown/crates/rolldown_plugin_vite_module_preload_polyfill" }
rolldown_plugin_vite_react_refresh_wrapper = { path = "./rolldown/crates/rolldown_plugin_vite_react_refresh_wrapper" }
rolldown_plugin_vite_reporter = { path = "./rolldown/crates/rolldown_plugin_vite_reporter" }
rolldown_plugin_vite_resolve = { path = "./rolldown/crates/rolldown_plugin_vite_resolve" }
rolldown_plugin_vite_transform = { path = "./rolldown/crates/rolldown_plugin_vite_transform" }
rolldown_plugin_vite_wasm_fallback = { path = "./rolldown/crates/rolldown_plugin_vite_wasm_fallback" }
rolldown_plugin_vite_web_worker_post = { path = "./rolldown/crates/rolldown_plugin_vite_web_worker_post" }
rolldown_resolver = { path = "./rolldown/crates/rolldown_resolver" }
rolldown_sourcemap = { path = "./rolldown/crates/rolldown_sourcemap" }
rolldown_std_utils = { path = "./rolldown/crates/rolldown_std_utils" }
rolldown_testing = { path = "./rolldown/crates/rolldown_testing" }
rolldown_testing_config = { path = "./rolldown/crates/rolldown_testing_config" }
rolldown_tracing = { path = "./rolldown/crates/rolldown_tracing" }
rolldown_utils = { path = "./rolldown/crates/rolldown_utils" }
rolldown_watcher = { path = "./rolldown/crates/rolldown_watcher" }
rolldown_workspace = { path = "./rolldown/crates/rolldown_workspace" }
string_wizard = { path = "./rolldown/crates/string_wizard", features = ["serde"] }
# =============================================================================
# Local Development Patches
# =============================================================================
# This section patches vite-task crates to use local paths for simultaneous
# vite-task and vite-plus development. When making changes to vite-task that
# affect vite-plus, this allows testing without publishing or pushing commits.
#
# To use: Ensure vite-task is cloned at ../vite-task relative to vite-plus.
# Comment out this section before committing.
# =============================================================================
# [patch."https://github.com/voidzero-dev/vite-task.git"]
# fspy = { path = "../vite-task/crates/fspy" }
# vite_glob = { path = "../vite-task/crates/vite_glob" }
# vite_path = { path = "../vite-task/crates/vite_path" }
# vite_str = { path = "../vite-task/crates/vite_str" }
# vite_task = { path = "../vite-task/crates/vite_task" }
# vite_workspace = { path = "../vite-task/crates/vite_workspace" }
[profile.dev]
# Disabling debug info speeds up local and CI builds,
# and we don't rely on it for debugging that much.
debug = false
[profile.release]
# Configurations explicitly listed here for clarity.
# Using the best options for performance.
opt-level = 3
lto = "fat"
codegen-units = 1
strip = "symbols" # set to `false` for debug information
debug = false # set to `true` for debug information
panic = "abort" # Let it crash and force ourselves to write safe Rust.
# The trampoline binary is copied per shim tool (~5-10 copies), so optimize for
# size instead of speed. This reduces it from ~200KB to ~100KB on Windows.
[profile.release.package.vite_trampoline]
opt-level = "z"
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2026-present, VoidZero Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
**The Unified Toolchain for the Web**
_runtime and package management, create, dev, check, test, build, pack, and monorepo task caching in a single dependency_
---
Vite+ is the unified entry point for local web development. It combines [Vite](https://vite.dev/), [Vitest](https://vitest.dev/), [Oxlint](https://oxc.rs/docs/guide/usage/linter.html), [Oxfmt](https://oxc.rs/docs/guide/usage/formatter.html), [Rolldown](https://rolldown.rs/), [tsdown](https://tsdown.dev/), and [Vite Task](https://github.com/voidzero-dev/vite-task) into one zero-config toolchain that also manages runtime and package manager workflows:
- **`vp env`:** Manage Node.js globally and per project
- **`vp install`:** Install dependencies with automatic package manager detection
- **`vp dev`:** Run Vite's fast native ESM dev server with instant HMR
- **`vp check`:** Run formatting, linting, and type checks in one command
- **`vp test`:** Run tests through bundled Vitest
- **`vp build`:** Build applications for production with Vite + Rolldown
- **`vp run`:** Execute monorepo tasks with caching and dependency-aware scheduling
- **`vp pack`:** Build libraries for npm publishing or standalone app binaries
- **`vp create` / `vp migrate`:** Scaffold new projects and migrate existing ones
All of this is configured from your project root and works across Vite's framework ecosystem.
Vite+ is fully open-source under the MIT license.
## Getting Started
Install Vite+ globally as `vp`:
For Linux or macOS:
```bash
curl -fsSL https://vite.plus | bash
```
For Windows:
```bash
irm https://viteplus.dev/install.ps1 | iex
```
`vp` handles the full development lifecycle such as package management, development servers, linting, formatting, testing and building for production.
## Configuring Vite+
Vite+ can be configured using a single `vite.config.ts` at the root of your project:
```ts
import { defineConfig } from 'vite-plus';
export default defineConfig({
// Standard Vite configuration for dev/build/preview.
plugins: [],
// Vitest configuration.
test: {
include: ['src/**/*.test.ts'],
},
// Oxlint configuration.
lint: {
ignorePatterns: ['dist/**'],
},
// Oxfmt configuration.
fmt: {
semi: true,
singleQuote: true,
},
// Vite Task configuration.
run: {
tasks: {
'generate:icons': {
command: 'node scripts/generate-icons.js',
envs: ['ICON_THEME'],
},
},
},
// `vp staged` configuration.
staged: {
'*': 'vp check --fix',
},
});
```
This lets you keep the configuration for your development server, build, test, lint, format, task runner, and staged-file workflow in one place with type-safe config and shared defaults.
Use `vp migrate` to migrate to Vite+. It merges tool-specific config files such as `.oxlintrc*`, `.oxfmtrc*`, and lint-staged config into `vite.config.ts`.
### CLI Workflows (`vp help`)
#### Start
- **create** - Create a new project from a template
- **migrate** - Migrate an existing project to Vite+
- **config** - Configure hooks and agent integration
- **staged** - Run linters on staged files
- **install** (`i`) - Install dependencies
- **env** - Manage Node.js versions
#### Develop
- **dev** - Run the development server
- **check** - Run format, lint, and type checks
- **lint** - Lint code
- **fmt** - Format code
- **test** - Run tests
#### Execute
- **run** - Run monorepo tasks
- **exec** - Execute a command from local `node_modules/.bin`
- **dlx** - Execute a package binary without installing it as a dependency
- **cache** - Manage the task cache
#### Build
- **build** - Build for production
- **pack** - Build libraries
- **preview** - Preview production build
#### Manage Dependencies
Vite+ automatically wraps your package manager (pnpm, npm, or Yarn) based on `packageManager` and lockfiles:
- **add** - Add packages to dependencies
- **remove** (`rm`, `un`, `uninstall`) - Remove packages from dependencies
- **update** (`up`) - Update packages to latest versions
- **dedupe** - Deduplicate dependencies
- **outdated** - Check outdated packages
- **list** (`ls`) - List installed packages
- **why** (`explain`) - Show why a package is installed
- **info** (`view`, `show`) - View package metadata from the registry
- **link** (`ln`) / **unlink** - Manage local package links
- **pm** - Forward a command to the package manager
#### Maintain
- **upgrade** - Update `vp` itself to the latest version
- **implode** - Remove `vp` and all related data
### Scaffolding your first Vite+ project
Use `vp create` to create a new project:
```bash
vp create
```
You can run `vp create` inside of a project to add new apps or libraries to your project.
### Migrating an existing project
You can migrate an existing project to Vite+:
```bash
vp migrate
```
### GitHub Actions
Use the official [`setup-vp`](https://github.com/voidzero-dev/setup-vp) action to install Vite+ in GitHub Actions:
```yaml
- uses: voidzero-dev/setup-vp@v1
with:
node-version: '22'
cache: true
```
#### Manual Installation & Migration
If you are manually migrating a project to Vite+, install these dev dependencies first:
```bash
npm install -D vite-plus @voidzero-dev/vite-plus-core@latest
```
You need to add overrides to your package manager for `vite` and `vitest` so that other packages depending on Vite and Vitest will use the Vite+ versions:
```json
"overrides": {
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
}
```
If you are using `pnpm`, add this to your `pnpm-workspace.yaml`:
```yaml
overrides:
vite: npm:@voidzero-dev/vite-plus-core@latest
vitest: npm:@voidzero-dev/vite-plus-test@latest
```
Or, if you are using Yarn:
```json
"resolutions": {
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
}
```
## Sponsors
Thanks to [namespace.so](https://namespace.so) for powering our CI/CD pipelines with fast, free macOS and Linux runners.
================================================
FILE: bench/.gitignore
================================================
fixtures/monorepo
================================================
FILE: bench/Cargo.toml
================================================
[package]
name = "vite-plus-benches"
version = "0.1.0"
edition = "2024"
[dev-dependencies]
anyhow = { workspace = true }
async-trait = { workspace = true }
criterion = { workspace = true }
rustc-hash = { workspace = true }
tokio = { workspace = true, features = ["rt"] }
vite_path = { workspace = true }
vite_str = { workspace = true }
vite_task = { workspace = true }
[[bench]]
name = "workspace_load"
harness = false
================================================
FILE: bench/benches/workspace_load.rs
================================================
use std::{ffi::OsStr, hint::black_box, path::PathBuf, sync::Arc};
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use rustc_hash::FxHashMap;
use tokio::runtime::Runtime;
use vite_path::{AbsolutePath, AbsolutePathBuf};
use vite_str::Str;
use vite_task::{
CommandHandler, HandledCommand, Session, SessionConfig, plan_request::ScriptCommand,
};
/// A no-op command handler for benchmarking purposes.
#[derive(Debug, Default)]
struct NoOpCommandHandler;
#[async_trait::async_trait(?Send)]
impl CommandHandler for NoOpCommandHandler {
async fn handle_command(
&mut self,
_command: &mut ScriptCommand,
) -> anyhow::Result {
Ok(HandledCommand::Verbatim)
}
}
/// A no-op user config loader for benchmarking.
#[derive(Debug, Default)]
struct NoOpUserConfigLoader;
#[async_trait::async_trait(?Send)]
impl vite_task::loader::UserConfigLoader for NoOpUserConfigLoader {
async fn load_user_config_file(
&self,
_package_path: &AbsolutePath,
) -> anyhow::Result