diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_tsdown_extensions/verify-extensions.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_tsdown_extensions/verify-extensions.mjs index 93dbe6cdb1..18a073926f 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_tsdown_extensions/verify-extensions.mjs +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_tsdown_extensions/verify-extensions.mjs @@ -4,7 +4,7 @@ // bundled tsdown loaded them as external top-level packages, so `vp pack --exe` // (and CSS bundling) failed with `Failed to import module "@tsdown/exe"`. // -// They are now bundled into core, so this loads the bundled extension chunks +// They now ship in the CLI's private pack runtime, so this loads the bundled extension chunks // directly to prove they resolve `tsdown/internal` against the bundled tsdown. // `vp pack --exe` itself needs Node >= 25.7 (SEA), so it cannot run end-to-end // in CI; this check is Node-version independent. @@ -13,7 +13,8 @@ import path from 'node:path'; import { pathToFileURL } from 'node:url'; const require = createRequire(import.meta.url); -const packEntry = require.resolve('@voidzero-dev/vite-plus-core/pack'); +const cliRequire = createRequire(require.resolve('vite-plus/package.json')); +const packEntry = cliRequire.resolve('#pack'); const tsdownDir = path.dirname(packEntry); for (const chunk of ['tsdown-exe.js', 'tsdown-css.js']) { diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_global.md index 209b74891e..072392f6ad 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_global.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_global.md @@ -8,6 +8,10 @@ The global flag reads the manifest paired with the global binary. Vite+ toolchain (global) vite-plus@ +├── bundles tsdown@ +├── bundles rolldown@ +│ ├── compiles oxc@ +│ └── compiles oxc-resolver@ ├── depends on @voidzero-dev/vite-plus-core@ │ ├── bundles vite@ │ │ └── uses rolldown@ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_local.md index a983347b5a..b5b2a533c9 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_local.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_full_local.md @@ -8,6 +8,10 @@ The local CLI reads the manifest shipped in its vite-plus package. Vite+ toolchain (local) vite-plus@ +├── bundles tsdown@ +├── bundles rolldown@ +│ ├── compiles oxc@ +│ └── compiles oxc-resolver@ ├── depends on @voidzero-dev/vite-plus-core@ │ ├── bundles vite@ │ │ └── uses rolldown@ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_old_local_routing.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_old_local_routing.md index b98a86b6cb..9ca44f0877 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_old_local_routing.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_toolchain/snapshots/toolchain_old_local_routing.md @@ -37,6 +37,10 @@ a package without a runnable local CLI uses the global toolchain Vite+ toolchain (global) vite-plus@ +├── bundles tsdown@ +├── bundles rolldown@ +│ ├── compiles oxc@ +│ └── compiles oxc-resolver@ ├── depends on @voidzero-dev/vite-plus-core@ │ ├── bundles vite@ │ │ └── uses rolldown@ @@ -74,6 +78,10 @@ Run `vp toolchain vite` to show these versions and relationships. Vite+ toolchain (global) vite-plus@ +├── bundles tsdown@ +├── bundles rolldown@ +│ ├── compiles oxc@ +│ └── compiles oxc-resolver@ ├── depends on @voidzero-dev/vite-plus-core@ │ ├── bundles vite@ │ │ └── uses rolldown@ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_command_root/snapshots/core_command_root_dispatch.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_command_root/snapshots/core_command_root_dispatch.global.md index 3661ad7796..a5f4ff6a4b 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_command_root/snapshots/core_command_root_dispatch.global.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_command_root/snapshots/core_command_root_dispatch.global.md @@ -137,10 +137,12 @@ error: Failed to resolve vite command: GenericFailure, Expected @voidzero-dev/vi ## `vp -C . pack packages/app/src/index.ts` -**Exit code:** 1 - ``` VITE+ - The Unified Toolchain for the Web -error: Failed to resolve pack command: GenericFailure, Expected @voidzero-dev/vite-plus-core@, but found vite@8.2.2 at /node_modules/.pnpm/vite@8.2.2/node_modules/vite/package.json. Run `vp migrate` to align the Vite alias, then run `vp install`. +ℹ entry: packages/app/src/index.ts +ℹ Build start +ℹ dist/index.mjs kB │ gzip: kB +ℹ 1 files, total: kB +✔ Build complete in ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_command_root/snapshots/core_command_root_dispatch.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_command_root/snapshots/core_command_root_dispatch.local.md index 7ec2ac9b22..8d8ca9766e 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_command_root/snapshots/core_command_root_dispatch.local.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_command_root/snapshots/core_command_root_dispatch.local.md @@ -119,8 +119,10 @@ error: Failed to resolve vite command: GenericFailure, Expected @voidzero-dev/vi ## `vp -C . pack packages/app/src/index.ts` -**Exit code:** 1 - ``` -error: Failed to resolve pack command: GenericFailure, Expected @voidzero-dev/vite-plus-core@, but found vite@8.2.2 at /node_modules/.pnpm/vite@8.2.2/node_modules/vite/package.json. Run `vp migrate` to align the Vite alias, then run `vp install`. +ℹ entry: packages/app/src/index.ts +ℹ Build start +ℹ dist/index.mjs kB │ gzip: kB +ℹ 1 files, total: kB +✔ Build complete in ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/check-bundled.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/check-bundled.mjs index 119a23c051..eb9bae87c8 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/check-bundled.mjs +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/check-bundled.mjs @@ -7,6 +7,6 @@ import { build } from 'vite-plus/pack'; const require = createRequire(import.meta.url); const cliRequire = createRequire(require.resolve('vite-plus/package.json')); assert.equal(createServer, cliRequire('vite').createServer); -assert.equal(build, cliRequire('vite/pack').build); +assert.equal(build, cliRequire('#pack').build); assert.equal(require('./package.json').devDependencies.vite, undefined); console.log('Bundled APIs resolve without a project Vite dependency'); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/check-tagged-alias.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/check-tagged-alias.mjs index 40e971754c..a94187ed8d 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/check-tagged-alias.mjs +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/check-tagged-alias.mjs @@ -7,18 +7,28 @@ const cliRequire = createRequire(require.resolve('vite-plus/package.json')); const project = require('./package.json'); assert.equal(project.overrides, undefined); assert.equal(project.devDependencies.vite, 'npm:@voidzero-dev/vite-plus-core@latest'); -// npm 11.11 accepts a peer's upstream Vite as satisfying the tagged alias. -assert.equal(require('vite/package.json').name, 'vite'); +// npm may satisfy a tagged alias with an upstream Vite peer. Check the +// command against the installed package instead of depending on that choice. +const projectVite = require('vite/package.json'); assert.equal(cliRequire('vite/package.json').name, '@voidzero-dev/vite-plus-core'); -assert.notEqual(require.resolve('vite/package.json'), cliRequire.resolve('vite/package.json')); - -for (const args of [['dev'], ['pack', 'entry.js']]) { - const result = spawnSync('vp', args, { encoding: 'utf8', timeout: 30_000 }); - assert.ifError(result.error); - const output = result.stdout + result.stderr; - assert.equal(result.status, 1, output); +const dev = spawnSync('vp', ['dev'], { encoding: 'utf8', timeout: 30_000 }); +assert.ifError(dev.error); +const output = dev.stdout + dev.stderr; +if (projectVite.name === 'vite') { + assert.notEqual(require.resolve('vite/package.json'), cliRequire.resolve('vite/package.json')); + assert.equal(dev.status, 1, output); assert.match(output, /Expected @voidzero-dev\/vite-plus-core@/); assert.match(output, /but found vite@/); assert.match(output, /Run `vp migrate` to align the Vite alias, then run `vp install`\./); - console.log(`vp ${args[0]} rejects the replaced npm alias and reports the repair command`); +} else { + assert.equal(projectVite.name, '@voidzero-dev/vite-plus-core'); + assert.equal(projectVite.version, cliRequire('vite-plus/package.json').version); + assert.equal(dev.status, 0, output); + assert.match(output, /SSR environment identity and HTTP response passed/); } +console.log('vp dev validates the installed package behind the tagged npm alias'); + +const pack = spawnSync('vp', ['pack', 'entry.js'], { encoding: 'utf8', timeout: 30_000 }); +assert.ifError(pack.error); +assert.equal(pack.status, 0, pack.stdout + pack.stderr); +console.log('vp pack uses its private runtime with the tagged npm alias'); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/snapshots.toml index d0be4f3f72..c497b7b185 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/snapshots.toml @@ -15,9 +15,7 @@ local-registry = true comment = "CI can stamp the package version after building the CLI. The resolver must compare installed manifests rather than an inlined build-time version." steps = [ { argv = ["vp", "install", "--ignore-scripts"], snapshot = false }, - { argv = ["vpt", "json-edit", "node_modules/vite-plus/package.json", "version", "0.0.0"], snapshot = false }, - { argv = ["vpt", "json-edit", "node_modules/vite-plus/package.json", "dependencies.vite", "npm:@voidzero-dev/vite-plus-core@0.0.0"], snapshot = false }, - { argv = ["vpt", "json-edit", "node_modules/vite/package.json", "version", "0.0.0"], snapshot = false }, + { argv = ["node", "stamp-version.mjs"], snapshot = false }, ["node", "check-api.mjs"], ["vp", "dev"], ["vp", "build"], @@ -156,7 +154,7 @@ steps = [ ] [[case]] -name = "core_module_identity_npm_tagged_alias_rejected" +name = "core_module_identity_npm_tagged_alias" vp = "global" local-registry = true steps = [ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/snapshots/core_module_identity_npm_tagged_alias_rejected.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/snapshots/core_module_identity_npm_tagged_alias.md similarity index 61% rename from crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/snapshots/core_module_identity_npm_tagged_alias_rejected.md rename to crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/snapshots/core_module_identity_npm_tagged_alias.md index 8f3b5c664c..4e8860318c 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/snapshots/core_module_identity_npm_tagged_alias_rejected.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/snapshots/core_module_identity_npm_tagged_alias.md @@ -1,4 +1,4 @@ -# core_module_identity_npm_tagged_alias_rejected +# core_module_identity_npm_tagged_alias ## `vpt write-file package.json '{"name":"core-module-identity","private":true,"type":"module","packageManager":"npm@11.11.0","devDependencies":{"vite-plus":"latest","vite":"npm:@voidzero-dev/vite-plus-core@latest"}} '` @@ -10,6 +10,6 @@ ## `node check-tagged-alias.mjs` ``` -vp dev rejects the replaced npm alias and reports the repair command -vp pack rejects the replaced npm alias and reports the repair command +vp dev validates the installed package behind the tagged npm alias +vp pack uses its private runtime with the tagged npm alias ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/snapshots/core_module_identity_repackaged_version.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/snapshots/core_module_identity_repackaged_version.md index 214d0ec8a8..d3863906d7 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/snapshots/core_module_identity_repackaged_version.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/snapshots/core_module_identity_repackaged_version.md @@ -5,13 +5,7 @@ CI can stamp the package version after building the CLI. The resolver must compa ## `vp install --ignore-scripts` -## `vpt json-edit node_modules/vite-plus/package.json version 0.0.0` - - -## `vpt json-edit node_modules/vite-plus/package.json dependencies.vite npm:@voidzero-dev/vite-plus-core@0.0.0` - - -## `vpt json-edit node_modules/vite/package.json version 0.0.0` +## `node stamp-version.mjs` ## `node check-api.mjs` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/stamp-version.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/stamp-version.mjs new file mode 100644 index 0000000000..ebc130ce31 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_module_identity/stamp-version.mjs @@ -0,0 +1,18 @@ +import { readFileSync, writeFileSync } from 'node:fs'; +import { createRequire } from 'node:module'; + +const require = createRequire(import.meta.url); +const cliManifest = require.resolve('vite-plus/package.json'); +const cliRequire = createRequire(cliManifest); +for (const manifest of new Set([ + cliManifest, + require.resolve('vite/package.json'), + cliRequire.resolve('vite/package.json'), +])) { + const pkg = JSON.parse(readFileSync(manifest, 'utf8')); + pkg.version = '0.0.0'; + if (pkg.name === 'vite-plus') { + pkg.dependencies.vite = 'npm:@voidzero-dev/vite-plus-core@0.0.0'; + } + writeFileSync(manifest, JSON.stringify(pkg)); +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/index.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/index.ts new file mode 100644 index 0000000000..46d3ca8c61 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/index.ts @@ -0,0 +1 @@ +export const value = 42; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/snapshots.toml index 09f24b2b7e..f59accfcaa 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/snapshots.toml @@ -4,7 +4,7 @@ vp = "local" steps = [ { argv = ["vpt", "write-file", "node_modules/vite/package.json", "{\"name\":\"vite\",\"version\":\"8.2.2\"}\n"], snapshot = false }, { argv = ["vp", "dev"], continue-on-failure = true }, - ["vp", "pack", "index.ts"], + ["vp", "pack", "--no-config", "index.ts"], ] [[case]] @@ -13,5 +13,5 @@ vp = "local" steps = [ { argv = ["vpt", "write-file", "node_modules/vite/package.json", "{\"name\":\"@voidzero-dev/vite-plus-core\",\"version\":\"0.0.0-stale\"}\n"], snapshot = false }, { argv = ["vp", "dev"], continue-on-failure = true }, - ["vp", "pack", "index.ts"], + ["vp", "pack", "--no-config", "index.ts"], ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/snapshots/core_resolution_stale_core.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/snapshots/core_resolution_stale_core.md index 80cf509b19..dc85995c0b 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/snapshots/core_resolution_stale_core.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/snapshots/core_resolution_stale_core.md @@ -12,10 +12,12 @@ error: Failed to resolve vite command: GenericFailure, Expected @voidzero-dev/vite-plus-core@, but found @voidzero-dev/vite-plus-core@ at /node_modules/vite/package.json. Run `vp migrate` to align the Vite alias, then run `vp install`. ``` -## `vp pack index.ts` - -**Exit code:** 1 +## `vp pack --no-config index.ts` ``` -error: Failed to resolve pack command: GenericFailure, Expected @voidzero-dev/vite-plus-core@, but found @voidzero-dev/vite-plus-core@ at /node_modules/vite/package.json. Run `vp migrate` to align the Vite alias, then run `vp install`. +ℹ entry: index.ts +ℹ Build start +ℹ dist/index.mjs kB │ gzip: kB +ℹ 1 files, total: kB +✔ Build complete in ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/snapshots/core_resolution_upstream_vite.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/snapshots/core_resolution_upstream_vite.md index 57f4377cc1..250f464a7d 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/snapshots/core_resolution_upstream_vite.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/core_resolution_errors/snapshots/core_resolution_upstream_vite.md @@ -12,10 +12,12 @@ error: Failed to resolve vite command: GenericFailure, Expected @voidzero-dev/vite-plus-core@, but found vite@8.2.2 at /node_modules/vite/package.json. Run `vp migrate` to align the Vite alias, then run `vp install`. ``` -## `vp pack index.ts` - -**Exit code:** 1 +## `vp pack --no-config index.ts` ``` -error: Failed to resolve pack command: GenericFailure, Expected @voidzero-dev/vite-plus-core@, but found vite@8.2.2 at /node_modules/vite/package.json. Run `vp migrate` to align the Vite alias, then run `vp install`. +ℹ entry: index.ts +ℹ Build start +ℹ dist/index.mjs kB │ gzip: kB +ℹ 1 files, total: kB +✔ Build complete in ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/check-api.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/check-api.mjs new file mode 100644 index 0000000000..ee255728f9 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/check-api.mjs @@ -0,0 +1,35 @@ +import assert from 'node:assert/strict'; +import { createRequire } from 'node:module'; +import { pathToFileURL } from 'node:url'; +import * as vite from 'vite'; +import { ModuleRunner } from 'vite/module-runner'; +import * as vp from 'vite-plus'; +import { ModuleRunner as VpModuleRunner } from 'vite-plus/module-runner'; +import { build as pack } from 'vite-plus/pack'; + +const require = createRequire(import.meta.url); +const cliRequire = createRequire(require.resolve('vite-plus/package.json')); +const vitestRequire = createRequire(cliRequire.resolve('vitest/package.json')); +const cliPkg = cliRequire('vite-plus/package.json'); +assert.equal(cliPkg.dependencies['@voidzero-dev/vite-plus-core'], undefined); +assert.throws(() => cliRequire.resolve('vite/pack'), { code: 'ERR_PACKAGE_PATH_NOT_EXPORTED' }); +assert.equal(pack, (await import(pathToFileURL(cliRequire.resolve('#pack')).href)).build); +assert.equal(vite.version, '8.3.4'); +for (const name of [ + 'createServer', + 'build', + 'resolveConfig', + 'DevEnvironment', + 'isRunnableDevEnvironment', + 'isFetchableDevEnvironment', +]) { + assert.equal(vite[name], vp[name], name); + assert.equal(require('vite-plus')[name], vite[name], name); +} +assert.equal(ModuleRunner, VpModuleRunner); +assert.equal(cliRequire.resolve('vite'), require.resolve('vite')); +assert.equal(vitestRequire.resolve('vite'), require.resolve('vite')); +if (process.env.LOCAL_VITE === '1') { + assert.equal(vp.ecosystemCiMarker, 'local-vite'); +} +console.log('API exports and bundled Vitest use upstream Vite; pack uses its private bundled runtime'); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/check-vite-identity-guard.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/check-vite-identity-guard.mjs new file mode 100644 index 0000000000..bf7c0f7339 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/check-vite-identity-guard.mjs @@ -0,0 +1,73 @@ +import assert from 'node:assert/strict'; +import { spawnSync } from 'node:child_process'; +import { cpSync, existsSync, mkdirSync, mkdtempSync, renameSync, rmSync, writeFileSync } from 'node:fs'; +import { createRequire } from 'node:module'; +import { dirname, join } from 'node:path'; + +const require = createRequire(import.meta.url); +const cliRoot = dirname(require.resolve('vite-plus/package.json')); +const upstream = dirname(require.resolve('vite/package.json')); +// Also cover the default strict behavior with real upstream Vite installed. +for (const command of ['dev', 'build', 'preview']) { + const local = process.argv[2] === 'local'; + const args = local ? [join(cliRoot, 'bin/vp'), command] : [command]; + const result = spawnSync(local ? process.execPath : 'vp', args, { + encoding: 'utf8', timeout: 30_000, + env: { ...process.env, VP_USE_PROJECT_VITE: '0' }, + }); + assert.ifError(result.error); + assert.equal(result.status, 1, result.stdout + result.stderr); + assert.match(result.stdout + result.stderr, /but found vite@8\.3\.4/); +} +console.log('Upstream Vite rejected without the opt-in before dev/build/preview'); + +const cliModules = join(cliRoot, 'node_modules'); +const cliVite = join(cliModules, 'vite'); +const temporary = mkdtempSync(join(cliRoot, '.vite-identity-')); +const original = join(temporary, 'original-vite'); +mkdirSync(cliModules, { recursive: true }); +const hadVite = existsSync(cliVite); +if (hadVite) { + renameSync(cliVite, original); +} + +function checkRejected() { + for (const command of ['dev', 'build', 'preview']) { + const local = process.argv[2] === 'local'; + const args = local ? [join(cliRoot, 'bin/vp'), command] : [command]; + const result = spawnSync(local ? process.execPath : 'vp', args, { + encoding: 'utf8', + timeout: 30_000, + }); + assert.ifError(result.error); + const output = result.stdout + result.stderr; + assert.equal(result.status, 1, output); + assert.match(output, /requires the project and vite-plus to resolve the same Vite package/); + assert.match(output, /Apply the same override to all `vite` dependencies/); + } +} + +try { + // A CLI alias left on core must fail before its runtime is loaded. + mkdirSync(cliVite); + writeFileSync(join(cliVite, 'package.json'), JSON.stringify({ + name: '@voidzero-dev/vite-plus-core', + version: require('vite-plus/package.json').version, + exports: { './package.json': './package.json' }, + })); + checkRejected(); + console.log('Partial Vite override rejected before dev/build/preview'); + rmSync(cliVite, { recursive: true }); + + // A second physical copy has the same version and exports but different + // constructor identities. Version checks alone must not accept it. + cpSync(upstream, cliVite, { recursive: true }); + checkRejected(); + console.log('Separate Vite copies with the same version rejected before dev/build/preview'); +} finally { + rmSync(cliVite, { recursive: true, force: true }); + if (hadVite) { + renameSync(original, cliVite); + } + rmSync(temporary, { recursive: true, force: true }); +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/entry.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/entry.ts new file mode 100644 index 0000000000..0a477f2129 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/entry.ts @@ -0,0 +1,3 @@ +import './style.css'; + +export const message: string = 'Packed with the private runtime'; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/index.html b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/index.html new file mode 100644 index 0000000000..ca3177ab0d --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/index.html @@ -0,0 +1,2 @@ +

Upstream Vite

+ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/override.test.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/override.test.ts new file mode 100644 index 0000000000..aefed16058 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/override.test.ts @@ -0,0 +1,21 @@ +import { expect, test } from 'vite-plus/test'; +import * as vite from 'vite'; +import * as vp from 'vite-plus'; + +test('uses the replacement Vite through public API exports', async () => { + expect(vp.version).toBe('8.3.4'); + expect(vp.createServer).toBe(vite.createServer); + if (process.env.LOCAL_VITE === '1') { + expect(vp.ecosystemCiMarker).toBe('local-vite'); + } + const server = await vp.createServer({ + configFile: false, + server: { middlewareMode: true, ws: false }, + }); + try { + expect(vite.isRunnableDevEnvironment(server.environments.ssr)).toBe(true); + expect(vp.isRunnableDevEnvironment(server.environments.ssr)).toBe(true); + } finally { + await server.close(); + } +}); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/package.json new file mode 100644 index 0000000000..433633326a --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/package.json @@ -0,0 +1,13 @@ +{ + "name": "upstream-vite-override", + "private": true, + "type": "module", + "packageManager": "pnpm@11.24.0", + "devDependencies": { + "vite": "8.3.4", + "vite-plus": "latest" + }, + "scripts": { + "postinstall": "vp pack" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/pnpm-workspace.yaml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/pnpm-workspace.yaml new file mode 100644 index 0000000000..109d7f196e --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +minimumReleaseAge: 0 +overrides: + vite: 8.3.4 diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/snapshots.toml new file mode 100644 index 0000000000..0743a0c462 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/snapshots.toml @@ -0,0 +1,59 @@ +[[case]] +name = "upstream_vite_override" +vp = ["global"] +# Use pinned upstream Vite through the runner's registry to exercise its real +# CLI, API exports, and Vitest peer layout instead of a mock Vite package. +local-registry = true +comment = "A global Vite override replaces CLI/API/Vitest Vite, while postinstall pack uses its private bundled runtime. Repeat with a local file dependency and a different public CLI entry." +env = { VP_USE_PROJECT_VITE = "1" } +steps = [ + { argv = ["vp", "install"], snapshot = false }, + ["node", "check-api.mjs"], + ["node", "check-vite-identity-guard.mjs"], + ["vpt", "stat-file", "packed/entry.mjs", "--assert", "file"], + ["vpt", "stat-file", "packed/entry.d.mts", "--assert", "file"], + ["vpt", "stat-file", "packed/style.css", "--assert", "file"], + ["vp", "build"], + ["vp", "dev"], + ["vp", "preview"], + ["vp", "test", "run"], + { argv = ["node", "use-local-vite.mjs"], snapshot = false }, + { argv = ["vp", "install"], envs = [["EXPECT_PROJECT_CONFIG", "1"]], snapshot = false }, + { argv = ["node", "check-api.mjs"], envs = [["LOCAL_VITE", "1"]] }, + ["node", "check-vite-identity-guard.mjs"], + ["vp", "build"], + ["vp", "dev"], + ["vp", "preview"], + { argv = ["vp", "test", "run"], envs = [["LOCAL_VITE", "1"]] }, +] + +# The registry installation owns the runtime dependencies. Invoke its public +# local bin instead of the runner's bootstrap CLI, which has separate Vitest peers. +[[case]] +name = "upstream_vite_override" +vp = ["local"] +# Use pinned upstream Vite through the runner's registry to exercise its real +# CLI, API exports, and Vitest peer layout instead of a mock Vite package. +local-registry = true +comment = "A global Vite override replaces CLI/API/Vitest Vite, while postinstall pack uses its private bundled runtime. Repeat with a local file dependency and a different public CLI entry." +env = { VP_USE_PROJECT_VITE = "1" } +steps = [ + { argv = ["vp", "install"], snapshot = false }, + ["node", "check-api.mjs"], + ["node", "check-vite-identity-guard.mjs", "local"], + ["vpt", "stat-file", "packed/entry.mjs", "--assert", "file"], + ["vpt", "stat-file", "packed/entry.d.mts", "--assert", "file"], + ["vpt", "stat-file", "packed/style.css", "--assert", "file"], + ["node", "node_modules/vite-plus/bin/vp", "build"], + ["node", "node_modules/vite-plus/bin/vp", "dev"], + ["node", "node_modules/vite-plus/bin/vp", "preview"], + ["node", "node_modules/vite-plus/bin/vp", "test", "run"], + { argv = ["node", "use-local-vite.mjs"], snapshot = false }, + { argv = ["node", "node_modules/vite-plus/bin/vp", "install"], envs = [["EXPECT_PROJECT_CONFIG", "1"]], snapshot = false }, + { argv = ["node", "check-api.mjs"], envs = [["LOCAL_VITE", "1"]] }, + ["node", "check-vite-identity-guard.mjs", "local"], + ["node", "node_modules/vite-plus/bin/vp", "build"], + ["node", "node_modules/vite-plus/bin/vp", "dev"], + ["node", "node_modules/vite-plus/bin/vp", "preview"], + { argv = ["node", "node_modules/vite-plus/bin/vp", "test", "run"], envs = [["LOCAL_VITE", "1"]] }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/snapshots/upstream_vite_override.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/snapshots/upstream_vite_override.global.md new file mode 100644 index 0000000000..812fd462e1 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/upstream_vite_override/snapshots/upstream_vite_override.global.md @@ -0,0 +1,165 @@ +# upstream_vite_override + +A global Vite override replaces CLI/API/Vitest Vite, while postinstall pack uses its private bundled runtime. Repeat with a local file dependency and a different public CLI entry. + +## `vp install` + + +## `node check-api.mjs` + +``` +API exports and bundled Vitest use upstream Vite; pack uses its private bundled runtime +``` + +## `node check-vite-identity-guard.mjs` + +``` +Upstream Vite rejected without the opt-in before dev/build/preview +Partial Vite override rejected before dev/build/preview +Separate Vite copies with the same version rejected before dev/build/preview +``` + +## `vpt stat-file packed/entry.mjs --assert file` + +``` +packed/entry.mjs: file +``` + +## `vpt stat-file packed/entry.d.mts --assert file` + +``` +packed/entry.d.mts: file +``` + +## `vpt stat-file packed/style.css --assert file` + +``` +packed/style.css: file +``` + +## `vp build` + +``` +VITE+ - The Unified Toolchain for the Web + +✓ 5 modules transformed. +computing gzip size... +dist/index.html kB │ gzip: kB +dist/assets/index-.css kB │ gzip: kB +dist/assets/index-.js kB │ gzip: kB + +✓ built in +``` + +## `vp dev` + +``` + + VITE ready in + + ➜ Local: http://127.0.0.1:/ + ➜ press h + enter to show help +Upstream Vite dev served HTTP 200 +``` + +## `vp preview` + +``` +VITE+ - The Unified Toolchain for the Web + + ➜ Local: http://127.0.0.1:/ + ➜ press h + enter to show help +Upstream Vite preview served HTTP 200 +``` + +## `vp test run` + +``` +VITE+ - The Unified Toolchain for the Web + + RUN + + ✓ override.test.ts (1 test) + ✓ uses the replacement Vite through public API exports + + Test Files 1 passed (1) + Tests 1 passed (1) + Start at