diff --git a/.github/actions/build-upstream/action.yml b/.github/actions/build-upstream/action.yml index d992565e60..4b0342d406 100644 --- a/.github/actions/build-upstream/action.yml +++ b/.github/actions/build-upstream/action.yml @@ -59,25 +59,44 @@ runs: pnpm --filter "@voidzero-dev/*" build pnpm --filter vite-plus build-ts + # Install zig + cargo-zigbuild for musl cross-compilation (napi-cross only supports gnu) + - name: Add musl Rust target + if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') + shell: bash + run: rustup target add ${{ inputs.target }} + + - name: Setup zig (musl) + if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') + uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1 + with: + version: 0.15.2 + + - name: Install cargo-zigbuild (musl) + if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') + uses: taiki-e/install-action@f916cfac5d8efd040e250d0cd6b967616504b3a4 # v2.68.32 + with: + tool: cargo-zigbuild + # 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) + - name: Build NAPI bindings (Linux gnu) shell: bash - if: steps.cache-restore.outputs.cache-hit != 'true' && inputs.target == 'x86_64-unknown-linux-gnu' + if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'linux') && !contains(inputs.target, 'musl') run: | pnpm --filter=vite-plus build-native --target ${{ inputs.target }} --use-napi-cross env: TARGET_CC: clang + TARGET_CFLAGS: ${{ contains(inputs.target, 'aarch64') && '-D_BSD_SOURCE' || '' }} DEBUG: napi:* - - name: Build NAPI bindings (aarch64-linux) + - name: Build NAPI bindings (Linux musl) shell: bash - if: steps.cache-restore.outputs.cache-hit != 'true' && inputs.target == 'aarch64-unknown-linux-gnu' + if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') run: | - pnpm --filter=vite-plus build-native --target ${{ inputs.target }} --use-napi-cross + pnpm --filter=vite-plus build-native --target ${{ inputs.target }} -x env: TARGET_CC: clang - TARGET_CFLAGS: '-D_BSD_SOURCE' + TARGET_CFLAGS: ${{ contains(inputs.target, 'aarch64') && '-D_BSD_SOURCE' || '' }} DEBUG: napi:* - name: Build NAPI bindings (non-Linux targets) @@ -88,23 +107,24 @@ runs: 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' + - name: Build Rust CLI binary (Linux gnu) + if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'linux') && !contains(inputs.target, 'musl') shell: bash run: | pnpm exec napi build --use-napi-cross --target ${{ inputs.target }} --release -p vite_global_cli env: TARGET_CC: clang + TARGET_CFLAGS: ${{ contains(inputs.target, 'aarch64') && '-D_BSD_SOURCE' || '' }} DEBUG: napi:* - - name: Build Rust CLI binary (aarch64-linux) - if: steps.cache-restore.outputs.cache-hit != 'true' && inputs.target == 'aarch64-unknown-linux-gnu' + - name: Build Rust CLI binary (Linux musl) + if: steps.cache-restore.outputs.cache-hit != 'true' && contains(inputs.target, 'musl') shell: bash run: | - pnpm exec napi build --use-napi-cross --target ${{ inputs.target }} --release -p vite_global_cli + pnpm exec napi build -x --target ${{ inputs.target }} --release -p vite_global_cli env: TARGET_CC: clang - TARGET_CFLAGS: '-D_BSD_SOURCE' + TARGET_CFLAGS: ${{ contains(inputs.target, 'aarch64') && '-D_BSD_SOURCE' || '' }} DEBUG: napi:* - name: Build Rust CLI binary (non-Linux targets) diff --git a/.github/actions/download-rolldown-binaries/action.yml b/.github/actions/download-rolldown-binaries/action.yml index 5bc103d006..edc3afb847 100644 --- a/.github/actions/download-rolldown-binaries/action.yml +++ b/.github/actions/download-rolldown-binaries/action.yml @@ -20,6 +20,9 @@ runs: - name: Install previous release shell: bash run: | + # Always download the binding that matches the runner OS (not the + # Rust cross-compilation target). The rolldown binding is loaded by + # Node.js on the host to run JS build steps, not cross-compiled. if ${{ runner.os == 'Windows' }}; then export TARGET="win32-x64-msvc" elif ${{ runner.os == 'Linux' }}; then diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7f7f48962..ee581ecc48 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,9 +99,6 @@ jobs: 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 @@ -112,6 +109,43 @@ jobs: env: RUST_MIN_STACK: 8388608 + test-musl: + needs: detect-changes + if: needs.detect-changes.outputs.code-changed == 'true' + name: Test (Linux x64 musl) + runs-on: namespace-profile-linux-x64-default + container: + image: node:22-alpine3.21 + env: + # GitHub Actions sets HOME=/github/home in containers, but the euid home is /root. + # Pin Rust tooling paths to avoid $HOME mismatch issues. + CARGO_HOME: /root/.cargo + RUSTUP_HOME: /root/.rustup + steps: + - name: Install Alpine dependencies + shell: sh {0} + run: apk add --no-cache bash curl git musl-dev gcc g++ python3 cmake make + + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: ./.github/actions/clone + + - name: Install rustup + run: | + # GitHub Actions sets HOME=/github/home in containers, but rustup expects euid home (/root) + export HOME=/root + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain none + echo "/root/.cargo/bin" >> "$GITHUB_PATH" + + - name: Install Rust toolchain + run: rustup show + + # Test all crates/* packages. New crates are automatically included. + # Also test vite-plus-cli (lives outside crates/) to catch type sync issues. + # Skip separate cargo check — cargo test already compiles everything. + - 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' @@ -146,48 +180,6 @@ jobs: - 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: @@ -197,8 +189,11 @@ jobs: matrix: include: - os: namespace-profile-linux-x64-default + target: x86_64-unknown-linux-gnu - os: namespace-profile-mac-default + target: aarch64-apple-darwin - os: windows-latest + target: x86_64-pc-windows-msvc runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 @@ -217,7 +212,7 @@ jobs: - uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0 with: save-cache: ${{ github.ref_name == 'main' }} - cache-key: cli-e2e-test + cache-key: cli-e2e-test-${{ matrix.target }} target-dir: ${{ runner.os == 'Windows' && format('{0}/target', env.DEV_DRIVE) || '' }} - uses: oxc-project/setup-node@fdbf0dfd334c4e6d56ceeb77d91c76339c2a0885 # v1.0.4 @@ -234,10 +229,10 @@ jobs: - 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' }} + target: ${{ matrix.target }} - name: Check TypeScript types - if: ${{ matrix.os == 'namespace-profile-linux-x64-default' }} + if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} run: pnpm tsgo - name: Install Global CLI vp @@ -395,7 +390,7 @@ jobs: - name: Run CLI snapshot tests run: | RUST_BACKTRACE=1 pnpm test - if ! git diff --exit-code; then + if ! git diff --quiet; 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 @@ -577,6 +572,87 @@ jobs: pnpm bootstrap-cli:ci vp --version + cli-e2e-test-musl: + name: CLI E2E test (Linux x64 musl) + needs: + - download-previous-rolldown-binaries + 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: cli-e2e-test-musl + + - 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 + + # Cross-compile for musl on the glibc host + - name: Build with upstream (musl) + uses: ./.github/actions/build-upstream + with: + target: x86_64-unknown-linux-musl + + # Run bootstrap-cli:ci and E2E tests inside an Alpine container where musl is native. + # Can't run on the glibc host because NAPI .node files are musl-linked. + - name: Run E2E in Alpine container + run: | + docker run --rm \ + -e CI=true \ + -v "${{ github.workspace }}:/workspace" \ + -w /workspace \ + node:22-alpine3.21 sh -c " + apk add --no-cache bash curl ca-certificates git + + # Install pnpm and re-resolve optional dependencies for musl. + # The host node_modules has glibc bindings; pnpm store holds both + # but we need to re-link the musl variants. + corepack enable + pnpm install --frozen-lockfile --force + + # Download musl rolldown binding (host downloaded glibc variant) + ROLLDOWN_VERSION=\$(node -p \"require('./rolldown/packages/rolldown/package.json').version\") + npm pack \"@rolldown/binding-linux-x64-musl@\${ROLLDOWN_VERSION}\" + tar -xzf \"rolldown-binding-linux-x64-musl-\${ROLLDOWN_VERSION}.tgz\" + cp ./package/rolldown-binding.linux-x64-musl.node ./packages/core/dist/rolldown/shared/ + cp ./package/rolldown-binding.linux-x64-musl.node ./rolldown/packages/rolldown/dist/shared/ + rm -rf package *.tgz + + pnpm bootstrap-cli:ci + export PATH=\"/root/.vite-plus/bin:\$PATH\" + + vp --version + vp -h + vp env doctor + + # Verify shims work + which node + which npm + node --version + + # Test global package install + vp install -g typescript + tsc --version + vp uninstall -g typescript + + # Run snap tests + git config --global --add safe.directory /workspace + RUST_BACKTRACE=1 pnpm test + if ! git diff --quiet; 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 + " + install-e2e-test: name: Local CLI `vp install` E2E test needs: @@ -651,9 +727,10 @@ jobs: if: always() needs: - test + - test-musl - lint - - run - cli-e2e-test + - cli-e2e-test-musl steps: - run: exit 1 # Thank you, next https://github.com/vercel/next.js/blob/canary/.github/workflows/build_and_test.yml#L379 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 65c7fc207f..460a2175ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -63,8 +63,12 @@ jobs: os: macos-latest - target: aarch64-unknown-linux-gnu os: ubuntu-latest + - target: aarch64-unknown-linux-musl + os: ubuntu-latest - target: x86_64-unknown-linux-gnu os: ubuntu-latest + - target: x86_64-unknown-linux-musl + os: ubuntu-latest - target: x86_64-pc-windows-msvc os: windows-latest - target: aarch64-pc-windows-msvc diff --git a/.github/workflows/test-standalone-install.yml b/.github/workflows/test-standalone-install.yml index 29dda56e10..a8d529d410 100644 --- a/.github/workflows/test-standalone-install.yml +++ b/.github/workflows/test-standalone-install.yml @@ -108,7 +108,7 @@ jobs: run: | # --check queries npm registry and prints update status vp upgrade --check - vp upgrade 0.0.0-gbe8891a5.20260227-1615 + vp upgrade 0.1.14-alpha.1 vp --version # rollback to the previous version (should succeed after a real update) vp upgrade --rollback @@ -203,7 +203,7 @@ jobs: # Verify upgrade vp upgrade --check - vp upgrade 0.0.0-gbe8891a5.20260227-1615 + vp upgrade 0.1.14-alpha.1 vp --version vp upgrade --rollback vp --version @@ -213,6 +213,118 @@ jobs: # cd hello && vp run build " + test-install-sh-musl-x64: + name: Test install.sh (Linux x64 musl) + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - name: Run install.sh in Alpine container + run: | + docker run --rm \ + -v "${{ github.workspace }}:/workspace" \ + -e VITE_PLUS_VERSION=alpha \ + -e CI=true \ + alpine:3.21 sh -c " + # libstdc++ is needed by unofficial-builds Node.js musl binary + apk add --no-cache bash curl ca-certificates libstdc++ + cat /workspace/packages/cli/install.sh | bash + export PATH=\"\$HOME/.vite-plus/bin:\$PATH\" + + 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 + vp env run --node 24 -- node -p \"process.versions\" + + # Test create command + vp create vite --no-interactive --no-agent -- hello --no-interactive -t vanilla + cd hello && vp run build && vp --version + cd .. + + # Verify upgrade + vp upgrade --check + vp upgrade 0.1.14-alpha.1 + vp --version + vp upgrade --rollback + vp --version + " + + test-install-sh-musl-arm64: + name: Test install.sh (Linux ARM64 musl 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 Alpine container + run: | + docker run --rm --platform linux/arm64 \ + -v "${{ github.workspace }}:/workspace" \ + -e VITE_PLUS_VERSION=alpha \ + -e CI=true \ + alpine:3.21 sh -c " + # libstdc++ is needed by unofficial-builds Node.js musl binary + apk add --no-cache bash curl ca-certificates libstdc++ + cat /workspace/packages/cli/install.sh | bash + export PATH=\"\$HOME/.vite-plus/bin:\$PATH\" + + 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\" + + # FIXME: QEMU doesn't support all syscalls needed by rolldown/tsdown + # vp create vite --no-interactive --no-agent -- hello --no-interactive -t vanilla + # cd hello && vp run build && vp --version + + # Verify upgrade + vp upgrade --check + vp upgrade 0.1.14-alpha.1 + vp --version + vp upgrade --rollback + vp --version + " + test-install-ps1-v5: name: Test install.ps1 (Windows x64, PowerShell 5.1) runs-on: windows-latest @@ -290,7 +402,7 @@ jobs: shell: powershell run: | vp upgrade --check - vp upgrade 0.0.0-gbe8891a5.20260227-1615 + vp upgrade 0.1.14-alpha.1 vp --version vp upgrade --rollback vp --version @@ -357,7 +469,7 @@ jobs: shell: pwsh run: | vp upgrade --check - vp upgrade 0.0.0-gbe8891a5.20260227-1615 + vp upgrade 0.1.14-alpha.1 vp --version vp upgrade --rollback vp --version @@ -385,7 +497,7 @@ jobs: run: | # --check queries npm registry and prints update status vp upgrade --check - vp upgrade 0.0.0-gbe8891a5.20260227-1615 + vp upgrade 0.1.14-alpha.1 vp --version # rollback to the previous version (should succeed after a real update) vp upgrade --rollback diff --git a/crates/vite_global_cli/src/commands/upgrade/registry.rs b/crates/vite_global_cli/src/commands/upgrade/registry.rs index 170ac257c3..9fa08a1f27 100644 --- a/crates/vite_global_cli/src/commands/upgrade/registry.rs +++ b/crates/vite_global_cli/src/commands/upgrade/registry.rs @@ -99,7 +99,9 @@ mod tests { "darwin-arm64", "darwin-x64", "linux-arm64-gnu", + "linux-arm64-musl", "linux-x64-gnu", + "linux-x64-musl", "win32-arm64-msvc", "win32-x64-msvc", ]; @@ -124,10 +126,6 @@ mod tests { for suffix in &detection_suffixes { let package_name = format!("{PLATFORM_PACKAGE_SCOPE}/{CLI_PACKAGE_NAME_PREFIX}-{suffix}"); - // musl variants are not published, so skip them - if suffix.contains("musl") { - continue; - } assert!( published_packages.contains(&package_name), "Platform suffix '{suffix}' produces CLI package name '{package_name}' \ diff --git a/crates/vite_install/src/request.rs b/crates/vite_install/src/request.rs index b2cc23657c..9eb1a047c2 100644 --- a/crates/vite_install/src/request.rs +++ b/crates/vite_install/src/request.rs @@ -537,6 +537,7 @@ mod tests { } #[tokio::test] + #[ignore] // Flaky on musl/Alpine — temp file race condition async fn test_verify_file_hash_sha1() { use sha1::Sha1; use sha2::Digest; diff --git a/crates/vite_js_runtime/src/providers/node.rs b/crates/vite_js_runtime/src/providers/node.rs index 81a0561505..844a9172d0 100644 --- a/crates/vite_js_runtime/src/providers/node.rs +++ b/crates/vite_js_runtime/src/providers/node.rs @@ -16,8 +16,13 @@ use crate::{ }; /// Default Node.js distribution base URL +#[cfg(not(target_env = "musl"))] const DEFAULT_NODE_DIST_URL: &str = "https://nodejs.org/dist"; +/// Unofficial builds URL for musl (official nodejs.org only provides glibc binaries) +#[cfg(target_env = "musl")] +const DEFAULT_NODE_DIST_URL: &str = "https://unofficial-builds.nodejs.org/download/release"; + /// Environment variable to override the Node.js distribution URL /// Default cache TTL in seconds (1 hour) @@ -553,6 +558,12 @@ impl JsRuntimeProvider for NodeProvider { crate::platform::Arch::X64 => "x64", crate::platform::Arch::Arm64 => "arm64", }; + // On musl targets, append "-musl" to match unofficial-builds filename pattern + // e.g. "linux-x64-musl" instead of "linux-x64" + #[cfg(target_env = "musl")] + if platform.os == Os::Linux { + return vite_str::format!("{os}-{arch}-musl"); + } vite_str::format!("{os}-{arch}") } @@ -616,6 +627,7 @@ mod tests { fn test_platform_string() { let provider = NodeProvider::new(); + #[cfg(not(target_env = "musl"))] let cases = [ (Platform { os: Os::Linux, arch: Arch::X64 }, "linux-x64"), (Platform { os: Os::Linux, arch: Arch::Arm64 }, "linux-arm64"), @@ -624,6 +636,15 @@ mod tests { (Platform { os: Os::Windows, arch: Arch::X64 }, "win-x64"), (Platform { os: Os::Windows, arch: Arch::Arm64 }, "win-arm64"), ]; + #[cfg(target_env = "musl")] + let cases = [ + (Platform { os: Os::Linux, arch: Arch::X64 }, "linux-x64-musl"), + (Platform { os: Os::Linux, arch: Arch::Arm64 }, "linux-arm64-musl"), + (Platform { os: Os::Darwin, arch: Arch::X64 }, "darwin-x64"), + (Platform { os: Os::Darwin, arch: Arch::Arm64 }, "darwin-arm64"), + (Platform { os: Os::Windows, arch: Arch::X64 }, "win-x64"), + (Platform { os: Os::Windows, arch: Arch::Arm64 }, "win-arm64"), + ]; for (platform, expected) in cases { assert_eq!(provider.platform_string(platform), expected); @@ -637,19 +658,38 @@ mod tests { let info = provider.get_download_info("22.13.1", platform); - assert_eq!(info.archive_filename, "node-v22.13.1-linux-x64.tar.gz"); - assert_eq!( - info.archive_url, - "https://nodejs.org/dist/v22.13.1/node-v22.13.1-linux-x64.tar.gz" - ); - assert_eq!(info.archive_format, ArchiveFormat::TarGz); - assert_eq!(info.extracted_dir_name, "node-v22.13.1-linux-x64"); - - if let HashVerification::ShasumsFile { url } = &info.hash_verification { - assert_eq!(url, "https://nodejs.org/dist/v22.13.1/SHASUMS256.txt"); - } else { - panic!("Expected ShasumsFile verification"); + #[cfg(not(target_env = "musl"))] + { + assert_eq!(info.archive_filename, "node-v22.13.1-linux-x64.tar.gz"); + assert_eq!( + info.archive_url, + "https://nodejs.org/dist/v22.13.1/node-v22.13.1-linux-x64.tar.gz" + ); + assert_eq!(info.extracted_dir_name, "node-v22.13.1-linux-x64"); + if let HashVerification::ShasumsFile { url } = &info.hash_verification { + assert_eq!(url, "https://nodejs.org/dist/v22.13.1/SHASUMS256.txt"); + } else { + panic!("Expected ShasumsFile verification"); + } + } + #[cfg(target_env = "musl")] + { + assert_eq!(info.archive_filename, "node-v22.13.1-linux-x64-musl.tar.gz"); + assert_eq!( + info.archive_url, + "https://unofficial-builds.nodejs.org/download/release/v22.13.1/node-v22.13.1-linux-x64-musl.tar.gz" + ); + assert_eq!(info.extracted_dir_name, "node-v22.13.1-linux-x64-musl"); + if let HashVerification::ShasumsFile { url } = &info.hash_verification { + assert_eq!( + url, + "https://unofficial-builds.nodejs.org/download/release/v22.13.1/SHASUMS256.txt" + ); + } else { + panic!("Expected ShasumsFile verification"); + } } + assert_eq!(info.archive_format, ArchiveFormat::TarGz); } #[test] @@ -724,7 +764,7 @@ fedcba987654 node-v22.13.1-win-x64.zip"; #[test] fn test_get_dist_url_default() { vite_shared::EnvConfig::test_scope(vite_shared::EnvConfig::for_test(), || { - assert_eq!(get_dist_url(), "https://nodejs.org/dist"); + assert_eq!(get_dist_url(), DEFAULT_NODE_DIST_URL); }); } diff --git a/docs/guide/index.md b/docs/guide/index.md index d2568d0847..6a066f216b 100644 --- a/docs/guide/index.md +++ b/docs/guide/index.md @@ -28,6 +28,35 @@ vp help Vite+ will manage your global Node.js runtime and package manager. If you'd like to opt out of this behavior, run `vp env off`. If you realize Vite+ is not for you, type `vp implode`, but please [share your feedback with us](https://discord.gg/cAnsqHh5PX). ::: +::: details Using a minor platform (CPU architecture, OS) ? + +Prebuilt binaries are distributed for the following platforms (grouped by [Node.js v24 platform support tier](https://github.com/nodejs/node/blob/v24.x/BUILDING.md#platform-list)): + +- Tier 1 + - Linux x64 glibc (`x86_64-unknown-linux-gnu`) + - Linux arm64 glibc (`aarch64-unknown-linux-gnu`) + - Windows x64 (`x86_64-pc-windows-msvc`) + - macOS x64 (`x86_64-apple-darwin`) + - macOS arm64 (`aarch64-apple-darwin`) +- Tier 2 + - Windows arm64 (`aarch64-pc-windows-msvc`) +- Experimental + - Linux x64 musl (`x86_64-unknown-linux-musl`) +- Other + - Linux arm64 musl (`aarch64-unknown-linux-musl`) + +If a prebuilt binary is not available for your platform, installation will fail with an error. + +On Alpine Linux (musl), you need to install `libstdc++` before using Vite+: + +```sh +apk add libstdc++ +``` + +This is required because the managed [unofficial-builds](https://unofficial-builds.nodejs.org/) Node.js runtime depends on the GNU C++ standard library. + +::: + ## Quick Start Create a project, install dependencies, and use the default commands: diff --git a/packages/cli/install.sh b/packages/cli/install.sh index fdd6150227..ecfd17df67 100644 --- a/packages/cli/install.sh +++ b/packages/cli/install.sh @@ -652,7 +652,12 @@ NPMRC_EOF vp_install_bin="$BIN_DIR/vp.exe" fi if ! (cd "$VERSION_DIR" && CI=true "$vp_install_bin" install --silent > "$install_log" 2>&1); then - error "Failed to install dependencies. See log for details: $install_log" + if [ "${CI:-}" = "true" ]; then + echo -e "${RED}error${NC}: Failed to install dependencies. Log output:" + cat "$install_log" + else + echo -e "${RED}error${NC}: Failed to install dependencies. See log for details: $install_log" + fi exit 1 fi fi diff --git a/packages/cli/package.json b/packages/cli/package.json index 55785efab3..d6c6e7f229 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -361,7 +361,9 @@ "aarch64-apple-darwin", "x86_64-apple-darwin", "aarch64-unknown-linux-gnu", + "aarch64-unknown-linux-musl", "x86_64-unknown-linux-gnu", + "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc", "aarch64-pc-windows-msvc" ] diff --git a/packages/cli/publish-native-addons.ts b/packages/cli/publish-native-addons.ts index 6e1821d759..e58509c2d7 100644 --- a/packages/cli/publish-native-addons.ts +++ b/packages/cli/publish-native-addons.ts @@ -12,7 +12,9 @@ import { readdir } from 'node:fs/promises'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; -import { NapiCli } from '@napi-rs/cli'; +import { NapiCli, parseTriple } from '@napi-rs/cli'; + +import pkg from './package.json' with { type: 'json' }; const cli = new NapiCli(); @@ -40,66 +42,56 @@ await cli.prePublish({ skipOptionalPublish: true, }); -// Mapping from npm platform directory names to Rust target triples -const RUST_TARGETS: Record = { - 'darwin-arm64': 'aarch64-apple-darwin', - 'darwin-x64': 'x86_64-apple-darwin', - 'linux-arm64-gnu': 'aarch64-unknown-linux-gnu', - 'linux-x64-gnu': 'x86_64-unknown-linux-gnu', - 'win32-arm64-msvc': 'aarch64-pc-windows-msvc', - 'win32-x64-msvc': 'x86_64-pc-windows-msvc', -}; const npmDir = join(currentDir, 'npm'); const platformDirs = await readdir(npmDir); // Publish each NAPI platform package (without vp binary) const npmTag = process.env.NPM_TAG || 'latest'; for (const file of platformDirs) { - execSync(`npm publish --tag ${npmTag} --access public`, { - cwd: join(currentDir, 'npm', file), - env: process.env, - stdio: 'inherit', - }); + try { + const output = execSync(`npm publish --tag ${npmTag} --access public`, { + cwd: join(currentDir, 'npm', file), + env: process.env, + stdio: 'pipe', + }); + process.stdout.write(output); + } catch (e) { + if ( + e instanceof Error && + e.message.includes('You cannot publish over the previously published versions') + ) { + // eslint-disable-next-line no-console + console.info(e.message); + // eslint-disable-next-line no-console + console.warn(`${file} has been published, skipping`); + } else { + throw e; + } + } } -// Platform metadata for CLI packages -const PLATFORM_META: Record = { - 'darwin-arm64': { os: 'darwin', cpu: 'arm64' }, - 'darwin-x64': { os: 'darwin', cpu: 'x64' }, - 'linux-arm64-gnu': { os: 'linux', cpu: 'arm64' }, - 'linux-x64-gnu': { os: 'linux', cpu: 'x64' }, - 'win32-arm64-msvc': { os: 'win32', cpu: 'arm64' }, - 'win32-x64-msvc': { os: 'win32', cpu: 'x64' }, -}; - // Read version from packages/cli/package.json for lockstep versioning const cliPackageJson = JSON.parse(readFileSync(join(currentDir, 'package.json'), 'utf-8')); const cliVersion = cliPackageJson.version; // Create and publish separate @voidzero-dev/vite-plus-cli-{platform} packages const cliNpmDir = join(currentDir, 'cli-npm'); -for (const [platform, rustTarget] of Object.entries(RUST_TARGETS)) { - const meta = PLATFORM_META[platform]; - if (!meta) { - // eslint-disable-next-line no-console - console.log(`Skipping CLI package for ${platform}: no platform metadata`); - continue; - } - - const isWindows = platform.startsWith('win32'); +for (const napiTarget of pkg.napi.targets) { + const { platform, arch, abi, platformArchABI } = parseTriple(napiTarget); + const isWindows = platform === 'win32'; const binaryName = isWindows ? 'vp.exe' : 'vp'; - const rustBinarySource = join(repoRoot, 'target', rustTarget, 'release', binaryName); + const rustBinarySource = join(repoRoot, 'target', napiTarget, 'release', binaryName); if (!existsSync(rustBinarySource)) { // eslint-disable-next-line no-console console.warn( - `Warning: Rust binary not found at ${rustBinarySource}, skipping CLI package for ${platform}`, + `Warning: Rust binary not found at ${rustBinarySource}, skipping CLI package for ${platformArchABI}`, ); continue; } // Create temp directory for CLI package - const platformCliDir = join(cliNpmDir, platform); + const platformCliDir = join(cliNpmDir, platformArchABI); mkdirSync(platformCliDir, { recursive: true }); // Copy binary @@ -114,10 +106,10 @@ for (const [platform, rustTarget] of Object.entries(RUST_TARGETS)) { const shimName = 'vp-shim.exe'; const files = [binaryName]; if (isWindows) { - const shimSource = join(repoRoot, 'target', rustTarget, 'release', shimName); + const shimSource = join(repoRoot, 'target', napiTarget, 'release', shimName); if (!existsSync(shimSource)) { console.error( - `Error: ${shimName} not found at ${shimSource}. Run "cargo build -p vite_trampoline --release --target ${rustTarget}" first.`, + `Error: ${shimName} not found at ${shimSource}. Run "cargo build -p vite_trampoline --release --target ${napiTarget}" first.`, ); process.exit(1); } @@ -127,12 +119,13 @@ for (const [platform, rustTarget] of Object.entries(RUST_TARGETS)) { // Generate package.json const cliPackage = { - name: `@voidzero-dev/vite-plus-cli-${platform}`, + name: `@voidzero-dev/vite-plus-cli-${platformArchABI}`, version: cliVersion, - os: [meta.os], - cpu: [meta.cpu], + os: [platform], + cpu: [arch], + ...(abi ? { libc: [abi] } : {}), files, - description: `Vite+ CLI binary for ${platform}`, + description: `Vite+ CLI binary for ${platformArchABI}`, repository: cliPackageJson.repository, }; writeFileSync(join(platformCliDir, 'package.json'), JSON.stringify(cliPackage, null, 2) + '\n'); diff --git a/packages/cli/snap-tests-global/command-dlx-npm10/steps.json b/packages/cli/snap-tests-global/command-dlx-npm10/steps.json index 1ad3ecb05c..bd53104b6d 100644 --- a/packages/cli/snap-tests-global/command-dlx-npm10/steps.json +++ b/packages/cli/snap-tests-global/command-dlx-npm10/steps.json @@ -1,5 +1,5 @@ { - "ignoredPlatforms": ["win32"], + "ignoredPlatforms": ["win32", { "os": "linux", "libc": "musl" }], "commands": [ "vp dlx --help # should show help message", "vp dlx -s cowsay hello # should run cowsay with npm exec", diff --git a/packages/cli/snap-tests-global/command-pack-exe/snap.txt b/packages/cli/snap-tests-global/command-pack-exe/snap.txt index c4f64aae6a..ca18a2f761 100644 --- a/packages/cli/snap-tests-global/command-pack-exe/snap.txt +++ b/packages/cli/snap-tests-global/command-pack-exe/snap.txt @@ -1,4 +1,4 @@ -> vp pack src/index.ts --exe +> vp pack src/index.ts --exe 2>&1 VITE+ - The Unified Toolchain for the Web ℹ entry: src/index.ts diff --git a/packages/cli/snap-tests-global/command-pack-exe/steps.json b/packages/cli/snap-tests-global/command-pack-exe/steps.json index 1be07f67c3..5748adb69a 100644 --- a/packages/cli/snap-tests-global/command-pack-exe/steps.json +++ b/packages/cli/snap-tests-global/command-pack-exe/steps.json @@ -1,8 +1,14 @@ { - "ignoredPlatforms": ["win32"], + "ignoredPlatforms": [ + "win32", + { + "os": "linux", + "libc": "musl" + } + ], "env": { "VITE_DISABLE_AUTO_INSTALL": "1" }, - "commands": ["vp pack src/index.ts --exe", "ls dist", "ls build", "./build/index"], + "commands": ["vp pack src/index.ts --exe 2>&1", "ls dist", "ls build", "./build/index"], "after": ["rm -rf dist", "rm -rf build"] } diff --git a/packages/cli/snap-tests-global/command-upgrade-check/steps.json b/packages/cli/snap-tests-global/command-upgrade-check/steps.json index 1dd54126fa..eeab1191db 100644 --- a/packages/cli/snap-tests-global/command-upgrade-check/steps.json +++ b/packages/cli/snap-tests-global/command-upgrade-check/steps.json @@ -1,4 +1,4 @@ { - "ignoredPlatforms": ["win32"], + "ignoredPlatforms": ["win32", { "os": "linux", "libc": "musl" }], "commands": ["vp upgrade --check # check for updates without installing"] } diff --git a/packages/cli/snap-tests-global/create-missing-typecheck/snap.txt b/packages/cli/snap-tests-global/create-missing-typecheck/snap.txt index ab68bbd06b..d066f83b35 100644 --- a/packages/cli/snap-tests-global/create-missing-typecheck/snap.txt +++ b/packages/cli/snap-tests-global/create-missing-typecheck/snap.txt @@ -20,5 +20,4 @@ export default defineConfig({ lint: { options: { typeAware: true, typeCheck: true } }, }); -> cat vite-plus-monorepo/apps/website/vite.config.ts 2>&1 || true # sub-app should NOT have typeAware/typeCheck -cat: vite-plus-monorepo/apps/website/vite.config.ts: No such file or directory +> test ! -f vite-plus-monorepo/apps/website/vite.config.ts # sub-app should NOT have typeAware/typeCheck \ No newline at end of file diff --git a/packages/cli/snap-tests-global/create-missing-typecheck/steps.json b/packages/cli/snap-tests-global/create-missing-typecheck/steps.json index 40fb228b78..4d4839b054 100644 --- a/packages/cli/snap-tests-global/create-missing-typecheck/steps.json +++ b/packages/cli/snap-tests-global/create-missing-typecheck/steps.json @@ -1,5 +1,5 @@ { - "ignoredPlatforms": ["darwin", "win32"], + "ignoredPlatforms": ["darwin", "win32", { "os": "linux", "libc": "musl" }], "commands": [ { "command": "vp create vite:application --no-interactive # create standalone app", @@ -11,6 +11,6 @@ "ignoreOutput": true }, "cat vite-plus-monorepo/vite.config.ts # check monorepo root vite.config.ts has typeAware and typeCheck", - "cat vite-plus-monorepo/apps/website/vite.config.ts 2>&1 || true # sub-app should NOT have typeAware/typeCheck" + "test ! -f vite-plus-monorepo/apps/website/vite.config.ts # sub-app should NOT have typeAware/typeCheck" ] } diff --git a/packages/cli/snap-tests-global/migration-already-vite-plus-with-husky-hookspath/steps.json b/packages/cli/snap-tests-global/migration-already-vite-plus-with-husky-hookspath/steps.json index 43931e9de7..97964417b1 100644 --- a/packages/cli/snap-tests-global/migration-already-vite-plus-with-husky-hookspath/steps.json +++ b/packages/cli/snap-tests-global/migration-already-vite-plus-with-husky-hookspath/steps.json @@ -1,10 +1,22 @@ { "commands": [ - { "command": "git init", "ignoreOutput": true }, - { "command": "git config core.hooksPath .husky/_", "ignoreOutput": true }, + { + "command": "git init", + "ignoreOutput": true + }, + { + "command": "git config core.hooksPath .husky/_", + "ignoreOutput": true + }, "vp migrate --no-interactive # should override husky's core.hooksPath and migrate hooks", "cat package.json # husky/lint-staged should be removed, prepare should be vp config", "cat .vite-hooks/pre-commit # pre-commit hook should be rewritten", "git config --local core.hooksPath # should be .vite-hooks/_" + ], + "ignoredPlatforms": [ + { + "os": "linux", + "libc": "musl" + } ] } diff --git a/packages/cli/snap-tests-global/migration-already-vite-plus-with-husky-lint-staged/steps.json b/packages/cli/snap-tests-global/migration-already-vite-plus-with-husky-lint-staged/steps.json index a1c20d3c25..6d0334c644 100644 --- a/packages/cli/snap-tests-global/migration-already-vite-plus-with-husky-lint-staged/steps.json +++ b/packages/cli/snap-tests-global/migration-already-vite-plus-with-husky-lint-staged/steps.json @@ -1,9 +1,18 @@ { "commands": [ - { "command": "git init", "ignoreOutput": true }, + { + "command": "git init", + "ignoreOutput": true + }, "vp migrate --no-interactive # should still migrate husky/lint-staged even though vite-plus exists", "cat package.json # husky/lint-staged should be removed, prepare should be vp config", "cat .vite-hooks/pre-commit # pre-commit hook should be rewritten", "test -d .husky && echo '.husky directory exists' || echo 'No .husky directory' # .husky should be removed" + ], + "ignoredPlatforms": [ + { + "os": "linux", + "libc": "musl" + } ] } diff --git a/packages/cli/snap-tests-global/migration-already-vite-plus/steps.json b/packages/cli/snap-tests-global/migration-already-vite-plus/steps.json index dbc19ed8ec..de62b61d76 100644 --- a/packages/cli/snap-tests-global/migration-already-vite-plus/steps.json +++ b/packages/cli/snap-tests-global/migration-already-vite-plus/steps.json @@ -1,3 +1,9 @@ { - "commands": ["vp migrate --no-interactive # should detect existing vite-plus and exit"] + "commands": ["vp migrate --no-interactive # should detect existing vite-plus and exit"], + "ignoredPlatforms": [ + { + "os": "linux", + "libc": "musl" + } + ] } diff --git a/packages/cli/snap-tests-global/migration-auto-create-vite-config/snap.txt b/packages/cli/snap-tests-global/migration-auto-create-vite-config/snap.txt index e2a110bc4d..9fb8a5d155 100644 --- a/packages/cli/snap-tests-global/migration-auto-create-vite-config/snap.txt +++ b/packages/cli/snap-tests-global/migration-auto-create-vite-config/snap.txt @@ -1,4 +1,4 @@ -> vp migrate --no-interactive # migration should auto create vite.config.ts and remove oxlintrc and oxfmtrc +> vp migrate --no-interactive 2>&1 # migration should auto create vite.config.ts and remove oxlintrc and oxfmtrc VITE+ - The Unified Toolchain for the Web ◇ Migrated . to Vite+ @@ -30,12 +30,8 @@ export default defineConfig({ }, }); -> cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is removed -cat: .oxlintrc.json: No such file or directory - -> cat .oxfmtrc.json && exit 1 || true # check .oxfmtrc.json is removed -cat: .oxfmtrc.json: No such file or directory - +> test ! -f .oxlintrc.json # check .oxlintrc.json is removed +> test ! -f .oxfmtrc.json # check .oxfmtrc.json is removed > cat package.json # check package.json { "devDependencies": { diff --git a/packages/cli/snap-tests-global/migration-auto-create-vite-config/steps.json b/packages/cli/snap-tests-global/migration-auto-create-vite-config/steps.json index 22f495e2ec..5892f08fea 100644 --- a/packages/cli/snap-tests-global/migration-auto-create-vite-config/steps.json +++ b/packages/cli/snap-tests-global/migration-auto-create-vite-config/steps.json @@ -1,9 +1,9 @@ { "commands": [ - "vp migrate --no-interactive # migration should auto create vite.config.ts and remove oxlintrc and oxfmtrc", + "vp migrate --no-interactive 2>&1 # migration should auto create vite.config.ts and remove oxlintrc and oxfmtrc", "cat vite.config.ts # check vite.config.ts", - "cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is removed", - "cat .oxfmtrc.json && exit 1 || true # check .oxfmtrc.json is removed", + "test ! -f .oxlintrc.json # check .oxlintrc.json is removed", + "test ! -f .oxfmtrc.json # check .oxfmtrc.json is removed", "cat package.json # check package.json" ] } diff --git a/packages/cli/snap-tests-global/migration-baseurl-tsconfig/snap.txt b/packages/cli/snap-tests-global/migration-baseurl-tsconfig/snap.txt index 525ab6ee9a..c25c8d0a00 100644 --- a/packages/cli/snap-tests-global/migration-baseurl-tsconfig/snap.txt +++ b/packages/cli/snap-tests-global/migration-baseurl-tsconfig/snap.txt @@ -23,9 +23,7 @@ export default defineConfig({ }, }); -> cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is removed -cat: .oxlintrc.json: No such file or directory - +> test ! -f .oxlintrc.json # check .oxlintrc.json is removed > cat package.json # check package.json { "devDependencies": { diff --git a/packages/cli/snap-tests-global/migration-baseurl-tsconfig/steps.json b/packages/cli/snap-tests-global/migration-baseurl-tsconfig/steps.json index d19d972551..213af167d1 100644 --- a/packages/cli/snap-tests-global/migration-baseurl-tsconfig/steps.json +++ b/packages/cli/snap-tests-global/migration-baseurl-tsconfig/steps.json @@ -2,7 +2,7 @@ "commands": [ "vp migrate --no-interactive # migration should skip typeAware/typeCheck when tsconfig has baseUrl", "cat vite.config.ts # check vite.config.ts — should NOT have typeAware or typeCheck", - "cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is removed", + "test ! -f .oxlintrc.json # check .oxlintrc.json is removed", "cat package.json # check package.json" ] } diff --git a/packages/cli/snap-tests-global/migration-eslint-legacy-already-vite-plus/steps.json b/packages/cli/snap-tests-global/migration-eslint-legacy-already-vite-plus/steps.json index 44c30c2933..c8aec76f61 100644 --- a/packages/cli/snap-tests-global/migration-eslint-legacy-already-vite-plus/steps.json +++ b/packages/cli/snap-tests-global/migration-eslint-legacy-already-vite-plus/steps.json @@ -1,5 +1,11 @@ { "commands": [ "vp migrate --no-interactive # should show legacy config warning and already using Vite+" + ], + "ignoredPlatforms": [ + { + "os": "linux", + "libc": "musl" + } ] } diff --git a/packages/cli/snap-tests-global/migration-eslint-lint-staged-mjs/steps.json b/packages/cli/snap-tests-global/migration-eslint-lint-staged-mjs/steps.json index 5ad71edce7..650a464554 100644 --- a/packages/cli/snap-tests-global/migration-eslint-lint-staged-mjs/steps.json +++ b/packages/cli/snap-tests-global/migration-eslint-lint-staged-mjs/steps.json @@ -2,5 +2,11 @@ "commands": [ "vp migrate --no-interactive # migration should warn about non-JSON lint-staged config", "cat lint-staged.config.mjs # verify non-JSON lint-staged config is preserved unchanged" + ], + "ignoredPlatforms": [ + { + "os": "linux", + "libc": "musl" + } ] } diff --git a/packages/cli/snap-tests-global/migration-eslint-lintstagedrc/snap.txt b/packages/cli/snap-tests-global/migration-eslint-lintstagedrc/snap.txt index 73c2e0df81..93ad9e3aa5 100644 --- a/packages/cli/snap-tests-global/migration-eslint-lintstagedrc/snap.txt +++ b/packages/cli/snap-tests-global/migration-eslint-lintstagedrc/snap.txt @@ -26,9 +26,7 @@ VITE+ - The Unified Toolchain for the Web } } -[1]> cat .lintstagedrc.json # check eslint rewritten to vp lint in lintstagedrc -cat: .lintstagedrc.json: No such file or directory - +> test ! -f .lintstagedrc.json # check lintstagedrc.json is removed > cat vite.config.ts # check oxlint config merged into vite.config.ts import { defineConfig } from 'vite-plus'; diff --git a/packages/cli/snap-tests-global/migration-eslint-lintstagedrc/steps.json b/packages/cli/snap-tests-global/migration-eslint-lintstagedrc/steps.json index 1fdb3a0ade..df474cdbe5 100644 --- a/packages/cli/snap-tests-global/migration-eslint-lintstagedrc/steps.json +++ b/packages/cli/snap-tests-global/migration-eslint-lintstagedrc/steps.json @@ -2,7 +2,7 @@ "commands": [ "vp migrate --no-interactive # migration should detect eslint and auto-migrate including lintstagedrc", "cat package.json # check eslint removed and scripts rewritten", - "cat .lintstagedrc.json # check eslint rewritten to vp lint in lintstagedrc", + "test ! -f .lintstagedrc.json # check lintstagedrc.json is removed", "cat vite.config.ts # check oxlint config merged into vite.config.ts" ] } diff --git a/packages/cli/snap-tests-global/migration-eslint-monorepo/snap.txt b/packages/cli/snap-tests-global/migration-eslint-monorepo/snap.txt index d7b4b59426..cbad7b75b1 100644 --- a/packages/cli/snap-tests-global/migration-eslint-monorepo/snap.txt +++ b/packages/cli/snap-tests-global/migration-eslint-monorepo/snap.txt @@ -50,5 +50,4 @@ VITE+ - The Unified Toolchain for the Web "devDependencies": {} } -> cat eslint.config.mjs && exit 1 || true # check root eslint config is removed -cat: eslint.config.mjs: No such file or directory +> test ! -f eslint.config.mjs # check root eslint config is removed \ No newline at end of file diff --git a/packages/cli/snap-tests-global/migration-eslint-monorepo/steps.json b/packages/cli/snap-tests-global/migration-eslint-monorepo/steps.json index 8f58004cc6..725590b9ed 100644 --- a/packages/cli/snap-tests-global/migration-eslint-monorepo/steps.json +++ b/packages/cli/snap-tests-global/migration-eslint-monorepo/steps.json @@ -4,6 +4,6 @@ "cat package.json # check root eslint removed and scripts rewritten", "cat packages/app/package.json # check app eslint removed and scripts rewritten", "cat packages/utils/package.json # check utils eslint removed and scripts rewritten", - "cat eslint.config.mjs && exit 1 || true # check root eslint config is removed" + "test ! -f eslint.config.mjs # check root eslint config is removed" ] } diff --git a/packages/cli/snap-tests-global/migration-eslint-npx-wrapper/snap.txt b/packages/cli/snap-tests-global/migration-eslint-npx-wrapper/snap.txt index 0ccc046344..200ef19892 100644 --- a/packages/cli/snap-tests-global/migration-eslint-npx-wrapper/snap.txt +++ b/packages/cli/snap-tests-global/migration-eslint-npx-wrapper/snap.txt @@ -31,5 +31,4 @@ VITE+ - The Unified Toolchain for the Web } } -> cat eslint.config.mjs && exit 1 || true # check eslint config is removed -cat: eslint.config.mjs: No such file or directory +> test ! -f eslint.config.mjs # check eslint config is removed \ No newline at end of file diff --git a/packages/cli/snap-tests-global/migration-eslint-npx-wrapper/steps.json b/packages/cli/snap-tests-global/migration-eslint-npx-wrapper/steps.json index 040af33370..333c9ad03c 100644 --- a/packages/cli/snap-tests-global/migration-eslint-npx-wrapper/steps.json +++ b/packages/cli/snap-tests-global/migration-eslint-npx-wrapper/steps.json @@ -2,6 +2,6 @@ "commands": [ "vp migrate --no-interactive # migration should rewrite bare eslint but leave npx wrappers unchanged", "cat package.json # check eslint removed, bare eslint rewritten, npx/pnpm exec/bunx wrappers unchanged", - "cat eslint.config.mjs && exit 1 || true # check eslint config is removed" + "test ! -f eslint.config.mjs # check eslint config is removed" ] } diff --git a/packages/cli/snap-tests-global/migration-eslint-rerun-dual-config/snap.txt b/packages/cli/snap-tests-global/migration-eslint-rerun-dual-config/snap.txt index 3f4ddf2883..b7dc9a4cd8 100644 --- a/packages/cli/snap-tests-global/migration-eslint-rerun-dual-config/snap.txt +++ b/packages/cli/snap-tests-global/migration-eslint-rerun-dual-config/snap.txt @@ -30,12 +30,8 @@ ESLint comments replaced "packageManager": "pnpm@" } -> cat eslint.config.mjs && exit 1 || true # check flat config is removed -cat: eslint.config.mjs: No such file or directory - -> cat .eslintrc && exit 1 || true # check legacy config is also removed -cat: .eslintrc: No such file or directory - +> test ! -f eslint.config.mjs # check flat config is removed +> test ! -f .eslintrc # check legacy config is also removed > cat vite.config.ts # check oxlint config merged into vite.config.ts import { defineConfig } from 'vite-plus'; diff --git a/packages/cli/snap-tests-global/migration-eslint-rerun-dual-config/steps.json b/packages/cli/snap-tests-global/migration-eslint-rerun-dual-config/steps.json index 833ec3864e..b0092174c5 100644 --- a/packages/cli/snap-tests-global/migration-eslint-rerun-dual-config/steps.json +++ b/packages/cli/snap-tests-global/migration-eslint-rerun-dual-config/steps.json @@ -2,8 +2,14 @@ "commands": [ "vp migrate --no-interactive # should detect vite-plus + eslint and auto-migrate eslint", "cat package.json # check eslint removed and scripts rewritten", - "cat eslint.config.mjs && exit 1 || true # check flat config is removed", - "cat .eslintrc && exit 1 || true # check legacy config is also removed", + "test ! -f eslint.config.mjs # check flat config is removed", + "test ! -f .eslintrc # check legacy config is also removed", "cat vite.config.ts # check oxlint config merged into vite.config.ts" + ], + "ignoredPlatforms": [ + { + "os": "linux", + "libc": "musl" + } ] } diff --git a/packages/cli/snap-tests-global/migration-eslint-rerun-mjs/snap.txt b/packages/cli/snap-tests-global/migration-eslint-rerun-mjs/snap.txt index 50c774d11b..7e5bfbd38d 100644 --- a/packages/cli/snap-tests-global/migration-eslint-rerun-mjs/snap.txt +++ b/packages/cli/snap-tests-global/migration-eslint-rerun-mjs/snap.txt @@ -28,9 +28,7 @@ ESLint comments replaced "packageManager": "pnpm@" } -> cat eslint.config.mjs && exit 1 || true # check eslint config is removed -cat: eslint.config.mjs: No such file or directory - +> test ! -f eslint.config.mjs # check eslint config is removed > cat vite.config.mjs # check oxlint config merged into existing vite.config.mjs (not creating vite.config.ts) import { defineConfig } from 'vite-plus'; diff --git a/packages/cli/snap-tests-global/migration-eslint-rerun-mjs/steps.json b/packages/cli/snap-tests-global/migration-eslint-rerun-mjs/steps.json index 2e0c082c3f..4cd12d73e1 100644 --- a/packages/cli/snap-tests-global/migration-eslint-rerun-mjs/steps.json +++ b/packages/cli/snap-tests-global/migration-eslint-rerun-mjs/steps.json @@ -2,7 +2,13 @@ "commands": [ "vp migrate --no-interactive # should detect vite-plus + eslint and auto-migrate eslint", "cat package.json # check eslint removed from devDependencies and scripts rewritten", - "cat eslint.config.mjs && exit 1 || true # check eslint config is removed", + "test ! -f eslint.config.mjs # check eslint config is removed", "cat vite.config.mjs # check oxlint config merged into existing vite.config.mjs (not creating vite.config.ts)" + ], + "ignoredPlatforms": [ + { + "os": "linux", + "libc": "musl" + } ] } diff --git a/packages/cli/snap-tests-global/migration-eslint-rerun/snap.txt b/packages/cli/snap-tests-global/migration-eslint-rerun/snap.txt index 3c5766d564..93b81626b7 100644 --- a/packages/cli/snap-tests-global/migration-eslint-rerun/snap.txt +++ b/packages/cli/snap-tests-global/migration-eslint-rerun/snap.txt @@ -28,9 +28,7 @@ ESLint comments replaced "packageManager": "pnpm@" } -> cat eslint.config.mjs && exit 1 || true # check eslint config is removed -cat: eslint.config.mjs: No such file or directory - +> test ! -f eslint.config.mjs # check eslint config is removed > cat vite.config.ts # check oxlint config merged into vite.config.ts import { defineConfig } from 'vite-plus'; diff --git a/packages/cli/snap-tests-global/migration-eslint-rerun/steps.json b/packages/cli/snap-tests-global/migration-eslint-rerun/steps.json index 2a0fea036c..17a5ccd303 100644 --- a/packages/cli/snap-tests-global/migration-eslint-rerun/steps.json +++ b/packages/cli/snap-tests-global/migration-eslint-rerun/steps.json @@ -2,7 +2,13 @@ "commands": [ "vp migrate --no-interactive # should detect vite-plus + eslint and auto-migrate eslint", "cat package.json # check eslint removed from devDependencies and scripts rewritten", - "cat eslint.config.mjs && exit 1 || true # check eslint config is removed", + "test ! -f eslint.config.mjs # check eslint config is removed", "cat vite.config.ts # check oxlint config merged into vite.config.ts" + ], + "ignoredPlatforms": [ + { + "os": "linux", + "libc": "musl" + } ] } diff --git a/packages/cli/snap-tests-global/migration-eslint/snap.txt b/packages/cli/snap-tests-global/migration-eslint/snap.txt index 0f514b0903..c6d5113eae 100644 --- a/packages/cli/snap-tests-global/migration-eslint/snap.txt +++ b/packages/cli/snap-tests-global/migration-eslint/snap.txt @@ -29,9 +29,7 @@ VITE+ - The Unified Toolchain for the Web } } -> cat eslint.config.mjs && exit 1 || true # check eslint config is removed -cat: eslint.config.mjs: No such file or directory - +> test ! -f eslint.config.mjs # check eslint config is removed > cat vite.config.ts # check oxlint config merged into vite.config.ts import { defineConfig } from 'vite-plus'; diff --git a/packages/cli/snap-tests-global/migration-eslint/steps.json b/packages/cli/snap-tests-global/migration-eslint/steps.json index 5ddf04f570..e9ad1449c9 100644 --- a/packages/cli/snap-tests-global/migration-eslint/steps.json +++ b/packages/cli/snap-tests-global/migration-eslint/steps.json @@ -2,7 +2,7 @@ "commands": [ "vp migrate --no-interactive # migration should detect eslint and auto-migrate", "cat package.json # check eslint removed and scripts rewritten", - "cat eslint.config.mjs && exit 1 || true # check eslint config is removed", + "test ! -f eslint.config.mjs # check eslint config is removed", "cat vite.config.ts # check oxlint config merged into vite.config.ts" ] } diff --git a/packages/cli/snap-tests-global/migration-existing-lint-staged-config/snap.txt b/packages/cli/snap-tests-global/migration-existing-lint-staged-config/snap.txt index fd52caac34..8ff2897af9 100644 --- a/packages/cli/snap-tests-global/migration-existing-lint-staged-config/snap.txt +++ b/packages/cli/snap-tests-global/migration-existing-lint-staged-config/snap.txt @@ -26,9 +26,7 @@ VITE+ - The Unified Toolchain for the Web } } -[1]> cat .lintstagedrc.json # check lintstagedrc.json (should be deleted after inlining to vite.config.ts) -cat: .lintstagedrc.json: No such file or directory - +> test ! -f .lintstagedrc.json # check lintstagedrc.json (should be deleted after inlining to vite.config.ts) > cat vite.config.ts # check staged config migrated to vite.config.ts import { defineConfig } from 'vite-plus'; diff --git a/packages/cli/snap-tests-global/migration-existing-lint-staged-config/steps.json b/packages/cli/snap-tests-global/migration-existing-lint-staged-config/steps.json index 9e710faeca..bd4eee52c4 100644 --- a/packages/cli/snap-tests-global/migration-existing-lint-staged-config/steps.json +++ b/packages/cli/snap-tests-global/migration-existing-lint-staged-config/steps.json @@ -3,7 +3,7 @@ { "command": "git init", "ignoreOutput": true }, "vp migrate --no-interactive # migration should add prepare script, remove lint-staged from devDeps", "cat package.json # check prepare script added, lint-staged removed from devDeps", - "cat .lintstagedrc.json # check lintstagedrc.json (should be deleted after inlining to vite.config.ts)", + "test ! -f .lintstagedrc.json # check lintstagedrc.json (should be deleted after inlining to vite.config.ts)", "cat vite.config.ts # check staged config migrated to vite.config.ts", "cat .vite-hooks/pre-commit # check pre-commit hook created" ] diff --git a/packages/cli/snap-tests-global/migration-from-tsdown-json-config/snap.txt b/packages/cli/snap-tests-global/migration-from-tsdown-json-config/snap.txt index b1539c66d2..dc8b844389 100644 --- a/packages/cli/snap-tests-global/migration-from-tsdown-json-config/snap.txt +++ b/packages/cli/snap-tests-global/migration-from-tsdown-json-config/snap.txt @@ -5,9 +5,7 @@ VITE+ - The Unified Toolchain for the Web • Node pnpm • 2 config updates applied -> cat tsdown.config.json && exit 1 || true # check tsdown.config.json should be removed -cat: tsdown.config.json: No such file or directory - +> test ! -f tsdown.config.json # check tsdown.config.json should be removed > cat vite.config.ts # check vite.config.ts import { defineConfig } from 'vite-plus'; diff --git a/packages/cli/snap-tests-global/migration-from-tsdown-json-config/steps.json b/packages/cli/snap-tests-global/migration-from-tsdown-json-config/steps.json index 68b08d1b03..288d212f2e 100644 --- a/packages/cli/snap-tests-global/migration-from-tsdown-json-config/steps.json +++ b/packages/cli/snap-tests-global/migration-from-tsdown-json-config/steps.json @@ -1,7 +1,7 @@ { "commands": [ "vp migrate --no-interactive # migration should rewrite imports to vite-plus", - "cat tsdown.config.json && exit 1 || true # check tsdown.config.json should be removed", + "test ! -f tsdown.config.json # check tsdown.config.json should be removed", "cat vite.config.ts # check vite.config.ts", "cat package.json # check package.json", "vp migrate --no-interactive # run migration again to check if it is idempotent", diff --git a/packages/cli/snap-tests-global/migration-merge-vite-config-js/snap.txt b/packages/cli/snap-tests-global/migration-merge-vite-config-js/snap.txt index 9e9979b08e..99319a0c63 100644 --- a/packages/cli/snap-tests-global/migration-merge-vite-config-js/snap.txt +++ b/packages/cli/snap-tests-global/migration-merge-vite-config-js/snap.txt @@ -24,9 +24,7 @@ export default { plugins: [react()], } -> cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is removed -cat: .oxlintrc.json: No such file or directory - +> test ! -f .oxlintrc.json # check .oxlintrc.json is removed > cat package.json # check package.json { "scripts": { diff --git a/packages/cli/snap-tests-global/migration-merge-vite-config-js/steps.json b/packages/cli/snap-tests-global/migration-merge-vite-config-js/steps.json index 0375320d78..2fd5b9b311 100644 --- a/packages/cli/snap-tests-global/migration-merge-vite-config-js/steps.json +++ b/packages/cli/snap-tests-global/migration-merge-vite-config-js/steps.json @@ -2,7 +2,7 @@ "commands": [ "vp migrate --no-interactive # migration should merge vite.config.js and remove oxlintrc", "cat vite.config.js # check vite.config.js", - "cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is removed", + "test ! -f .oxlintrc.json # check .oxlintrc.json is removed", "cat package.json # check package.json" ] } diff --git a/packages/cli/snap-tests-global/migration-merge-vite-config-ts/snap.txt b/packages/cli/snap-tests-global/migration-merge-vite-config-ts/snap.txt index 95e1c2e3b2..d11f61d1bb 100644 --- a/packages/cli/snap-tests-global/migration-merge-vite-config-ts/snap.txt +++ b/packages/cli/snap-tests-global/migration-merge-vite-config-ts/snap.txt @@ -45,12 +45,8 @@ export default defineConfig({ }, }); -> cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is removed -cat: .oxlintrc.json: No such file or directory - -> cat .oxfmtrc.json && exit 1 || true # check .oxfmtrc.json is removed -cat: .oxfmtrc.json: No such file or directory - +> test ! -f .oxlintrc.json # check .oxlintrc.json is removed +> test ! -f .oxfmtrc.json # check .oxfmtrc.json is removed > cat package.json # check package.json { "scripts": { diff --git a/packages/cli/snap-tests-global/migration-merge-vite-config-ts/steps.json b/packages/cli/snap-tests-global/migration-merge-vite-config-ts/steps.json index 542a8d33d6..e36168e665 100644 --- a/packages/cli/snap-tests-global/migration-merge-vite-config-ts/steps.json +++ b/packages/cli/snap-tests-global/migration-merge-vite-config-ts/steps.json @@ -2,8 +2,8 @@ "commands": [ "vp migrate --no-interactive # migration should merge vite.config.ts and remove oxlintrc and oxfmtrc", "cat vite.config.ts # check vite.config.ts", - "cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is removed", - "cat .oxfmtrc.json && exit 1 || true # check .oxfmtrc.json is removed", + "test ! -f .oxlintrc.json # check .oxlintrc.json is removed", + "test ! -f .oxfmtrc.json # check .oxfmtrc.json is removed", "cat package.json # check package.json" ] } diff --git a/packages/cli/snap-tests-global/migration-monorepo-pnpm/snap.txt b/packages/cli/snap-tests-global/migration-monorepo-pnpm/snap.txt index 0db0b7ca6f..928f4a9842 100644 --- a/packages/cli/snap-tests-global/migration-monorepo-pnpm/snap.txt +++ b/packages/cli/snap-tests-global/migration-monorepo-pnpm/snap.txt @@ -36,12 +36,8 @@ export default defineConfig({ plugins: [react()], }); -> cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is removed -cat: .oxlintrc.json: No such file or directory - -> cat .oxfmtrc.json && exit 1 || true # check .oxfmtrc.json is removed -cat: .oxfmtrc.json: No such file or directory - +> test ! -f .oxlintrc.json # check .oxlintrc.json is removed +> test ! -f .oxfmtrc.json # check .oxfmtrc.json is removed > cat package.json # check package.json { "name": "migration-monorepo-pnpm", @@ -174,5 +170,4 @@ export default defineConfig({ }); -> cat packages/only-oxlint/.oxlintrc.json && exit 1 || true # check only-oxlint .oxlintrc.json is removed -cat: packages/only-oxlint/.oxlintrc.json: No such file or directory +> test ! -f packages/only-oxlint/.oxlintrc.json # check only-oxlint .oxlintrc.json is removed \ No newline at end of file diff --git a/packages/cli/snap-tests-global/migration-monorepo-pnpm/steps.json b/packages/cli/snap-tests-global/migration-monorepo-pnpm/steps.json index d4bda211fe..313e6f66be 100644 --- a/packages/cli/snap-tests-global/migration-monorepo-pnpm/steps.json +++ b/packages/cli/snap-tests-global/migration-monorepo-pnpm/steps.json @@ -2,14 +2,14 @@ "commands": [ "vp migrate --no-interactive # migration should merge vite.config.ts and remove oxlintrc and oxfmtrc", "cat vite.config.ts # check vite.config.ts", - "cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is removed", - "cat .oxfmtrc.json && exit 1 || true # check .oxfmtrc.json is removed", + "test ! -f .oxlintrc.json # check .oxlintrc.json is removed", + "test ! -f .oxfmtrc.json # check .oxfmtrc.json is removed", "cat package.json # check package.json", "cat pnpm-workspace.yaml # check pnpm-workspace.yaml", "cat packages/app/package.json # check app package.json", "cat packages/utils/package.json # check utils package.json", "cat packages/only-oxlint/package.json # check only-oxlint package.json", "cat packages/only-oxlint/vite.config.ts # check only-oxlint vite.config.ts", - "cat packages/only-oxlint/.oxlintrc.json && exit 1 || true # check only-oxlint .oxlintrc.json is removed" + "test ! -f packages/only-oxlint/.oxlintrc.json # check only-oxlint .oxlintrc.json is removed" ] } 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 9b922b4188..64196416d0 100644 --- a/packages/cli/snap-tests-global/migration-monorepo-yarn4/snap.txt +++ b/packages/cli/snap-tests-global/migration-monorepo-yarn4/snap.txt @@ -27,9 +27,7 @@ export default defineConfig({ plugins: [react()], }); -> cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is removed -cat: .oxlintrc.json: No such file or directory - +> test ! -f .oxlintrc.json # check .oxlintrc.json is removed > cat package.json # check package.json { "name": "migration-monorepo-yarn4", diff --git a/packages/cli/snap-tests-global/migration-monorepo-yarn4/steps.json b/packages/cli/snap-tests-global/migration-monorepo-yarn4/steps.json index 03324b637b..b9a8361f10 100644 --- a/packages/cli/snap-tests-global/migration-monorepo-yarn4/steps.json +++ b/packages/cli/snap-tests-global/migration-monorepo-yarn4/steps.json @@ -2,7 +2,7 @@ "commands": [ "vp migrate --no-interactive # migration should merge vite.config.ts and remove oxlintrc", "cat vite.config.ts # check vite.config.ts", - "cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is removed", + "test ! -f .oxlintrc.json # check .oxlintrc.json is removed", "cat package.json # check package.json", "cat .yarnrc.yml # check .yarnrc.yml", "cat packages/app/package.json # check app package.json", diff --git a/packages/cli/snap-tests-global/migration-prettier-eslint-combo/snap.txt b/packages/cli/snap-tests-global/migration-prettier-eslint-combo/snap.txt index 25f88e2b1a..8d2a0bc035 100644 --- a/packages/cli/snap-tests-global/migration-prettier-eslint-combo/snap.txt +++ b/packages/cli/snap-tests-global/migration-prettier-eslint-combo/snap.txt @@ -33,12 +33,8 @@ Prettier configuration detected. Auto-migrating to Oxfmt... } } -> cat eslint.config.mjs && exit 1 || true # check eslint config is removed -cat: eslint.config.mjs: No such file or directory - -> cat .prettierrc.json && exit 1 || true # check prettier config is removed -cat: .prettierrc.json: No such file or directory - +> test ! -f eslint.config.mjs # check eslint config is removed +> test ! -f .prettierrc.json # check prettier config is removed > cat vite.config.ts # check oxlint and oxfmt config merged into vite.config.ts import { defineConfig } from 'vite-plus'; diff --git a/packages/cli/snap-tests-global/migration-prettier-eslint-combo/steps.json b/packages/cli/snap-tests-global/migration-prettier-eslint-combo/steps.json index 6b22ecce29..3d6f1ad388 100644 --- a/packages/cli/snap-tests-global/migration-prettier-eslint-combo/steps.json +++ b/packages/cli/snap-tests-global/migration-prettier-eslint-combo/steps.json @@ -2,8 +2,8 @@ "commands": [ "vp migrate --no-interactive # migration should detect both eslint and prettier and auto-migrate", "cat package.json # check eslint and prettier removed, scripts rewritten", - "cat eslint.config.mjs && exit 1 || true # check eslint config is removed", - "cat .prettierrc.json && exit 1 || true # check prettier config is removed", + "test ! -f eslint.config.mjs # check eslint config is removed", + "test ! -f .prettierrc.json # check prettier config is removed", "cat vite.config.ts # check oxlint and oxfmt config merged into vite.config.ts" ] } diff --git a/packages/cli/snap-tests-global/migration-prettier-ignore-unknown/snap.txt b/packages/cli/snap-tests-global/migration-prettier-ignore-unknown/snap.txt index f4603a133e..4565ec15ee 100644 --- a/packages/cli/snap-tests-global/migration-prettier-ignore-unknown/snap.txt +++ b/packages/cli/snap-tests-global/migration-prettier-ignore-unknown/snap.txt @@ -31,5 +31,4 @@ Prettier configuration detected. Auto-migrating to Oxfmt... "packageManager": "pnpm@" } -> cat .prettierrc.json && exit 1 || true # check prettier config is removed -cat: .prettierrc.json: No such file or directory +> test ! -f .prettierrc.json # check prettier config is removed \ No newline at end of file diff --git a/packages/cli/snap-tests-global/migration-prettier-ignore-unknown/steps.json b/packages/cli/snap-tests-global/migration-prettier-ignore-unknown/steps.json index 5289ab5349..16dfe97b4a 100644 --- a/packages/cli/snap-tests-global/migration-prettier-ignore-unknown/steps.json +++ b/packages/cli/snap-tests-global/migration-prettier-ignore-unknown/steps.json @@ -2,6 +2,6 @@ "commands": [ "vp migrate --no-interactive # migration should strip --ignore-unknown and -u flags", "cat package.json # check prettier removed and --ignore-unknown stripped from scripts", - "cat .prettierrc.json && exit 1 || true # check prettier config is removed" + "test ! -f .prettierrc.json # check prettier config is removed" ] } diff --git a/packages/cli/snap-tests-global/migration-prettier-rerun/snap.txt b/packages/cli/snap-tests-global/migration-prettier-rerun/snap.txt index 3e3c9d6df8..7e4656f311 100644 --- a/packages/cli/snap-tests-global/migration-prettier-rerun/snap.txt +++ b/packages/cli/snap-tests-global/migration-prettier-rerun/snap.txt @@ -24,9 +24,7 @@ Prettier config migrated to .oxfmtrc.json } } -> cat .prettierrc.json && exit 1 || true # check prettier config is removed -cat: .prettierrc.json: No such file or directory - +> test ! -f .prettierrc.json # check prettier config is removed > cat vite.config.ts # check oxfmt config merged into vite.config.ts import { defineConfig } from 'vite-plus'; diff --git a/packages/cli/snap-tests-global/migration-prettier-rerun/steps.json b/packages/cli/snap-tests-global/migration-prettier-rerun/steps.json index 5fe91a0aff..b45e3ec9fb 100644 --- a/packages/cli/snap-tests-global/migration-prettier-rerun/steps.json +++ b/packages/cli/snap-tests-global/migration-prettier-rerun/steps.json @@ -2,7 +2,13 @@ "commands": [ "vp migrate --no-interactive # should detect vite-plus + prettier and auto-migrate prettier", "cat package.json # check prettier removed from devDependencies and scripts rewritten", - "cat .prettierrc.json && exit 1 || true # check prettier config is removed", + "test ! -f .prettierrc.json # check prettier config is removed", "cat vite.config.ts # check oxfmt config merged into vite.config.ts" + ], + "ignoredPlatforms": [ + { + "os": "linux", + "libc": "musl" + } ] } diff --git a/packages/cli/snap-tests-global/migration-prettier/snap.txt b/packages/cli/snap-tests-global/migration-prettier/snap.txt index 09a5a5319b..782ea99b4d 100644 --- a/packages/cli/snap-tests-global/migration-prettier/snap.txt +++ b/packages/cli/snap-tests-global/migration-prettier/snap.txt @@ -31,9 +31,7 @@ Prettier configuration detected. Auto-migrating to Oxfmt... "packageManager": "pnpm@" } -> cat .prettierrc.json && exit 1 || true # check prettier config is removed -cat: .prettierrc.json: No such file or directory - +> test ! -f .prettierrc.json # check prettier config is removed > cat vite.config.ts # check oxfmt config merged into vite.config.ts import { defineConfig } from 'vite-plus'; diff --git a/packages/cli/snap-tests-global/migration-prettier/steps.json b/packages/cli/snap-tests-global/migration-prettier/steps.json index 9fb2f24d7c..4a4a15672e 100644 --- a/packages/cli/snap-tests-global/migration-prettier/steps.json +++ b/packages/cli/snap-tests-global/migration-prettier/steps.json @@ -2,7 +2,7 @@ "commands": [ "vp migrate --no-interactive # migration should detect prettier and auto-migrate", "cat package.json # check prettier removed and scripts rewritten", - "cat .prettierrc.json && exit 1 || true # check prettier config is removed", + "test ! -f .prettierrc.json # check prettier config is removed", "cat vite.config.ts # check oxfmt config merged into vite.config.ts" ] } diff --git a/packages/cli/snap-tests/command-init-inline-config-existing/snap.txt b/packages/cli/snap-tests/command-init-inline-config-existing/snap.txt index c4b063c11e..33eaff80f3 100644 --- a/packages/cli/snap-tests/command-init-inline-config-existing/snap.txt +++ b/packages/cli/snap-tests/command-init-inline-config-existing/snap.txt @@ -1,11 +1,8 @@ > vp lint --init Skipped initialization: 'lint' already exists in 'vite.config.ts'. -> cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is not created -cat: .oxlintrc.json: No such file or directory - +> test ! -f .oxlintrc.json # check .oxlintrc.json is not created > vp fmt --init Skipped initialization: 'fmt' already exists in 'vite.config.ts'. -> cat .oxfmtrc.json && exit 1 || true # check .oxfmtrc.json is not created -cat: .oxfmtrc.json: No such file or directory +> test ! -f .oxfmtrc.json # check .oxfmtrc.json is not created \ No newline at end of file diff --git a/packages/cli/snap-tests/command-init-inline-config-existing/steps.json b/packages/cli/snap-tests/command-init-inline-config-existing/steps.json index 0a6f19618a..19b3b8684f 100644 --- a/packages/cli/snap-tests/command-init-inline-config-existing/steps.json +++ b/packages/cli/snap-tests/command-init-inline-config-existing/steps.json @@ -4,8 +4,8 @@ }, "commands": [ "vp lint --init", - "cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is not created", + "test ! -f .oxlintrc.json # check .oxlintrc.json is not created", "vp fmt --init", - "cat .oxfmtrc.json && exit 1 || true # check .oxfmtrc.json is not created" + "test ! -f .oxfmtrc.json # check .oxfmtrc.json is not created" ] } diff --git a/packages/cli/snap-tests/command-init-inline-config/snap.txt b/packages/cli/snap-tests/command-init-inline-config/snap.txt index 27afbb52b1..0f0dd21907 100644 --- a/packages/cli/snap-tests/command-init-inline-config/snap.txt +++ b/packages/cli/snap-tests/command-init-inline-config/snap.txt @@ -8,9 +8,7 @@ export default defineConfig({ lint: { options: { typeAware: true, typeCheck: true } }, }); -> cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is removed -cat: .oxlintrc.json: No such file or directory - +> test ! -f .oxlintrc.json # check .oxlintrc.json is removed > rm vite.config.ts > vp fmt --init Added 'fmt' to 'vite.config.ts'. @@ -24,5 +22,4 @@ export default defineConfig({ }, }); -> cat .oxfmtrc.json && exit 1 || true # check .oxfmtrc.json is removed -cat: .oxfmtrc.json: No such file or directory +> test ! -f .oxfmtrc.json # check .oxfmtrc.json is removed \ No newline at end of file diff --git a/packages/cli/snap-tests/command-init-inline-config/steps.json b/packages/cli/snap-tests/command-init-inline-config/steps.json index 038f74c537..db6ca60a9d 100644 --- a/packages/cli/snap-tests/command-init-inline-config/steps.json +++ b/packages/cli/snap-tests/command-init-inline-config/steps.json @@ -5,10 +5,10 @@ "commands": [ "vp lint --init", "cat vite.config.ts", - "cat .oxlintrc.json && exit 1 || true # check .oxlintrc.json is removed", + "test ! -f .oxlintrc.json # check .oxlintrc.json is removed", "rm vite.config.ts", "vp fmt --init", "cat vite.config.ts", - "cat .oxfmtrc.json && exit 1 || true # check .oxfmtrc.json is removed" + "test ! -f .oxfmtrc.json # check .oxfmtrc.json is removed" ] } diff --git a/packages/tools/src/snap-test.ts b/packages/tools/src/snap-test.ts index 7436021896..3556a87805 100755 --- a/packages/tools/src/snap-test.ts +++ b/packages/tools/src/snap-test.ts @@ -204,8 +204,13 @@ interface Command { timeout?: number; } +interface PlatformFilter { + os: string; + libc?: string; +} + interface Steps { - ignoredPlatforms?: string[]; + ignoredPlatforms?: (string | PlatformFilter)[]; env: Record; commands: (string | Command)[]; /** @@ -221,11 +226,62 @@ interface Steps { serial?: boolean; } +let _isMusl: boolean | null = null; + +function isMusl(): boolean { + if (_isMusl === null) { + if (process.platform !== 'linux') { + _isMusl = false; + } else if (typeof process.report?.getReport === 'function') { + // Use Node.js process.report API to detect libc type: + // - glibcVersionRuntime present → glibc + // - shared objects contain "musl" → musl + const report = process.report.getReport() as Record; + if (report.header?.glibcVersionRuntime) { + _isMusl = false; + } else if (Array.isArray(report.sharedObjects)) { + _isMusl = report.sharedObjects.some( + (f: string) => f.includes('libc.musl-') || f.includes('ld-musl-'), + ); + } else { + _isMusl = false; + } + } else { + _isMusl = false; + } + } + return _isMusl; +} + +function shouldSkipPlatform(ignoredPlatforms: (string | PlatformFilter)[]): boolean { + for (const filter of ignoredPlatforms) { + if (typeof filter === 'string') { + if (filter === process.platform) { + return true; + } + } else { + if (filter.os !== process.platform) { + continue; + } + if (filter.libc === undefined) { + return true; + } + if (filter.libc === 'musl' && isMusl()) { + return true; + } + if (filter.libc === 'glibc' && !isMusl()) { + return true; + } + } + } + return false; +} + async function runTestCase(name: string, tempTmpDir: string, casesDir: string, binDir?: string) { const steps: Steps = JSON.parse( await fsPromises.readFile(`${casesDir}/${name}/steps.json`, 'utf-8'), ); - if (steps.ignoredPlatforms !== undefined && steps.ignoredPlatforms.includes(process.platform)) { + if (steps.ignoredPlatforms !== undefined && shouldSkipPlatform(steps.ignoredPlatforms)) { console.log('%s skipped on platform %s', name, process.platform); return; }