From 2cd5fd75380f4d6ce59f041cb7375df64a5e61e1 Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 15:30:12 +0800 Subject: [PATCH 01/15] ci: add e2e tests for `vp create` across all templates and package managers Test all 3 built-in templates (monorepo, application, library) with all 4 package managers (pnpm, npm, yarn, bun) using the same tgz-packaging approach as ecosystem-ci. Each test verifies project structure, lockfiles, local package installation, and successful builds. --- .github/workflows/test-vp-create.yml | 245 +++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 .github/workflows/test-vp-create.yml diff --git a/.github/workflows/test-vp-create.yml b/.github/workflows/test-vp-create.yml new file mode 100644 index 0000000000..0e64e424a7 --- /dev/null +++ b/.github/workflows/test-vp-create.yml @@ -0,0 +1,245 @@ +name: Test vp create + +permissions: {} + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - 'packages/cli/src/create/**' + - 'packages/cli/templates/**' + - 'packages/cli/src/migration/**' + - '.github/workflows/test-vp-create.yml' + 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: filter + with: + filters: | + related-files: + - 'packages/cli/src/create/**' + - 'packages/cli/templates/**' + - 'packages/cli/src/migration/**' + - .github/workflows/test-vp-create.yml + + download-previous-rolldown-binaries: + needs: detect-changes + runs-on: ubuntu-latest + # Run if: not a PR, OR PR has 'test: create-e2e' label, OR create-related files changed + if: >- + github.event_name != 'pull_request' || + contains(github.event.pull_request.labels.*.name, 'test: create-e2e') || + needs.detect-changes.outputs.related-files-changed == 'true' + permissions: + contents: read + packages: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: ./.github/actions/download-rolldown-binaries + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + build: + name: Build vite-plus packages + runs-on: ubuntu-latest + permissions: + contents: read + packages: read + needs: + - download-previous-rolldown-binaries + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: ./.github/actions/clone + + - uses: oxc-project/setup-rust@23f38cfb0c04af97a055f76acee94d5be71c7c82 # v1.0.16 + with: + save-cache: ${{ github.ref_name == 'main' }} + cache-key: create-e2e-build + + - uses: oxc-project/setup-node@4c26e7cb3605b6bdef5450dacd02c434b10fd8ba # v1.2.0 + + - 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: 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 test jobs + cp target/x86_64-unknown-linux-gnu/release/vp tmp/tgz/vp + ls -la tmp/tgz + + - name: Upload tgz artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: vite-plus-packages + path: tmp/tgz/ + retention-days: 1 + + test-vp-create: + name: vp create ${{ matrix.template.name }} (${{ matrix.package-manager }}) + runs-on: ubuntu-latest + permissions: + contents: read + needs: + - build + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + template: + - name: monorepo + create-args: vite:monorepo --directory test-project + verify-command: vp run ready + - name: application + create-args: vite:application --directory test-project -- --template vanilla-ts + verify-command: vp run build + - name: library + create-args: vite:library --directory test-project + verify-command: vp run build + package-manager: + - pnpm + - npm + - yarn + - bun + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version: 24 + + - name: Download vite-plus packages + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: vite-plus-packages + path: tmp/tgz + + - name: Install vp CLI + run: | + mkdir -p target/release + cp tmp/tgz/vp target/release/vp + chmod +x target/release/vp + node $GITHUB_WORKSPACE/packages/tools/src/install-global-cli.ts --tgz $GITHUB_WORKSPACE/tmp/tgz/vite-plus-0.0.0.tgz + echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH + + - name: Verify vp installation + run: | + which vp + vp --version + + - name: Run vp create ${{ matrix.template.name }} with ${{ matrix.package-manager }} + working-directory: ${{ runner.temp }} + env: + VP_OVERRIDE_PACKAGES: '{"vite":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz","vitest":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz","@voidzero-dev/vite-plus-core":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz","@voidzero-dev/vite-plus-test":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz"}' + VP_VERSION: 'file:${{ github.workspace }}/tmp/tgz/vite-plus-0.0.0.tgz' + run: | + vp create ${{ matrix.template.create-args }} \ + --no-interactive \ + --no-agent \ + --package-manager ${{ matrix.package-manager }} + + - name: Verify project structure + working-directory: ${{ runner.temp }}/test-project + run: | + # package.json must exist + test -f package.json + echo "✓ package.json exists" + cat package.json + + # Check correct lockfile exists + case "${{ matrix.package-manager }}" in + pnpm) + test -f pnpm-lock.yaml + echo "✓ pnpm-lock.yaml exists" + ;; + npm) + test -f package-lock.json + echo "✓ package-lock.json exists" + ;; + yarn) + test -f yarn.lock + echo "✓ yarn.lock exists" + ;; + bun) + if [ -f bun.lock ]; then + echo "✓ bun.lock exists" + elif [ -f bun.lockb ]; then + echo "✓ bun.lockb exists" + else + echo "✗ No bun lockfile found" + exit 1 + fi + ;; + esac + + # node_modules must exist (vp install ran successfully) + test -d node_modules + echo "✓ node_modules exists" + + # Monorepo-specific checks + if [ "${{ matrix.template.name }}" = "monorepo" ]; then + test -d apps/website + echo "✓ apps/website exists" + test -d packages/utils + echo "✓ packages/utils exists" + + case "${{ matrix.package-manager }}" in + pnpm) + test -f pnpm-workspace.yaml + echo "✓ pnpm-workspace.yaml exists" + ;; + yarn) + test -f .yarnrc.yml + echo "✓ .yarnrc.yml exists" + ;; + esac + fi + + - name: Verify local tgz packages installed + working-directory: ${{ runner.temp }}/test-project + run: | + node -e " + const path = require('path'); + const pkg = require(path.resolve('node_modules/vite-plus/package.json')); + if (pkg.version !== '0.0.0') { + console.error('Expected vite-plus@0.0.0, got ' + pkg.version); + process.exit(1); + } + console.log('✓ vite-plus@' + pkg.version + ' installed correctly'); + " + + - name: Verify project builds + working-directory: ${{ runner.temp }}/test-project + run: ${{ matrix.template.verify-command }} From 2692c877abeaa552d3bf5bf7e460c2426e42760b Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 15:40:08 +0800 Subject: [PATCH 02/15] fix(ci): fix vp create e2e arg ordering and env var scope Split template-args from create-args so --package-manager is placed before the -- separator. Move VP_OVERRIDE_PACKAGES/VP_VERSION to job-level env. Add ls -la for debugging lockfile issues. --- .github/workflows/test-vp-create.yml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-vp-create.yml b/.github/workflows/test-vp-create.yml index 0e64e424a7..2e3143cad8 100644 --- a/.github/workflows/test-vp-create.yml +++ b/.github/workflows/test-vp-create.yml @@ -121,18 +121,24 @@ jobs: template: - name: monorepo create-args: vite:monorepo --directory test-project + template-args: '' verify-command: vp run ready - name: application - create-args: vite:application --directory test-project -- --template vanilla-ts + create-args: vite:application --directory test-project + template-args: '-- --template vanilla-ts' verify-command: vp run build - name: library create-args: vite:library --directory test-project + template-args: '' verify-command: vp run build package-manager: - pnpm - npm - yarn - bun + env: + VP_OVERRIDE_PACKAGES: '{"vite":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz","vitest":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz","@voidzero-dev/vite-plus-core":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz","@voidzero-dev/vite-plus-test":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz"}' + VP_VERSION: 'file:${{ github.workspace }}/tmp/tgz/vite-plus-0.0.0.tgz' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -161,14 +167,12 @@ jobs: - name: Run vp create ${{ matrix.template.name }} with ${{ matrix.package-manager }} working-directory: ${{ runner.temp }} - env: - VP_OVERRIDE_PACKAGES: '{"vite":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz","vitest":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz","@voidzero-dev/vite-plus-core":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz","@voidzero-dev/vite-plus-test":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz"}' - VP_VERSION: 'file:${{ github.workspace }}/tmp/tgz/vite-plus-0.0.0.tgz' run: | vp create ${{ matrix.template.create-args }} \ --no-interactive \ --no-agent \ - --package-manager ${{ matrix.package-manager }} + --package-manager ${{ matrix.package-manager }} \ + ${{ matrix.template.template-args }} - name: Verify project structure working-directory: ${{ runner.temp }}/test-project @@ -178,6 +182,10 @@ jobs: echo "✓ package.json exists" cat package.json + # List all files for debugging + echo "--- Project root files ---" + ls -la + # Check correct lockfile exists case "${{ matrix.package-manager }}" in pnpm) From a2e8ba894327a7a07b13755926357ce582f80136 Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 17:01:47 +0800 Subject: [PATCH 03/15] ci(create-e2e): add vp check step and vp run test for library --- .github/workflows/test-vp-create.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-vp-create.yml b/.github/workflows/test-vp-create.yml index 2e3143cad8..76c556ae5f 100644 --- a/.github/workflows/test-vp-create.yml +++ b/.github/workflows/test-vp-create.yml @@ -130,7 +130,9 @@ jobs: - name: library create-args: vite:library --directory test-project template-args: '' - verify-command: vp run build + verify-command: | + vp run build + vp run test package-manager: - pnpm - npm @@ -248,6 +250,10 @@ jobs: console.log('✓ vite-plus@' + pkg.version + ' installed correctly'); " + - name: Run vp check + working-directory: ${{ runner.temp }}/test-project + run: vp check + - name: Verify project builds working-directory: ${{ runner.temp }}/test-project run: ${{ matrix.template.verify-command }} From 8ffa42718b83e0d649e924fad08aef8f2c3255f8 Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 17:04:26 +0800 Subject: [PATCH 04/15] ci(create-e2e): fix library override, exclude broken yarn/bun combos - Add VP_FORCE_MIGRATE=1 so the library template's existing vite-plus dep gets overridden with the local tgz (fixes all 4 library tests) - Exclude yarn (all templates): yarn install fails with absolute file: paths to tgz files - Exclude bun monorepo: migration writes empty catalog with catalog: references when using file: protocol --- .github/workflows/test-vp-create.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/test-vp-create.yml b/.github/workflows/test-vp-create.yml index 76c556ae5f..9d407fbb75 100644 --- a/.github/workflows/test-vp-create.yml +++ b/.github/workflows/test-vp-create.yml @@ -138,9 +138,29 @@ jobs: - npm - yarn - bun + exclude: + # FIXME: yarn install fails with absolute file: paths to tgz + # https://github.com/voidzero-dev/vite-plus/issues/XXX + - package-manager: yarn + template: + name: monorepo + - package-manager: yarn + template: + name: application + - package-manager: yarn + template: + name: library + # FIXME: bun monorepo migration writes empty catalog with catalog: references + # https://github.com/voidzero-dev/vite-plus/issues/XXX + - package-manager: bun + template: + name: monorepo env: VP_OVERRIDE_PACKAGES: '{"vite":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz","vitest":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz","@voidzero-dev/vite-plus-core":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz","@voidzero-dev/vite-plus-test":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz"}' VP_VERSION: 'file:${{ github.workspace }}/tmp/tgz/vite-plus-0.0.0.tgz' + # Force full dependency rewriting so the library template's existing + # vite-plus dep gets overridden with the local tgz + VP_FORCE_MIGRATE: '1' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 From 2f889e93b34561561cc464e1c5dd196176121464 Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 17:08:48 +0800 Subject: [PATCH 05/15] fix(migration): use file: paths directly in bun overrides instead of empty catalog When VP_OVERRIDE_PACKAGES uses file: protocol paths (e.g., in CI with local tgz packages), rewriteBunCatalog() was unconditionally setting all overrides to 'catalog:' while skipping file: entries from the catalog itself, resulting in unresolvable 'catalog:' references pointing to an empty catalog. Now file: paths are used directly in overrides, matching npm behavior. --- .github/workflows/test-vp-create.yml | 5 -- .../bun-catalog-file-protocol.spec.ts | 89 +++++++++++++++++++ packages/cli/src/migration/migrator.ts | 4 +- 3 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 packages/cli/src/migration/__tests__/bun-catalog-file-protocol.spec.ts diff --git a/.github/workflows/test-vp-create.yml b/.github/workflows/test-vp-create.yml index 9d407fbb75..06e7c34b4f 100644 --- a/.github/workflows/test-vp-create.yml +++ b/.github/workflows/test-vp-create.yml @@ -150,11 +150,6 @@ jobs: - package-manager: yarn template: name: library - # FIXME: bun monorepo migration writes empty catalog with catalog: references - # https://github.com/voidzero-dev/vite-plus/issues/XXX - - package-manager: bun - template: - name: monorepo env: VP_OVERRIDE_PACKAGES: '{"vite":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz","vitest":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz","@voidzero-dev/vite-plus-core":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz","@voidzero-dev/vite-plus-test":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz"}' VP_VERSION: 'file:${{ github.workspace }}/tmp/tgz/vite-plus-0.0.0.tgz' diff --git a/packages/cli/src/migration/__tests__/bun-catalog-file-protocol.spec.ts b/packages/cli/src/migration/__tests__/bun-catalog-file-protocol.spec.ts new file mode 100644 index 0000000000..75c5926c1a --- /dev/null +++ b/packages/cli/src/migration/__tests__/bun-catalog-file-protocol.spec.ts @@ -0,0 +1,89 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; + +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { PackageManager, type WorkspaceInfo } from '../../types/index.js'; + +// Mock with file: protocol paths (simulating VP_OVERRIDE_PACKAGES in CI) +vi.mock('../../utils/constants.js', async (importOriginal) => { + const mod = await importOriginal(); + return { + ...mod, + VITE_PLUS_VERSION: 'file:/tmp/tgz/vite-plus-0.0.0.tgz', + VITE_PLUS_OVERRIDE_PACKAGES: { + vite: 'file:/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz', + vitest: 'file:/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz', + '@voidzero-dev/vite-plus-core': 'file:/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz', + '@voidzero-dev/vite-plus-test': 'file:/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz', + }, + }; +}); + +const { rewriteMonorepo } = await import('../migrator.js'); + +function makeWorkspaceInfo(rootDir: string, packageManager: PackageManager): WorkspaceInfo { + return { + rootDir, + isMonorepo: false, + monorepoScope: '', + workspacePatterns: [], + parentDirs: [], + packageManager, + packageManagerVersion: '10.33.0', + downloadPackageManager: { + name: 'pnpm', + installDir: '/tmp', + binPrefix: '/tmp/bin', + packageName: 'pnpm', + version: '10.33.0', + }, + packages: [], + }; +} + +function readJson(filePath: string): Record { + return JSON.parse(fs.readFileSync(filePath, 'utf8')); +} + +describe('rewriteMonorepo bun catalog with file: protocol', () => { + let tmpDir: string; + + beforeEach(() => { + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vp-test-bun-file-')); + }); + + afterEach(() => { + fs.rmSync(tmpDir, { recursive: true, force: true }); + }); + + it('uses file: paths directly in overrides instead of catalog:', () => { + fs.writeFileSync( + path.join(tmpDir, 'package.json'), + JSON.stringify({ + name: 'bun-monorepo', + workspaces: ['packages/*'], + devDependencies: { vite: '^7.0.0' }, + packageManager: 'bun@1.3.11', + }), + ); + rewriteMonorepo(makeWorkspaceInfo(tmpDir, PackageManager.bun), true); + + const pkg = readJson(path.join(tmpDir, 'package.json')); + // catalog should not contain file: entries + const catalog = (pkg.catalog ?? {}) as Record; + expect(catalog.vite).toBeUndefined(); + expect(catalog.vitest).toBeUndefined(); + // overrides should use file: paths directly, not catalog: + const overrides = pkg.overrides as Record; + expect(overrides.vite).toBe('file:/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz'); + expect(overrides.vitest).toBe('file:/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz'); + expect(overrides['@voidzero-dev/vite-plus-core']).toBe( + 'file:/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz', + ); + expect(overrides['@voidzero-dev/vite-plus-test']).toBe( + 'file:/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz', + ); + }); +}); diff --git a/packages/cli/src/migration/migrator.ts b/packages/cli/src/migration/migrator.ts index dc74f8b20c..feb8aafd34 100644 --- a/packages/cli/src/migration/migrator.ts +++ b/packages/cli/src/migration/migrator.ts @@ -1193,8 +1193,8 @@ function rewriteBunCatalog(projectPath: string): void { // bun overrides support catalog: references const overrides: Record = { ...pkg.overrides }; - for (const key of Object.keys(VITE_PLUS_OVERRIDE_PACKAGES)) { - overrides[key] = 'catalog:'; + for (const [key, value] of Object.entries(VITE_PLUS_OVERRIDE_PACKAGES)) { + overrides[key] = value.startsWith('file:') ? value : 'catalog:'; } pkg.overrides = overrides; From a938cf515547b1136c36502dba493603b101beca Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 17:18:45 +0800 Subject: [PATCH 06/15] fix(cli): fix yarn install failure in vp create Two issues caused yarn install to fail in CI for newly created projects: 1. In CI (CI=true), yarn Berry defaults to enableImmutableInstalls which prevents creating a new lockfile. Pass --no-frozen-lockfile to vp install since vp create always needs to generate a fresh lockfile. 2. Standalone yarn projects had no .yarnrc.yml created during migration. Without nodeLinker: node-modules, yarn 4 defaults to PnP which doesn't work with vite-plus. Now rewriteYarnrcYml() ensures nodeLinker is set and is called for standalone projects too. Also removes all yarn exclusions from the create e2e test workflow (full 12-job matrix now enabled). --- .github/workflows/test-vp-create.yml | 12 ------------ packages/cli/src/create/bin.ts | 22 ++++++++++++++++------ packages/cli/src/migration/migrator.ts | 9 +++++++++ 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test-vp-create.yml b/.github/workflows/test-vp-create.yml index 06e7c34b4f..a68f76223c 100644 --- a/.github/workflows/test-vp-create.yml +++ b/.github/workflows/test-vp-create.yml @@ -138,18 +138,6 @@ jobs: - npm - yarn - bun - exclude: - # FIXME: yarn install fails with absolute file: paths to tgz - # https://github.com/voidzero-dev/vite-plus/issues/XXX - - package-manager: yarn - template: - name: monorepo - - package-manager: yarn - template: - name: application - - package-manager: yarn - template: - name: library env: VP_OVERRIDE_PACKAGES: '{"vite":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz","vitest":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz","@voidzero-dev/vite-plus-core":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz","@voidzero-dev/vite-plus-test":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-test-0.0.0.tgz"}' VP_VERSION: 'file:${{ github.workspace }}/tmp/tgz/vite-plus-0.0.0.tgz' diff --git a/packages/cli/src/create/bin.ts b/packages/cli/src/create/bin.ts index 9ce1b3d460..1342e036ae 100644 --- a/packages/cli/src/create/bin.ts +++ b/packages/cli/src/create/bin.ts @@ -804,9 +804,14 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h installGitHooks(fullPath, compactOutput); } updateCreateProgress('Installing dependencies'); - const installSummary = await runViteInstall(fullPath, options.interactive, undefined, { - silent: compactOutput, - }); + const installSummary = await runViteInstall( + fullPath, + options.interactive, + ['--no-frozen-lockfile'], + { + silent: compactOutput, + }, + ); updateCreateProgress('Formatting code'); await runViteFmt(fullPath, options.interactive, undefined, { silent: compactOutput }); clearCreateProgress(); @@ -955,9 +960,14 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h updateWorkspaceConfig(projectDir, workspaceInfo); updateCreateProgress('Installing dependencies'); - installSummary = await runViteInstall(workspaceInfo.rootDir, options.interactive, undefined, { - silent: compactOutput, - }); + installSummary = await runViteInstall( + workspaceInfo.rootDir, + options.interactive, + ['--no-frozen-lockfile'], + { + silent: compactOutput, + }, + ); updateCreateProgress('Formatting code'); await runViteFmt(workspaceInfo.rootDir, options.interactive, [projectDir], { silent: compactOutput, diff --git a/packages/cli/src/migration/migrator.ts b/packages/cli/src/migration/migrator.ts index feb8aafd34..fdb33bd6d6 100644 --- a/packages/cli/src/migration/migrator.ts +++ b/packages/cli/src/migration/migrator.ts @@ -810,6 +810,11 @@ export function rewriteStandaloneProject( migratePnpmOverridesToWorkspaceYaml(projectPath, remainingPnpmOverrides); } + // Ensure .yarnrc.yml exists with nodeLinker for standalone yarn projects + if (packageManager === PackageManager.yarn) { + rewriteYarnrcYml(projectPath); + } + // Merge extracted staged config into vite.config.ts, then remove lint-staged from package.json if (extractedStagedConfig) { if (mergeStagedConfigToViteConfig(projectPath, extractedStagedConfig, silent, report)) { @@ -1116,6 +1121,10 @@ function rewriteYarnrcYml(projectPath: string): void { } editYamlFile(yarnrcYmlPath, (doc) => { + // Ensure nodeLinker is set to node-modules (yarn 4 defaults to PnP) + if (!doc.has('nodeLinker')) { + doc.set('nodeLinker', 'node-modules'); + } // catalog rewriteCatalog(doc); }); From 3690d5fd6b5c625ae5143f7f371bd7cdfe257c88 Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 17:23:33 +0800 Subject: [PATCH 07/15] fix(cli): revert --no-frozen-lockfile, set enableImmutableInstalls in .yarnrc.yml Revert passing --no-frozen-lockfile to vp install from vp create. Instead, set enableImmutableInstalls: false in .yarnrc.yml so yarn Berry can create/update lockfiles in CI where it defaults to immutable. --- packages/cli/src/create/bin.ts | 22 ++++++---------------- packages/cli/src/migration/migrator.ts | 5 +++++ 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/packages/cli/src/create/bin.ts b/packages/cli/src/create/bin.ts index 1342e036ae..9ce1b3d460 100644 --- a/packages/cli/src/create/bin.ts +++ b/packages/cli/src/create/bin.ts @@ -804,14 +804,9 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h installGitHooks(fullPath, compactOutput); } updateCreateProgress('Installing dependencies'); - const installSummary = await runViteInstall( - fullPath, - options.interactive, - ['--no-frozen-lockfile'], - { - silent: compactOutput, - }, - ); + const installSummary = await runViteInstall(fullPath, options.interactive, undefined, { + silent: compactOutput, + }); updateCreateProgress('Formatting code'); await runViteFmt(fullPath, options.interactive, undefined, { silent: compactOutput }); clearCreateProgress(); @@ -960,14 +955,9 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h updateWorkspaceConfig(projectDir, workspaceInfo); updateCreateProgress('Installing dependencies'); - installSummary = await runViteInstall( - workspaceInfo.rootDir, - options.interactive, - ['--no-frozen-lockfile'], - { - silent: compactOutput, - }, - ); + installSummary = await runViteInstall(workspaceInfo.rootDir, options.interactive, undefined, { + silent: compactOutput, + }); updateCreateProgress('Formatting code'); await runViteFmt(workspaceInfo.rootDir, options.interactive, [projectDir], { silent: compactOutput, diff --git a/packages/cli/src/migration/migrator.ts b/packages/cli/src/migration/migrator.ts index fdb33bd6d6..e9fdb69b49 100644 --- a/packages/cli/src/migration/migrator.ts +++ b/packages/cli/src/migration/migrator.ts @@ -1125,6 +1125,11 @@ function rewriteYarnrcYml(projectPath: string): void { if (!doc.has('nodeLinker')) { doc.set('nodeLinker', 'node-modules'); } + // Disable immutable installs so yarn can create/update lockfiles + // (yarn Berry defaults to enableImmutableInstalls: true in CI) + if (!doc.has('enableImmutableInstalls')) { + doc.set('enableImmutableInstalls', false); + } // catalog rewriteCatalog(doc); }); From c1e5ad7609055aa8cb703301db0c95febe399495 Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 17:28:38 +0800 Subject: [PATCH 08/15] refactor(cli): remove enableImmutableInstalls override from .yarnrc.yml Let yarn Berry use its default behavior to see what errors occur in CI with immutable installs enabled. --- packages/cli/src/migration/migrator.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/cli/src/migration/migrator.ts b/packages/cli/src/migration/migrator.ts index e9fdb69b49..fdb33bd6d6 100644 --- a/packages/cli/src/migration/migrator.ts +++ b/packages/cli/src/migration/migrator.ts @@ -1125,11 +1125,6 @@ function rewriteYarnrcYml(projectPath: string): void { if (!doc.has('nodeLinker')) { doc.set('nodeLinker', 'node-modules'); } - // Disable immutable installs so yarn can create/update lockfiles - // (yarn Berry defaults to enableImmutableInstalls: true in CI) - if (!doc.has('enableImmutableInstalls')) { - doc.set('enableImmutableInstalls', false); - } // catalog rewriteCatalog(doc); }); From 08724f1e4b58f0ce0e75a98480d874b2e1f80cc7 Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 20:13:55 +0800 Subject: [PATCH 09/15] fix(cli): pass --no-frozen-lockfile to vp install in CI Yarn Berry defaults to enableImmutableInstalls in CI, preventing lockfile creation. Pass --no-frozen-lockfile when CI env is set so vp create can generate fresh lockfiles. --- packages/cli/src/utils/prompts.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/utils/prompts.ts b/packages/cli/src/utils/prompts.ts index 3d6408dd53..0e5a17f7ad 100644 --- a/packages/cli/src/utils/prompts.ts +++ b/packages/cli/src/utils/prompts.ts @@ -73,9 +73,10 @@ export async function runViteInstall( const spinner = options?.silent ? getSilentSpinner() : getSpinner(interactive); const startTime = Date.now(); spinner.start(`Installing dependencies...`); + const ciArgs = process.env.CI ? ['--no-frozen-lockfile'] : []; const { exitCode, stderr, stdout } = await runCommandSilently({ command: process.env.VP_CLI_BIN ?? 'vp', - args: ['install', ...(extraArgs ?? [])], + args: ['install', ...ciArgs, ...(extraArgs ?? [])], cwd, envs: process.env, }); From 38801db98cf96c9dec0705a8f3002201c1213d52 Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 20:15:28 +0800 Subject: [PATCH 10/15] fix(cli): scope --no-frozen-lockfile to vp create only Pass --no-frozen-lockfile to vp install only from vp create in CI, so yarn Berry can create fresh lockfiles without affecting vp install behavior in other contexts. --- packages/cli/src/create/bin.ts | 5 +++-- packages/cli/src/utils/prompts.ts | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/create/bin.ts b/packages/cli/src/create/bin.ts index 9ce1b3d460..3cd3d5cb63 100644 --- a/packages/cli/src/create/bin.ts +++ b/packages/cli/src/create/bin.ts @@ -439,6 +439,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h let selectedParentDir: string | undefined; let remoteTargetDir: string | undefined; let shouldSetupHooks = false; + const installArgs = process.env.CI ? ['--no-frozen-lockfile'] : undefined; if (!selectedTemplateName) { const template = await prompts.select({ @@ -804,7 +805,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h installGitHooks(fullPath, compactOutput); } updateCreateProgress('Installing dependencies'); - const installSummary = await runViteInstall(fullPath, options.interactive, undefined, { + const installSummary = await runViteInstall(fullPath, options.interactive, installArgs, { silent: compactOutput, }); updateCreateProgress('Formatting code'); @@ -955,7 +956,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h updateWorkspaceConfig(projectDir, workspaceInfo); updateCreateProgress('Installing dependencies'); - installSummary = await runViteInstall(workspaceInfo.rootDir, options.interactive, undefined, { + installSummary = await runViteInstall(workspaceInfo.rootDir, options.interactive, installArgs, { silent: compactOutput, }); updateCreateProgress('Formatting code'); diff --git a/packages/cli/src/utils/prompts.ts b/packages/cli/src/utils/prompts.ts index 0e5a17f7ad..3d6408dd53 100644 --- a/packages/cli/src/utils/prompts.ts +++ b/packages/cli/src/utils/prompts.ts @@ -73,10 +73,9 @@ export async function runViteInstall( const spinner = options?.silent ? getSilentSpinner() : getSpinner(interactive); const startTime = Date.now(); spinner.start(`Installing dependencies...`); - const ciArgs = process.env.CI ? ['--no-frozen-lockfile'] : []; const { exitCode, stderr, stdout } = await runCommandSilently({ command: process.env.VP_CLI_BIN ?? 'vp', - args: ['install', ...ciArgs, ...(extraArgs ?? [])], + args: ['install', ...(extraArgs ?? [])], cwd, envs: process.env, }); From 35632dd6db77f4c7aebaadcfd680764632c1cffb Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 20:17:47 +0800 Subject: [PATCH 11/15] refactor: remove self-evident comments from migrator --- packages/cli/src/migration/migrator.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/cli/src/migration/migrator.ts b/packages/cli/src/migration/migrator.ts index fdb33bd6d6..2787d5bb7d 100644 --- a/packages/cli/src/migration/migrator.ts +++ b/packages/cli/src/migration/migrator.ts @@ -810,7 +810,6 @@ export function rewriteStandaloneProject( migratePnpmOverridesToWorkspaceYaml(projectPath, remainingPnpmOverrides); } - // Ensure .yarnrc.yml exists with nodeLinker for standalone yarn projects if (packageManager === PackageManager.yarn) { rewriteYarnrcYml(projectPath); } @@ -1121,7 +1120,6 @@ function rewriteYarnrcYml(projectPath: string): void { } editYamlFile(yarnrcYmlPath, (doc) => { - // Ensure nodeLinker is set to node-modules (yarn 4 defaults to PnP) if (!doc.has('nodeLinker')) { doc.set('nodeLinker', 'node-modules'); } From 738755cb743931f740126bccc09564b75d162828 Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 20:18:44 +0800 Subject: [PATCH 12/15] fix(test): use correct package manager in test helper --- .../cli/snap-tests-global/migration-monorepo-yarn4/snap.txt | 1 + .../migration/__tests__/bun-catalog-file-protocol.spec.ts | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/cli/snap-tests-global/migration-monorepo-yarn4/snap.txt b/packages/cli/snap-tests-global/migration-monorepo-yarn4/snap.txt index 408140e1b4..6c93f55ef4 100644 --- a/packages/cli/snap-tests-global/migration-monorepo-yarn4/snap.txt +++ b/packages/cli/snap-tests-global/migration-monorepo-yarn4/snap.txt @@ -65,6 +65,7 @@ export default defineConfig({ } > cat .yarnrc.yml # check .yarnrc.yml +nodeLinker: node-modules catalog: vite: npm:@voidzero-dev/vite-plus-core@latest vitest: npm:@voidzero-dev/vite-plus-test@latest diff --git a/packages/cli/src/migration/__tests__/bun-catalog-file-protocol.spec.ts b/packages/cli/src/migration/__tests__/bun-catalog-file-protocol.spec.ts index 75c5926c1a..1f66f21036 100644 --- a/packages/cli/src/migration/__tests__/bun-catalog-file-protocol.spec.ts +++ b/packages/cli/src/migration/__tests__/bun-catalog-file-protocol.spec.ts @@ -33,11 +33,11 @@ function makeWorkspaceInfo(rootDir: string, packageManager: PackageManager): Wor packageManager, packageManagerVersion: '10.33.0', downloadPackageManager: { - name: 'pnpm', + name: packageManager, installDir: '/tmp', binPrefix: '/tmp/bin', - packageName: 'pnpm', - version: '10.33.0', + packageName: packageManager, + version: '1.0.0', }, packages: [], }; From d67b5be436d5d0d6f8b643fa1d4667326b62df92 Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 20:27:00 +0800 Subject: [PATCH 13/15] ci(create-e2e): remove redundant vp installation check --- .github/workflows/test-vp-create.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/test-vp-create.yml b/.github/workflows/test-vp-create.yml index a68f76223c..72be3caf78 100644 --- a/.github/workflows/test-vp-create.yml +++ b/.github/workflows/test-vp-create.yml @@ -165,11 +165,6 @@ jobs: node $GITHUB_WORKSPACE/packages/tools/src/install-global-cli.ts --tgz $GITHUB_WORKSPACE/tmp/tgz/vite-plus-0.0.0.tgz echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH - - name: Verify vp installation - run: | - which vp - vp --version - - name: Run vp create ${{ matrix.template.name }} with ${{ matrix.package-manager }} working-directory: ${{ runner.temp }} run: | From 3fa25415b452520e7f4175a2cb3d2287990ad31e Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 20:28:15 +0800 Subject: [PATCH 14/15] Revert "ci(create-e2e): remove redundant vp installation check" This reverts commit d67b5be436d5d0d6f8b643fa1d4667326b62df92. --- .github/workflows/test-vp-create.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test-vp-create.yml b/.github/workflows/test-vp-create.yml index 72be3caf78..a68f76223c 100644 --- a/.github/workflows/test-vp-create.yml +++ b/.github/workflows/test-vp-create.yml @@ -165,6 +165,11 @@ jobs: node $GITHUB_WORKSPACE/packages/tools/src/install-global-cli.ts --tgz $GITHUB_WORKSPACE/tmp/tgz/vite-plus-0.0.0.tgz echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH + - name: Verify vp installation + run: | + which vp + vp --version + - name: Run vp create ${{ matrix.template.name }} with ${{ matrix.package-manager }} working-directory: ${{ runner.temp }} run: | From 7de2e6cb73f2ee74f054f71673a18788854ab226 Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 6 Apr 2026 21:03:50 +0800 Subject: [PATCH 15/15] ci(create-e2e): disable yarn hardened mode for lockfile creation Yarn Berry enables hardened mode on public PRs which blocks lockfile creation (YN0028). Set YARN_ENABLE_HARDENED_MODE=0 so vp create can generate fresh lockfiles in the e2e test. --- .github/workflows/test-vp-create.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test-vp-create.yml b/.github/workflows/test-vp-create.yml index a68f76223c..af5284c425 100644 --- a/.github/workflows/test-vp-create.yml +++ b/.github/workflows/test-vp-create.yml @@ -172,6 +172,9 @@ jobs: - name: Run vp create ${{ matrix.template.name }} with ${{ matrix.package-manager }} working-directory: ${{ runner.temp }} + env: + # Yarn Berry enables hardened mode on public PRs, blocking lockfile creation + YARN_ENABLE_HARDENED_MODE: '0' run: | vp create ${{ matrix.template.create-args }} \ --no-interactive \