diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1bddc36e9c..394f157f3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -142,7 +142,7 @@ jobs: cache-key: test tools: just - - run: cargo check --all-targets --all-features + - run: cargo check --workspace --all-targets --all-features env: RUSTFLAGS: '-D warnings --cfg tokio_unstable' # also update .cargo/config.toml @@ -201,7 +201,7 @@ jobs: ;; esac export CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS="$CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS -D warnings" - cargo check --all-targets --all-features --target x86_64-pc-windows-msvc + cargo check --workspace --all-targets --all-features --target x86_64-pc-windows-msvc # Keep the package selection in sync with the `test` recipe in justfile. # vp_cli_snapshots is excluded there too: its snapshot suite needs a @@ -336,7 +336,7 @@ jobs: components: clippy rust-docs rustfmt - run: | - cargo shear + cargo shear --exclude 'rolldown*' --exclude string_wizard cargo fmt --check cargo fmt --manifest-path crates/vp_trampoline/Cargo.toml --check just lint diff --git a/.github/workflows/prepare_release.yml b/.github/workflows/prepare_release.yml index 09192d6b8d..6d7f2f32b8 100644 --- a/.github/workflows/prepare_release.yml +++ b/.github/workflows/prepare_release.yml @@ -58,7 +58,7 @@ jobs: update_toml crates/vp_global_cli/Cargo.toml - name: Refresh Cargo.lock - run: cargo check + run: cargo check --workspace --all-targets --all-features - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 id: app-token diff --git a/AGENTS.md b/AGENTS.md index 34cc3a6465..0099e8b732 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -165,6 +165,8 @@ Reference these files instead of duplicating rules here: Prefer shared output helpers for user-facing messages and match nearby command style. New Rust code should satisfy the custom clippy restrictions. +Use the narrowest Rust visibility that fits the consumer. Reserve `pub` for APIs that another crate or external runtime intentionally consumes. Prefer private items, then `pub(super)` or `pub(crate)`, for implementation details. Add a crate-root `pub use` only when it is part of the intended crate API. + ### TypeScript Reference these files instead of duplicating rules here: diff --git a/Cargo.lock b/Cargo.lock index 6701c1784d..426e7239a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,9 +60,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.3.4", "once_cell", - "serde", "version_check", "zerocopy", ] @@ -723,18 +721,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "borrow-or-share" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c" - -[[package]] -name = "bpaf" -version = "0.9.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b86829876e7e200161a5aa6ea688d46c32d64d70ee3100127790dde84688d6e" - [[package]] name = "brush-parser" version = "0.3.0" @@ -947,17 +933,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid", -] - [[package]] name = "cfb-mode" version = "0.8.2" @@ -1373,21 +1348,6 @@ dependencies = [ "itertools 0.13.0", ] -[[package]] -name = "criterion2" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b961b72d7eaf7444d1eb98d353d5c2aa08e2443d77fef6bff0e6e97064162482" -dependencies = [ - "bpaf", - "cast", - "ciborium", - "num-traits", - "oorandom", - "serde", - "serde_json", -] - [[package]] name = "crossbeam-deque" version = "0.8.7" @@ -1636,12 +1596,6 @@ dependencies = [ "parking_lot_core", ] -[[package]] -name = "data-encoding" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" - [[package]] name = "dbl" version = "0.3.2" @@ -1978,15 +1932,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "email_address" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" -dependencies = [ - "serde", -] - [[package]] name = "embedded-io" version = "0.4.0" @@ -2103,17 +2048,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" -[[package]] -name = "fancy-regex" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e1dacd0d2082dfcf1351c4bdd566bbe89a2b263235a2b50058f1e130a47277" -dependencies = [ - "bit-set 0.8.0", - "regex-automata", - "regex-syntax", -] - [[package]] name = "fast-glob" version = "1.1.0" @@ -2205,17 +2139,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "fluent-uri" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc74ac4d8359ae70623506d512209619e5cf8f347124910440dbc221714b328e" -dependencies = [ - "borrow-or-share", - "ref-cast", - "serde", -] - [[package]] name = "fnv" version = "1.0.7" @@ -2258,16 +2181,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fraction" -version = "0.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076045bb43dac435333ed5f04caf35c7463631d0dae2deb2638d94dd0a5b872" -dependencies = [ - "lazy_static", - "num", -] - [[package]] name = "fspy" version = "0.1.0" @@ -2555,20 +2468,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi 5.3.0", - "wasip2", - "wasm-bindgen", -] - [[package]] name = "getrandom" version = "0.4.3" @@ -2578,7 +2477,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi 6.0.0", + "r-efi", "rand_core 0.10.1", "wasm-bindgen", ] @@ -2593,12 +2492,6 @@ dependencies = [ "polyval", ] -[[package]] -name = "glob" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" - [[package]] name = "globset" version = "0.4.19" @@ -3066,12 +2959,9 @@ dependencies = [ [[package]] name = "infer" -version = "0.19.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" -dependencies = [ - "cfb", -] +checksum = "f4200d433cbd5178df7797c9c2e75b348b728e39631cf14520d1e2fc424201f4" [[package]] name = "inotify" @@ -3282,42 +3172,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "jsonschema" -version = "0.46.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a699d3e77675e6aa4bfffe3b907c8b5f7ed3241f9965bffb25475ad4b08d05" -dependencies = [ - "ahash", - "bytecount", - "data-encoding", - "email_address", - "fancy-regex", - "fraction", - "getrandom 0.3.4", - "idna", - "itoa", - "jsonschema-regex", - "num-cmp", - "num-traits", - "percent-encoding", - "referencing", - "regex", - "serde", - "serde_json", - "unicode-general-category", - "uuid-simd", -] - -[[package]] -name = "jsonschema-regex" -version = "0.46.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbd1086b01b9349fd4ef9a07433965af64c8ce8159abe633a189e4ff817bd13" -dependencies = [ - "regex-syntax", -] - [[package]] name = "junction" version = "1.4.2" @@ -3634,12 +3488,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "micromap" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a86d3146ed3995b5913c414f6664344b9617457320782e64f0bb44afd49d74" - [[package]] name = "miette" version = "7.6.0" @@ -4009,12 +3857,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "num-cmp" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" - [[package]] name = "num-complex" version = "0.4.6" @@ -4283,7 +4125,6 @@ dependencies = [ "oxc_allocator", "oxc_ast", "oxc_ast_visit", - "oxc_cfg", "oxc_codegen", "oxc_diagnostics", "oxc_isolated_declarations", @@ -4370,20 +4211,6 @@ dependencies = [ "oxc_syntax", ] -[[package]] -name = "oxc_cfg" -version = "0.146.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c337a2633a9e2d69d52e1594c401bc523cc2f9dada676c2c440f601420c4110" -dependencies = [ - "bitflags 2.13.1", - "itertools 0.15.0", - "oxc_index", - "oxc_syntax", - "petgraph 0.8.3", - "rustc-hash", -] - [[package]] name = "oxc_codegen" version = "0.146.0" @@ -4701,7 +4528,6 @@ dependencies = [ "oxc_allocator", "oxc_ast", "oxc_ast_visit", - "oxc_cfg", "oxc_data_structures", "oxc_diagnostics", "oxc_ecmascript", @@ -5563,12 +5389,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - [[package]] name = "r-efi" version = "6.0.0" @@ -5709,23 +5529,6 @@ dependencies = [ "syn 3.0.3", ] -[[package]] -name = "referencing" -version = "0.46.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fbf332a2f81899f6836f22c03da73dae8a664c32e3016b84692c23cddadc95d" -dependencies = [ - "ahash", - "fluent-uri", - "getrandom 0.3.4", - "hashbrown 0.16.1", - "itoa", - "micromap", - "parking_lot", - "percent-encoding", - "serde_json", -] - [[package]] name = "regex" version = "1.13.1" @@ -5765,12 +5568,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "relative-path" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" - [[package]] name = "replace_with" version = "0.1.8" @@ -5860,12 +5657,9 @@ dependencies = [ "append-only-vec", "arcstr", "bitflags 2.13.1", - "cow-utils", "dashmap", - "dunce", "futures", "indexmap", - "insta", "itertools 0.15.0", "itoa", "json-escape-simd", @@ -5878,7 +5672,6 @@ dependencies = [ "oxc_traverse", "petgraph 0.8.3", "rayon", - "regex", "rolldown_common", "rolldown_dev_common", "rolldown_devtools", @@ -5897,22 +5690,17 @@ dependencies = [ "rolldown_resolver", "rolldown_sourcemap", "rolldown_std_utils", - "rolldown_testing", "rolldown_tracing", "rolldown_utils", - "rolldown_workspace", "rustc-hash", "serde", "serde_json", - "serde_yaml", "smallvec 1.15.2", "string_wizard", "sugar_path", - "testing_macros", "tokio", "tracing", "url", - "walkdir", "xxhash-rust", ] @@ -6029,7 +5817,6 @@ dependencies = [ "rolldown_plugin_vite_web_worker_post", "rolldown_sourcemap", "rolldown_tracing", - "rolldown_tracking_allocator", "rolldown_utils", "rolldown_watcher", "rustc-hash", @@ -6051,7 +5838,6 @@ dependencies = [ "dashmap", "derive_more", "fast-glob", - "insta", "itertools 0.15.0", "num-bigint 0.5.1", "oxc", @@ -6067,7 +5853,6 @@ dependencies = [ "rolldown_std_utils", "rolldown_utils", "rustc-hash", - "schemars", "serde", "serde_json", "simdutf8", @@ -6091,7 +5876,6 @@ dependencies = [ "rolldown_error", "rolldown_fs_watcher", "rolldown_utils", - "rolldown_workspace", "rustc-hash", "sugar_path", "tokio", @@ -6106,8 +5890,6 @@ dependencies = [ "rolldown_common", "rolldown_error", "rolldown_utils", - "schemars", - "serde", ] [[package]] @@ -6309,9 +6091,7 @@ dependencies = [ "rolldown_common", "rolldown_error", "rolldown_plugin", - "rolldown_testing", "sugar_path", - "testing_macros", ] [[package]] @@ -6346,12 +6126,9 @@ dependencies = [ "oxc", "regex", "regress", - "rolldown", "rolldown_plugin", - "rolldown_testing", "rustc-hash", "string_wizard", - "tokio", ] [[package]] @@ -6580,7 +6357,6 @@ dependencies = [ name = "rolldown_sourcemap" version = "1.2.5" dependencies = [ - "criterion2", "memchr", "oxc", "oxc_sourcemap", @@ -6594,60 +6370,14 @@ dependencies = [ "sugar_path", ] -[[package]] -name = "rolldown_testing" -version = "0.1.0" -dependencies = [ - "anyhow", - "dunce", - "glob", - "insta", - "json-strip-comments", - "jsonschema", - "oxc", - "percent-encoding", - "regex", - "rolldown", - "rolldown_common", - "rolldown_dev", - "rolldown_error", - "rolldown_sourcemap", - "rolldown_testing_config", - "rolldown_workspace", - "rustc-hash", - "schemars", - "serde_json", - "sugar_path", - "tokio", -] - -[[package]] -name = "rolldown_testing_config" -version = "0.1.0" -dependencies = [ - "rolldown_common", - "rolldown_dev_common", - "schemars", - "serde", - "serde_json", -] - [[package]] name = "rolldown_tracing" version = "1.2.5" dependencies = [ "tracing", - "tracing-chrome", "tracing-subscriber", ] -[[package]] -name = "rolldown_tracking_allocator" -version = "0.0.1" -dependencies = [ - "mimalloc-safe", -] - [[package]] name = "rolldown_utils" version = "1.2.5" @@ -6658,7 +6388,6 @@ dependencies = [ "base-encode", "base64-simd", "cow-utils", - "criterion2", "dashmap", "fast-glob", "form_urlencoded", @@ -6700,16 +6429,10 @@ dependencies = [ "rolldown_error", "rolldown_fs_watcher", "rolldown_utils", - "rolldown_workspace", - "sugar_path", "tokio", "tracing", ] -[[package]] -name = "rolldown_workspace" -version = "1.2.5" - [[package]] name = "ropey" version = "1.6.1" @@ -7543,7 +7266,6 @@ dependencies = [ name = "string_wizard" version = "1.2.5" dependencies = [ - "insta", "memchr", "oxc_index", "oxc_sourcemap", @@ -7764,22 +7486,6 @@ dependencies = [ "test-log-core", ] -[[package]] -name = "testing_macros" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7442bd3ca09f38d4788dc5ebafbc1967c3717726b4b074db011d470b353548b" -dependencies = [ - "anyhow", - "glob", - "once_cell", - "proc-macro2", - "quote", - "regex", - "relative-path", - "syn 2.0.119", -] - [[package]] name = "textwrap" version = "0.16.2" @@ -8038,17 +7744,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "tracing-chrome" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf0a738ed5d6450a9fb96e86a23ad808de2b727fd1394585da5cdd6788ffe724" -dependencies = [ - "serde_json", - "tracing-core", - "tracing-subscriber", -] - [[package]] name = "tracing-core" version = "0.1.36" @@ -8093,7 +7788,6 @@ dependencies = [ "serde", "serde_json", "sharded-slab", - "smallvec 1.15.2", "thread_local", "tracing", "tracing-core", @@ -8211,12 +7905,6 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" -[[package]] -name = "unicode-general-category" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b993bddc193ae5bd0d623b49ec06ac3e9312875fdae725a975c51db1cc1677f" - [[package]] name = "unicode-id-start" version = "1.4.0" @@ -8337,16 +8025,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "uuid-simd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b082222b4f6619906941c17eb2297fff4c2fb96cb60164170522942a200bd8" -dependencies = [ - "outref", - "vsimd", -] - [[package]] name = "valuable" version = "0.1.1" @@ -9054,15 +8732,6 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasip2" -version = "1.0.4+wasi-0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" -dependencies = [ - "wit-bindgen", -] - [[package]] name = "wasm-bindgen" version = "0.2.126" @@ -9484,12 +9153,6 @@ version = "0.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcfff8264c3af1b0352006934e2265365ecb5a145aee88e770dc8b82e0456f4f" -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index e30978e759..12d9d38de7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,12 @@ [workspace] resolver = "3" members = ["bench", "crates/*", "packages/cli/binding"] -# vp_trampoline is a standalone package. +# vp_trampoline is a standalone package. Rolldown is a pinned source dependency +# with its own workspace and lint policy. # It needs a separate release profile and a crate-local build-std config. # Cargo ignores `panic` in per-package profile overrides. # See crates/vp_trampoline/Cargo.toml. -exclude = ["crates/vp_trampoline"] +exclude = ["crates/vp_trampoline", "rolldown"] [workspace.metadata.cargo-shear] ignored = [ @@ -26,8 +27,10 @@ rust-version = "1.92.0" [workspace.lints.rust] absolute_paths_not_starting_with_crate = "warn" +dead_code = "deny" non_ascii_idents = "warn" unit_bindings = "warn" +unreachable_pub = "deny" unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage)', 'cfg(coverage_nightly)'] } unsafe_op_in_unsafe_fn = "warn" unused_unsafe = "warn" diff --git a/crates/vp_cli_snapshots/src/bin/vpt/backpressure_run.rs b/crates/vp_cli_snapshots/src/bin/vpt/backpressure_run.rs index a489795257..dbcfdbe13f 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/backpressure_run.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/backpressure_run.rs @@ -30,7 +30,7 @@ struct Options { /// The reader consumes one small chunk whenever the channel fills. That lets a /// blocking writer advance while keeping enough pressure for a non-blocking /// writer to encounter `EAGAIN`. -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { let options = parse_options(args)?; let (reader_fd, writer_fd) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())?; diff --git a/crates/vp_cli_snapshots/src/bin/vpt/barrier.rs b/crates/vp_cli_snapshots/src/bin/vpt/barrier.rs index 4f5772a1b1..bc5906f581 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/barrier.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/barrier.rs @@ -7,7 +7,7 @@ /// Options: /// - `--exit=`: Exit with the given code after the barrier is met. /// - `--hang`: Keep process alive after the barrier (for kill tests). -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { let mut positional: Vec<&str> = Vec::new(); let mut exit_code: i32 = 0; let mut hang = false; diff --git a/crates/vp_cli_snapshots/src/bin/vpt/check_tty.rs b/crates/vp_cli_snapshots/src/bin/vpt/check_tty.rs index 0c3f0a7319..f272f413ac 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/check_tty.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/check_tty.rs @@ -1,4 +1,4 @@ -pub fn run() { +pub(super) fn run() { use std::io::IsTerminal as _; let stdin_tty = if std::io::stdin().is_terminal() { "tty" } else { "not-tty" }; let stdout_tty = if std::io::stdout().is_terminal() { "tty" } else { "not-tty" }; diff --git a/crates/vp_cli_snapshots/src/bin/vpt/chmod.rs b/crates/vp_cli_snapshots/src/bin/vpt/chmod.rs index 8eac04ece0..935524d730 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/chmod.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/chmod.rs @@ -4,7 +4,7 @@ /// mode (for example, `chmod +x hook.mjs`). Windows /// treats it as a validated no-op: the mode and target are still checked, /// so a typo or a failed earlier setup step fails on every platform. -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { if args.len() != 2 { return Err("Usage: vpt chmod |+x ".into()); } diff --git a/crates/vp_cli_snapshots/src/bin/vpt/cp.rs b/crates/vp_cli_snapshots/src/bin/vpt/cp.rs index 8d9cba6865..d53a3f8b62 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/cp.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/cp.rs @@ -1,4 +1,4 @@ -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { let (recursive, paths) = match args { [flag, src, dst] if flag == "-r" => (true, [src.as_str(), dst.as_str()]), [src, dst] => (false, [src.as_str(), dst.as_str()]), diff --git a/crates/vp_cli_snapshots/src/bin/vpt/exit.rs b/crates/vp_cli_snapshots/src/bin/vpt/exit.rs index eb33608acd..f8cb4e11b4 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/exit.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/exit.rs @@ -1,4 +1,4 @@ -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { let code: i32 = args.first().map(|s| s.parse()).transpose()?.unwrap_or(0); std::process::exit(code); } diff --git a/crates/vp_cli_snapshots/src/bin/vpt/exit_code.rs b/crates/vp_cli_snapshots/src/bin/vpt/exit_code.rs index 908fc1cfc3..ad2bb4ddd3 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/exit_code.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/exit_code.rs @@ -4,7 +4,7 @@ use std::process::ExitStatus; /// Depending on `vp_shared` here would pull its full dependency tree into /// this test-only crate and enable serde_json's `preserve_order` feature via /// unification, changing `vpt json-edit` output order. -pub fn exit_code_from_status(status: ExitStatus) -> i32 { +pub(super) fn exit_code_from_status(status: ExitStatus) -> i32 { #[cfg(unix)] { use std::os::unix::process::ExitStatusExt; diff --git a/crates/vp_cli_snapshots/src/bin/vpt/exit_on_ctrlc.rs b/crates/vp_cli_snapshots/src/bin/vpt/exit_on_ctrlc.rs index 7a6b4ce0a8..2b216c19d3 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/exit_on_ctrlc.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/exit_on_ctrlc.rs @@ -4,7 +4,7 @@ use std::sync::Arc; /// /// Sets up a Ctrl+C handler, emits a "ready" milestone, then waits. /// When Ctrl+C is received, prints "ctrl-c received" and exits. -pub fn run() -> Result<(), Box> { +pub(super) fn run() -> Result<(), Box> { // On Windows, an ancestor process (e.g. cargo, the test runner) may have // been created with CREATE_NEW_PROCESS_GROUP, which implicitly calls // SetConsoleCtrlHandler(NULL, TRUE) and sets CONSOLE_IGNORE_CTRL_C in the diff --git a/crates/vp_cli_snapshots/src/bin/vpt/grep_file.rs b/crates/vp_cli_snapshots/src/bin/vpt/grep_file.rs index 2a21f89c0a..50d6361732 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/grep_file.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/grep_file.rs @@ -3,7 +3,7 @@ /// Prints found/missing for the snapshot and keeps grep's exit semantics: /// nonzero when the pattern is absent or the file is unreadable, so /// content guards short-circuit under the runner's failure flow. -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { let [path, pattern] = args else { return Err("Usage: vpt grep-file ".into()); }; diff --git a/crates/vp_cli_snapshots/src/bin/vpt/json_edit.rs b/crates/vp_cli_snapshots/src/bin/vpt/json_edit.rs index 689ffe3750..6b4c6112d9 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/json_edit.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/json_edit.rs @@ -3,7 +3,7 @@ /// Sets a (possibly nested) key in a JSON file. `value` is parsed as JSON; /// if that fails it is treated as a plain string. Intermediate objects are /// created as needed. -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { let [file, dot_path, raw_value] = args else { return Err("Usage: vpt json-edit ".into()); }; diff --git a/crates/vp_cli_snapshots/src/bin/vpt/list_dir.rs b/crates/vp_cli_snapshots/src/bin/vpt/list_dir.rs index 0cf5d38f5e..b22db8cbcf 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/list_dir.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/list_dir.rs @@ -14,7 +14,7 @@ /// Used by e2e tests to assert on cache directory contents (e.g. exactly one /// `.tar.zst` archive after a re-run that should have cleaned up the prior /// archive). -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { let mut dir: Option<&str> = None; let mut ext: Option<&str> = None; let mut recursive = false; diff --git a/crates/vp_cli_snapshots/src/bin/vpt/mkdir.rs b/crates/vp_cli_snapshots/src/bin/vpt/mkdir.rs index 56adffbe41..3a11afe66b 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/mkdir.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/mkdir.rs @@ -1,4 +1,4 @@ -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { let mut parents = false; let mut paths = Vec::new(); for arg in args { diff --git a/crates/vp_cli_snapshots/src/bin/vpt/pipe_stdin.rs b/crates/vp_cli_snapshots/src/bin/vpt/pipe_stdin.rs index b04506bc77..e66b6ecdab 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/pipe_stdin.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/pipe_stdin.rs @@ -2,7 +2,7 @@ /// /// Spawns `` with `` piped to its stdin, then exits with /// the child's exit code. If `` is empty, an empty stdin is provided. -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { let sep = args .iter() .position(|a| a == "--") diff --git a/crates/vp_cli_snapshots/src/bin/vpt/print.rs b/crates/vp_cli_snapshots/src/bin/vpt/print.rs index e434e6cf88..4e2a5fc2a6 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/print.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/print.rs @@ -1,3 +1,3 @@ -pub fn run(args: &[String]) { +pub(super) fn run(args: &[String]) { println!("{}", args.join(" ")); } diff --git a/crates/vp_cli_snapshots/src/bin/vpt/print_color.rs b/crates/vp_cli_snapshots/src/bin/vpt/print_color.rs index d48f06f661..99b8531179 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/print_color.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/print_color.rs @@ -2,7 +2,7 @@ //! escape sequence when `FORCE_COLOR` is set to a non-zero value, otherwise //! prints plain text. Used by e2e tests to verify color-env handling. -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { if args.len() < 2 { return Err("Usage: vpt print-color ...".into()); } diff --git a/crates/vp_cli_snapshots/src/bin/vpt/print_cwd.rs b/crates/vp_cli_snapshots/src/bin/vpt/print_cwd.rs index 55db04cb88..4015566930 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/print_cwd.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/print_cwd.rs @@ -1,4 +1,4 @@ -pub fn run() -> Result<(), Box> { +pub(super) fn run() -> Result<(), Box> { let cwd = std::env::current_dir()?; println!("{}", cwd.display()); Ok(()) diff --git a/crates/vp_cli_snapshots/src/bin/vpt/print_env.rs b/crates/vp_cli_snapshots/src/bin/vpt/print_env.rs index 9bf8e1a8df..787ba8a94c 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/print_env.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/print_env.rs @@ -1,4 +1,4 @@ -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { if args.is_empty() { return Err("Usage: vpt print-env ".into()); } diff --git a/crates/vp_cli_snapshots/src/bin/vpt/print_file.rs b/crates/vp_cli_snapshots/src/bin/vpt/print_file.rs index 102d450e3e..38a7a8816b 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/print_file.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/print_file.rs @@ -3,7 +3,7 @@ /// Prints each file's bytes like cat, and like cat exits nonzero when any /// operand is missing, so migrated `cat` assertions keep their shell exit /// semantics under the runner's failure flow. -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { use std::io::Write as _; let stdout = std::io::stdout(); let mut out = stdout.lock(); diff --git a/crates/vp_cli_snapshots/src/bin/vpt/print_native_path.rs b/crates/vp_cli_snapshots/src/bin/vpt/print_native_path.rs index 571936f559..5db02df6f7 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/print_native_path.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/print_native_path.rs @@ -4,7 +4,7 @@ /// payload for asserting the runner's separator normalization: snapshots /// must show forward slashes on every platform even though tools print /// OS-native separators. -pub fn run(args: &[String]) { +pub(super) fn run(args: &[String]) { for arg in args { println!("{}", arg.replace('/', std::path::MAIN_SEPARATOR_STR)); } diff --git a/crates/vp_cli_snapshots/src/bin/vpt/probe.rs b/crates/vp_cli_snapshots/src/bin/vpt/probe.rs index 4a29135b3f..2a0e7445bd 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/probe.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/probe.rs @@ -4,7 +4,7 @@ /// synchronization, keystroke delivery, and screen capture end-to-end without /// requiring milestone instrumentation in the product CLI. Prints a question, /// marks `probe:ask`, reads a line, greets, then marks `probe:done`. -pub fn run() -> Result<(), Box> { +pub(super) fn run() -> Result<(), Box> { use std::io::{BufRead as _, Write as _}; println!("What is your name?"); diff --git a/crates/vp_cli_snapshots/src/bin/vpt/read_stdin.rs b/crates/vp_cli_snapshots/src/bin/vpt/read_stdin.rs index 1072df52ce..7d58cede76 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/read_stdin.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/read_stdin.rs @@ -1,4 +1,4 @@ -pub fn run() -> Result<(), Box> { +pub(super) fn run() -> Result<(), Box> { use std::io::{Read as _, Write as _}; let mut stdin = std::io::stdin().lock(); let mut stdout = std::io::stdout().lock(); diff --git a/crates/vp_cli_snapshots/src/bin/vpt/replace_file_content.rs b/crates/vp_cli_snapshots/src/bin/vpt/replace_file_content.rs index 58fc80df41..fe13484fde 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/replace_file_content.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/replace_file_content.rs @@ -1,4 +1,4 @@ -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { if args.len() < 3 { return Err("Usage: vpt replace-file-content ".into()); } diff --git a/crates/vp_cli_snapshots/src/bin/vpt/rm.rs b/crates/vp_cli_snapshots/src/bin/vpt/rm.rs index 6d2662b372..70ce1f9f81 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/rm.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/rm.rs @@ -1,4 +1,4 @@ -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { let mut recursive = false; let mut force = false; let mut paths = Vec::new(); diff --git a/crates/vp_cli_snapshots/src/bin/vpt/stat_file.rs b/crates/vp_cli_snapshots/src/bin/vpt/stat_file.rs index f6a61efed3..1d17556d00 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/stat_file.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/stat_file.rs @@ -7,7 +7,7 @@ /// assert flags (states: `file`, `dir`, `symlink`, `missing`) additionally /// fail the step on mismatch, preserving shell `test` guard semantics under /// the runner's line-boundary failure flow. -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { let mut paths: Vec<&str> = Vec::new(); let mut expect: Option<(&str, bool)> = None; let mut i = 0; diff --git a/crates/vp_cli_snapshots/src/bin/vpt/touch_file.rs b/crates/vp_cli_snapshots/src/bin/vpt/touch_file.rs index 04d51efec3..676ee18272 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/touch_file.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/touch_file.rs @@ -1,4 +1,4 @@ -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { if args.is_empty() { return Err("Usage: vpt touch-file ...".into()); } diff --git a/crates/vp_cli_snapshots/src/bin/vpt/write_file.rs b/crates/vp_cli_snapshots/src/bin/vpt/write_file.rs index c883f6ea11..3c52020845 100644 --- a/crates/vp_cli_snapshots/src/bin/vpt/write_file.rs +++ b/crates/vp_cli_snapshots/src/bin/vpt/write_file.rs @@ -1,4 +1,4 @@ -pub fn run(args: &[String]) -> Result<(), Box> { +pub(super) fn run(args: &[String]) -> Result<(), Box> { if args.len() < 2 { return Err("Usage: vpt write-file ".into()); } diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/flavor.rs b/crates/vp_cli_snapshots/tests/cli_snapshots/flavor.rs index d786f786c3..47ad426ea6 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/flavor.rs +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/flavor.rs @@ -14,13 +14,13 @@ use std::path::{Path, PathBuf}; #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Deserialize)] #[serde(rename_all = "lowercase")] -pub enum Flavor { +pub(super) enum Flavor { Local, Global, } impl Flavor { - pub const fn as_str(self) -> &'static str { + pub(super) const fn as_str(self) -> &'static str { match self { Self::Local => "local", Self::Global => "global", @@ -29,42 +29,42 @@ impl Flavor { } /// Everything the runner needs to spawn commands under one flavor. -pub struct FlavorRuntime { - pub runner_bin_dir: PathBuf, - pub vpt: PathBuf, +pub(super) struct FlavorRuntime { + pub(super) runner_bin_dir: PathBuf, + pub(super) vpt: PathBuf, /// Runner-owned POSIX shell binaries used by fixtures that execute the /// generated `env` file. - pub sh: Option, - pub bash: Option, - pub zsh: Option, + pub(super) sh: Option, + pub(super) bash: Option, + pub(super) zsh: Option, /// Runner-owned Fish binary used by fixtures that execute generated /// `env.fish` files. CI supplies it through `VP_SNAP_FISH_BIN`. - pub fish: Option, + pub(super) fish: Option, /// Runner-owned Nushell binary used by fixtures that execute generated /// `env.nu` files. CI supplies it through `VP_SNAP_NU_BIN`. - pub nu: Option, + pub(super) nu: Option, /// Canonical PowerShell binary used by fixtures that execute generated /// `env.ps1` files. CI supplies it through `VP_SNAP_PWSH_BIN`. - pub pwsh: Option, + pub(super) pwsh: Option, /// Canonical system cmd.exe used by fixtures that execute generated batch /// files. CI supplies it through `VP_SNAP_CMD_BIN`; it is not relocated. - pub cmd: Option, + pub(super) cmd: Option, /// Source global `vp` binary to install into each case's `VP_HOME/current`. - pub global_vp: PathBuf, + pub(super) global_vp: PathBuf, /// Source package installed into each case's `VP_HOME/current/node_modules`. - pub cli_package_dir: PathBuf, + pub(super) cli_package_dir: PathBuf, } /// The runner crate's manifest dir. The runtime env var wins: cargo sets it /// for test processes, and nextest rewrites it when running a relocated /// archive (`--workspace-remap`), where the compile-time path is a /// build-machine path that no longer exists. -pub fn manifest_dir() -> PathBuf { +pub(super) fn manifest_dir() -> PathBuf { std::env::var_os("CARGO_MANIFEST_DIR") .map_or_else(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")), PathBuf::from) } -pub fn repo_root() -> PathBuf { +pub(super) fn repo_root() -> PathBuf { manifest_dir().parent().unwrap().parent().unwrap().to_path_buf() } @@ -227,38 +227,38 @@ fn optional_tool_path(env_var: &str, binary: &str) -> Result, St /// Resolves an optional Fish binary for fixtures that exercise generated /// `env.fish` files. -pub fn fish_path() -> Result, String> { +pub(super) fn fish_path() -> Result, String> { optional_tool_path("VP_SNAP_FISH_BIN", "fish") } -pub fn sh_path() -> Result, String> { +pub(super) fn sh_path() -> Result, String> { optional_tool_path("VP_SNAP_SH_BIN", "sh") } -pub fn bash_path() -> Result, String> { +pub(super) fn bash_path() -> Result, String> { optional_tool_path("VP_SNAP_BASH_BIN", "bash") } -pub fn zsh_path() -> Result, String> { +pub(super) fn zsh_path() -> Result, String> { optional_tool_path("VP_SNAP_ZSH_BIN", "zsh") } /// Resolves an optional Nushell binary for fixtures that exercise generated /// `env.nu` files. -pub fn nushell_path() -> Result, String> { +pub(super) fn nushell_path() -> Result, String> { optional_tool_path("VP_SNAP_NU_BIN", "nu") } /// Resolves an optional PowerShell binary for fixtures that exercise generated /// `env.ps1` files. -pub fn powershell_path() -> Result, String> { +pub(super) fn powershell_path() -> Result, String> { optional_tool_path("VP_SNAP_PWSH_BIN", "pwsh") } /// Resolves an optional cmd.exe for fixtures that exercise generated batch /// files. Keep the canonical system executable in place instead of copying it /// into the runner bin directory. -pub fn cmd_path() -> Result, String> { +pub(super) fn cmd_path() -> Result, String> { let path = optional_tool_path("VP_SNAP_CMD_BIN", "cmd.exe")?; path.map(|path| { std::fs::canonicalize(&path) @@ -269,8 +269,8 @@ pub fn cmd_path() -> Result, String> { /// Home-layout names, shared with `CaseHome` in main.rs so the product's /// `~/.vite-plus/js_runtime` layout is spelled once. -pub const VP_HOME_DIR: &str = ".vite-plus"; -pub const JS_RUNTIME_DIR: &str = "js_runtime"; +pub(super) const VP_HOME_DIR: &str = ".vite-plus"; +pub(super) const JS_RUNTIME_DIR: &str = "js_runtime"; /// Directory holding an already-provisioned managed JS runtime that each /// case's `VP_HOME` is seeded with (symlinked, read-mostly). Without a seed, @@ -279,7 +279,7 @@ pub const JS_RUNTIME_DIR: &str = "js_runtime"; /// there); defaults to the developer's real `~/.vite-plus/js_runtime`. /// Cases that test runtime provisioning itself opt out via /// `seed-runtime = false`. -pub fn js_runtime_seed_dir() -> Option { +pub(super) fn js_runtime_seed_dir() -> Option { if let Some(dir) = std::env::var_os("VP_SNAP_JS_RUNTIME_DIR") { let dir = PathBuf::from(dir); return dir.is_dir().then_some(dir); @@ -293,7 +293,7 @@ pub fn js_runtime_seed_dir() -> Option { /// node shim (which resolves a project-pinned version from its cwd). Used for /// runner infrastructure like the local-registry server, which needs a /// TypeScript-capable Node regardless of what the fixture under test pins. -pub fn seed_runtime_node() -> Option { +pub(super) fn seed_runtime_node() -> Option { let node_root = js_runtime_seed_dir()?.join("node"); let mut versions: Vec<(Vec, PathBuf)> = std::fs::read_dir(&node_root) .ok()? @@ -339,7 +339,7 @@ fn install_runner_tool(bin_dir: &Path, name: &str, target: &Path) -> Result Result<(), String> { +pub(super) fn install_file(dest: &Path, source: &Path, label: &str) -> Result<(), String> { let source = std::fs::canonicalize(source).unwrap_or_else(|_| source.to_path_buf()); std::fs::hard_link(&source, dest) .or_else(|_| std::fs::copy(&source, dest).map(|_| ())) @@ -348,7 +348,7 @@ pub fn install_file(dest: &Path, source: &Path, label: &str) -> Result<(), Strin /// Best-effort directory link. On Windows, directory symlinks may require /// privileges, so a junction (which never does) is the fallback. -pub fn link_dir(target: &Path, link: &Path) { +pub(super) fn link_dir(target: &Path, link: &Path) { #[cfg(unix)] let _ = std::os::unix::fs::symlink(target, link); #[cfg(windows)] @@ -358,7 +358,7 @@ pub fn link_dir(target: &Path, link: &Path) { } /// Creates the per-run bin directory for `flavor` under `run_root`. -pub fn provision(flavor: Flavor, run_root: &Path) -> Result { +pub(super) fn provision(flavor: Flavor, run_root: &Path) -> Result { let runner_bin_dir = run_root.join(format!("bin-{}", flavor.as_str())); std::fs::create_dir_all(&runner_bin_dir) .map_err(|e| format!("failed to create bin dir: {e}"))?; diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/redact.rs b/crates/vp_cli_snapshots/tests/cli_snapshots/redact.rs index 70e661e3ad..1903958a99 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/redact.rs +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/redact.rs @@ -386,7 +386,7 @@ fn path_variants(path: &str, label: &'static str) -> Vec<(String, &'static str)> clippy::disallowed_types, reason = "String required by regex replace_all and cow_replace APIs" )] -pub fn redact_output( +pub(super) fn redact_output( mut output: String, paths: &[(&str, &'static str)], normalize_separators: bool, @@ -601,7 +601,7 @@ pub fn redact_output( /// only for steps it identifies as version probes, so fixture-controlled bare /// versions elsewhere (a printed `.node-version` file) stay assertable. #[expect(clippy::disallowed_types, reason = "String required by regex replace_all API")] -pub fn redact_version_probe_output(output: String) -> String { +pub(super) fn redact_version_probe_output(output: String) -> String { BARE_VERSION_BLOCK_RE.replace_all(&output, "${1}${2}").into_owned() } diff --git a/crates/vp_command/src/lib.rs b/crates/vp_command/src/lib.rs index a8bcd09d72..689f08af98 100644 --- a/crates/vp_command/src/lib.rs +++ b/crates/vp_command/src/lib.rs @@ -352,7 +352,7 @@ where } #[cfg(unix)] -pub fn fix_stdio_streams() { +fn fix_stdio_streams() { // libuv may mark stdin/stdout/stderr as close-on-exec, which interferes with Rust's subprocess spawning. // As a workaround, we clear the FD_CLOEXEC flag on these file descriptors to prevent them from being closed when spawning child processes. // diff --git a/crates/vp_command/src/ps1_shim.rs b/crates/vp_command/src/ps1_shim.rs index 7d1d530c3b..e1325bec89 100644 --- a/crates/vp_command/src/ps1_shim.rs +++ b/crates/vp_command/src/ps1_shim.rs @@ -47,7 +47,7 @@ use vt_powershell::{POWERSHELL_PREFIX, find_ps1_sibling, is_stdin_terminal, powe /// - the resolved path is not a `.cmd` (case-insensitive), /// - the `.cmd` has no sibling `.ps1`. #[must_use] -pub fn rewrite_cmd_to_powershell( +pub(super) fn rewrite_cmd_to_powershell( resolved: &AbsolutePath, ) -> Option<(AbsolutePathBuf, Vec)> { // `build_command` gives its standard input to child processes. Thus, a TTY diff --git a/crates/vp_global_cli/src/cli.rs b/crates/vp_global_cli/src/cli.rs index 00f28b5bc8..d917206bf9 100644 --- a/crates/vp_global_cli/src/cli.rs +++ b/crates/vp_global_cli/src/cli.rs @@ -28,8 +28,8 @@ const DEFAULT_GLOBAL_INSTALL_CONCURRENCY: usize = 5; const DEFAULT_GLOBAL_VIEW_CONCURRENCY: usize = 3 * DEFAULT_GLOBAL_INSTALL_CONCURRENCY; #[derive(Clone, Copy, Debug)] -pub struct RenderOptions { - pub show_header: bool, +pub(crate) struct RenderOptions { + pub(crate) show_header: bool, } impl Default for RenderOptions { @@ -48,22 +48,22 @@ impl Default for RenderOptions { long_about = None )] #[command(disable_help_subcommand = true, disable_version_flag = true)] -pub struct Args { +pub(crate) struct Args { /// Print version #[arg(short = 'V', long = "version")] - pub version: bool, + pub(crate) version: bool, /// Run as if vp was started in instead of the current working directory #[arg(short = 'C', value_name = "DIR")] - pub chdir: Option, + pub(crate) chdir: Option, #[clap(subcommand)] - pub command: Option, + pub(crate) command: Option, } /// Available commands #[derive(Subcommand, Debug)] -pub enum Commands { +pub(crate) enum Commands { // ========================================================================= // Category A: Package Manager Commands // (clap-flattened from `vp_pm_cli::PackageManagerCommand` so the @@ -269,7 +269,7 @@ pub enum Commands { impl Commands { /// Whether the command was invoked with flags that request quiet or /// machine-readable output (--silent, -s, --json, --parseable, --format json/list). - pub fn is_quiet_or_machine_readable(&self) -> bool { + pub(crate) fn is_quiet_or_machine_readable(&self) -> bool { match self { Self::PackageManager(pm) => pm.is_quiet_or_machine_readable(), Self::Toolchain { json, .. } => *json, @@ -284,15 +284,15 @@ impl Commands { /// Arguments for the `env` command #[derive(clap::Args, Debug)] -pub struct EnvArgs { +pub(crate) struct EnvArgs { /// Subcommand (e.g., 'default', 'setup', 'doctor', 'which') #[command(subcommand)] - pub command: Option, + pub(crate) command: Option, } /// Subcommands for the `env` command #[derive(clap::Subcommand, Debug)] -pub enum EnvSubcommands { +pub(crate) enum EnvSubcommands { /// Show current environment information Current { /// Output in JSON format @@ -489,7 +489,7 @@ impl EnvSubcommands { /// Write target for `vp env pin` / `vp env unpin` (see rfcs/dev-engines.md) #[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)] -pub enum PinTarget { +pub(crate) enum PinTarget { /// Pin via the .node-version file NodeVersion, /// Pin via package.json#devEngines.runtime @@ -498,7 +498,7 @@ pub enum PinTarget { /// Version sorting order for list-remote command #[derive(clap::ValueEnum, Clone, Debug, Default)] -pub enum SortingMethod { +pub(crate) enum SortingMethod { /// Sort versions in ascending order (earliest to latest) #[default] Asc, @@ -951,12 +951,12 @@ fn prompt_reinstall_node_mismatches( /// Parsing resolves a subcommand to one clap variant, which does not record the /// spelling used, so it is read straight from the command line instead. #[must_use] -pub fn raw_subcommand(argv: &[String]) -> Option<&str> { +pub(crate) fn raw_subcommand(argv: &[String]) -> Option<&str> { argv.iter().skip(1).map(String::as_str).find(|arg| !arg.starts_with('-')) } /// Run the CLI command. -pub async fn run_command( +pub(crate) async fn run_command( cwd: AbsolutePathBuf, args: Args, raw_subcommand: Option<&str>, @@ -967,7 +967,7 @@ pub async fn run_command( } /// Run the CLI command with rendering options. -pub async fn run_command_with_options( +pub(crate) async fn run_command_with_options( mut cwd: AbsolutePathBuf, args: Args, render_options: RenderOptions, @@ -1164,12 +1164,12 @@ fn maybe_print_runtime_header(command: &str, args: &[String], show_header: bool) } /// Build a clap Command with custom help formatting matching the JS CLI output. -pub fn command_with_help() -> clap::Command { +pub(crate) fn command_with_help() -> clap::Command { command_with_help_with_options(RenderOptions::default()) } /// Build a clap Command with custom help formatting and rendering options. -pub fn command_with_help_with_options(render_options: RenderOptions) -> clap::Command { +pub(crate) fn command_with_help_with_options(render_options: RenderOptions) -> clap::Command { apply_custom_help(Args::command(), render_options) } @@ -1189,7 +1189,7 @@ fn apply_custom_help(cmd: clap::Command, render_options: RenderOptions) -> clap: /// Parse CLI arguments from a custom args iterator with custom help formatting. /// Returns `Err` with the clap error if parsing fails (e.g., unknown command). -pub fn try_parse_args_from( +pub(crate) fn try_parse_args_from( args: impl IntoIterator, ) -> Result { try_parse_args_from_with_options(args, RenderOptions::default()) @@ -1197,7 +1197,7 @@ pub fn try_parse_args_from( /// Parse CLI arguments from a custom args iterator with rendering options. /// Returns `Err` with the clap error if parsing fails (e.g., unknown command). -pub fn try_parse_args_from_with_options( +pub(crate) fn try_parse_args_from_with_options( args: impl IntoIterator, render_options: RenderOptions, ) -> Result { diff --git a/crates/vp_global_cli/src/command_picker.rs b/crates/vp_global_cli/src/command_picker.rs index 815fe9d88f..fcee3fecbc 100644 --- a/crates/vp_global_cli/src/command_picker.rs +++ b/crates/vp_global_cli/src/command_picker.rs @@ -22,13 +22,13 @@ const SELECTED_MARKER: &str = "›"; const UNSELECTED_MARKER: &str = " "; #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct PickedCommand { - pub command: &'static str, - pub append_help: bool, +pub(crate) struct PickedCommand { + pub(crate) command: &'static str, + pub(crate) append_help: bool, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum TopLevelCommandPick { +pub(crate) enum TopLevelCommandPick { Skipped, Selected(PickedCommand), Cancelled, @@ -126,7 +126,7 @@ const COMMANDS: &[CommandEntry] = &[ }, ]; -pub fn pick_top_level_command_if_interactive( +pub(crate) fn pick_top_level_command_if_interactive( cwd: &AbsolutePath, ) -> io::Result { if !vp_shared::is_interactive_terminal() { diff --git a/crates/vp_global_cli/src/commands/config.rs b/crates/vp_global_cli/src/commands/config.rs index eec99269b8..993dc9ead9 100644 --- a/crates/vp_global_cli/src/commands/config.rs +++ b/crates/vp_global_cli/src/commands/config.rs @@ -7,7 +7,7 @@ use vt_path::AbsolutePathBuf; use crate::error::Error; /// Execute the `config` command by delegating to local or global vite-plus. -pub async fn execute( +pub(crate) async fn execute( cwd: AbsolutePathBuf, args: &[String], raw_subcommand: Option<&str>, diff --git a/crates/vp_global_cli/src/commands/create.rs b/crates/vp_global_cli/src/commands/create.rs index a92751546e..007a976e7c 100644 --- a/crates/vp_global_cli/src/commands/create.rs +++ b/crates/vp_global_cli/src/commands/create.rs @@ -7,7 +7,7 @@ use vt_path::AbsolutePathBuf; use crate::error::Error; /// Execute the `create` command by delegating to local or global vite-plus. -pub async fn execute( +pub(crate) async fn execute( cwd: AbsolutePathBuf, args: &[String], raw_subcommand: Option<&str>, diff --git a/crates/vp_global_cli/src/commands/delegate.rs b/crates/vp_global_cli/src/commands/delegate.rs index bf2dee8eb7..d74fdf387e 100644 --- a/crates/vp_global_cli/src/commands/delegate.rs +++ b/crates/vp_global_cli/src/commands/delegate.rs @@ -10,7 +10,7 @@ use crate::{error::Error, js_executor::JsExecutor}; /// /// `raw_subcommand` is the subcommand as the user wrote it, which the local CLI /// cannot recover from `command` alone once parsing has resolved it. -pub async fn execute( +pub(crate) async fn execute( cwd: AbsolutePathBuf, command: &str, args: &[String], @@ -20,7 +20,7 @@ pub async fn execute( } /// Execute an app command and preserve whether the user supplied `-C`. -pub async fn execute_app( +pub(crate) async fn execute_app( cwd: AbsolutePathBuf, command: &str, args: &[String], @@ -36,7 +36,7 @@ pub async fn execute_app( } /// Execute a command by delegating to the local `vite-plus` CLI, capturing output. -pub async fn execute_output( +pub(crate) async fn execute_output( cwd: AbsolutePathBuf, command: &str, args: &[String], @@ -50,7 +50,7 @@ pub async fn execute_output( /// Execute a command by delegating to the global `vite-plus` CLI. /// /// `raw_subcommand` is the subcommand as the user wrote it; see [`execute`]. -pub async fn execute_global( +pub(crate) async fn execute_global( cwd: AbsolutePathBuf, command: &str, args: &[String], diff --git a/crates/vp_global_cli/src/commands/env/bin_config.rs b/crates/vp_global_cli/src/commands/env/bin_config.rs index cd21839200..46438f6ed6 100644 --- a/crates/vp_global_cli/src/commands/env/bin_config.rs +++ b/crates/vp_global_cli/src/commands/env/bin_config.rs @@ -15,7 +15,7 @@ use crate::error::Error; /// Source that installed a binary. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] -pub enum BinSource { +pub(crate) enum BinSource { /// Installed via `vp install -g` (managed shim) #[default] Vp, @@ -26,43 +26,48 @@ pub enum BinSource { /// Config for a single binary, stored at `/bins/{name}.json` #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct BinConfig { +pub(crate) struct BinConfig { /// Binary name - pub name: String, + pub(crate) name: String, /// Package that installed this binary - pub package: String, + pub(crate) package: String, /// Package version - pub version: String, + pub(crate) version: String, /// Node.js version used - pub node_version: String, + pub(crate) node_version: String, /// How this binary was installed #[serde(default)] - pub source: BinSource, + pub(crate) source: BinSource, } impl BinConfig { /// Create a new BinConfig with `Vp` source (used by `vp install -g`). - pub fn new(name: String, package: String, version: String, node_version: String) -> Self { + pub(crate) fn new( + name: String, + package: String, + version: String, + node_version: String, + ) -> Self { Self { name, package, version, node_version, source: BinSource::Vp } } /// Create a new BinConfig with `Npm` source (used by npm install -g interception). - pub fn new_npm(name: String, package: String, node_version: String) -> Self { + pub(crate) fn new_npm(name: String, package: String, node_version: String) -> Self { Self { name, package, version: String::new(), node_version, source: BinSource::Npm } } /// Get the bins directory path (`/bins`). - pub fn bins_dir() -> Result { + pub(crate) fn bins_dir() -> Result { Ok(vp_shared::EnvConfig::get().dirs.data.join("bins")) } /// Get the path to a binary's config file. - pub fn path(bin_name: &str) -> Result { + pub(crate) fn path(bin_name: &str) -> Result { Ok(Self::bins_dir()?.join(format!("{bin_name}.json"))) } /// Load config for a binary (synchronous). - pub fn load_sync(bin_name: &str) -> Result, Error> { + pub(crate) fn load_sync(bin_name: &str) -> Result, Error> { let path = Self::path(bin_name)?; match std::fs::read_to_string(path.as_path()) { Ok(content) => { @@ -77,7 +82,7 @@ impl BinConfig { } /// Save config for a binary (synchronous). - pub fn save_sync(&self) -> Result<(), Error> { + pub(crate) fn save_sync(&self) -> Result<(), Error> { let path = Self::path(&self.name)?; if let Some(parent) = path.parent() { std::fs::create_dir_all(parent)?; @@ -90,7 +95,7 @@ impl BinConfig { } /// Delete config for a binary (synchronous). - pub fn delete_sync(bin_name: &str) -> Result<(), Error> { + pub(crate) fn delete_sync(bin_name: &str) -> Result<(), Error> { let path = Self::path(bin_name)?; match std::fs::remove_file(path.as_path()) { Ok(()) => Ok(()), @@ -100,7 +105,7 @@ impl BinConfig { } /// Load config for a binary. - pub async fn load(bin_name: &str) -> Result, Error> { + pub(crate) async fn load(bin_name: &str) -> Result, Error> { let path = Self::path(bin_name)?; if !tokio::fs::try_exists(&path).await.unwrap_or(false) { return Ok(None); @@ -112,7 +117,7 @@ impl BinConfig { } /// Save config for a binary. - pub async fn save(&self) -> Result<(), Error> { + pub(crate) async fn save(&self) -> Result<(), Error> { let path = Self::path(&self.name)?; // Ensure bins directory exists @@ -128,7 +133,7 @@ impl BinConfig { } /// Delete config for a binary. - pub async fn delete(bin_name: &str) -> Result<(), Error> { + pub(crate) async fn delete(bin_name: &str) -> Result<(), Error> { let path = Self::path(bin_name)?; if tokio::fs::try_exists(&path).await.unwrap_or(false) { tokio::fs::remove_file(&path).await?; @@ -141,7 +146,7 @@ impl BinConfig { /// Used during shim refresh to discover package shims that need their /// trampoline executables updated after a vite-plus upgrade. #[cfg_attr(not(windows), allow(dead_code))] // Only called from #[cfg(windows)] refresh_package_shims - pub async fn find_all_vp_source() -> Result, Error> { + pub(crate) async fn find_all_vp_source() -> Result, Error> { Self::find_bins_where(|config| config.source == BinSource::Vp).await } @@ -149,7 +154,7 @@ impl BinConfig { /// /// This is used as a fallback during uninstall when PackageMetadata is missing /// (orphan recovery). - pub async fn find_by_package(package_name: &str) -> Result, Error> { + pub(crate) async fn find_by_package(package_name: &str) -> Result, Error> { Self::find_bins_where(|config| config.package == package_name).await } diff --git a/crates/vp_global_cli/src/commands/env/clean.rs b/crates/vp_global_cli/src/commands/env/clean.rs index 8faad4c0fc..56c29c0f16 100644 --- a/crates/vp_global_cli/src/commands/env/clean.rs +++ b/crates/vp_global_cli/src/commands/env/clean.rs @@ -12,7 +12,7 @@ use super::{config, list::list_installed_versions}; use crate::error::Error; /// Execute the clean command. -pub async fn execute(cwd: AbsolutePathBuf) -> Result { +pub(crate) async fn execute(cwd: AbsolutePathBuf) -> Result { let config = vp_shared::EnvConfig::get(); let data_dir = &config.dirs.data; let node_dir = data_dir.join("js_runtime").join("node"); diff --git a/crates/vp_global_cli/src/commands/env/config.rs b/crates/vp_global_cli/src/commands/env/config.rs index 8cb1bffc10..4f87324cdd 100644 --- a/crates/vp_global_cli/src/commands/env/config.rs +++ b/crates/vp_global_cli/src/commands/env/config.rs @@ -20,7 +20,7 @@ const CONFIG_FILE: &str = "config.json"; /// Shim mode determines how shims resolve tools. #[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, PartialEq, Eq)] #[serde(rename_all = "snake_case")] -pub enum ShimMode { +pub(crate) enum ShimMode { /// Shims always use vite-plus managed Node.js #[default] Managed, @@ -31,13 +31,13 @@ pub enum ShimMode { /// User configuration stored in `/config.json` #[derive(Serialize, Deserialize, Default, Debug)] #[serde(rename_all = "camelCase")] -pub struct Config { +pub(crate) struct Config { /// Default Node.js version when no project version file is found #[serde(default, skip_serializing_if = "Option::is_none")] - pub default_node_version: Option, + pub(crate) default_node_version: Option, /// Shim mode for tool resolution #[serde(default, skip_serializing_if = "is_default_shim_mode")] - pub shim_mode: ShimMode, + pub(crate) shim_mode: ShimMode, } /// Check if shim mode is the default (for skip_serializing_if) @@ -47,27 +47,27 @@ fn is_default_shim_mode(mode: &ShimMode) -> bool { /// Version resolution result #[derive(Debug)] -pub struct VersionResolution { +pub(crate) struct VersionResolution { /// The resolved version string (e.g., "20.18.0") - pub version: String, + pub(crate) version: String, /// The source of the version (e.g., ".node-version", "engines.node", "default") - pub source: String, + pub(crate) source: String, /// Path to the source file (if applicable) - pub source_path: Option, + pub(crate) source_path: Option, /// Project root directory (if version came from a project file) - pub project_root: Option, + pub(crate) project_root: Option, /// Whether the original version spec was a range (e.g., "20", "^20.0.0", "lts/*") /// Range versions should use time-based cache expiry instead of mtime-only validation - pub is_range: bool, + pub(crate) is_range: bool, } /// Get the bin directory path (``). -pub fn get_bin_dir() -> Result { +pub(crate) fn get_bin_dir() -> Result { Ok(vp_shared::EnvConfig::get().dirs.bin.clone()) } /// Get the packages directory path (`/packages`). -pub fn get_packages_dir() -> Result { +pub(crate) fn get_packages_dir() -> Result { Ok(vp_shared::EnvConfig::get().dirs.data.join("packages")) } @@ -79,7 +79,7 @@ pub fn get_packages_dir() -> Result { /// /// This function probes both paths and returns the one that exists, /// falling back to the platform default if neither exists. -pub fn get_node_modules_dir(prefix: &AbsolutePath, package_name: &str) -> AbsolutePathBuf { +pub(crate) fn get_node_modules_dir(prefix: &AbsolutePath, package_name: &str) -> AbsolutePathBuf { // Try Unix layout first (lib/node_modules) let unix_path = prefix.join("lib").join("node_modules").join(package_name); if unix_path.as_path().exists() { @@ -104,12 +104,12 @@ pub fn get_node_modules_dir(prefix: &AbsolutePath, package_name: &str) -> Absolu } /// Get the config file path (`/config.json`). -pub fn get_config_path() -> Result { +pub(crate) fn get_config_path() -> Result { Ok(vp_shared::EnvConfig::get().dirs.config.join(CONFIG_FILE)) } /// Load configuration from disk. -pub async fn load_config() -> Result { +pub(crate) async fn load_config() -> Result { let config_path = get_config_path()?; if !tokio::fs::try_exists(&config_path).await.unwrap_or(false) { @@ -122,7 +122,7 @@ pub async fn load_config() -> Result { } /// Save configuration to disk. -pub async fn save_config(config: &Config) -> Result<(), Error> { +pub(crate) async fn save_config(config: &Config) -> Result<(), Error> { let config_path = get_config_path()?; // Ensure directory exists @@ -137,18 +137,18 @@ pub async fn save_config(config: &Config) -> Result<(), Error> { /// Environment variable for per-shell session Node.js version override. /// Set by `vp env use` command. -pub const VERSION_ENV_VAR: &str = vp_shared::env_vars::VP_NODE_VERSION; +pub(crate) const VERSION_ENV_VAR: &str = vp_shared::env_vars::VP_NODE_VERSION; /// Session version file name, written by `vp env use` so shims work without the shell eval wrapper. -pub const SESSION_VERSION_FILE: &str = ".session-node-version"; +pub(crate) const SESSION_VERSION_FILE: &str = ".session-node-version"; /// Get the path to the session version file (`/.session-node-version`). -pub fn get_session_version_path() -> Result { +pub(crate) fn get_session_version_path() -> Result { Ok(vp_shared::EnvConfig::get().dirs.state.join(SESSION_VERSION_FILE)) } /// Read the session version file. Returns `None` if the file is missing or empty. -pub async fn read_session_version() -> Option { +pub(crate) async fn read_session_version() -> Option { let path = get_session_version_path().ok()?; let content = tokio::fs::read_to_string(&path).await.ok()?; let trimmed = content.trim().to_string(); @@ -156,7 +156,7 @@ pub async fn read_session_version() -> Option { } /// Read the session version file synchronously. Returns `None` if the file is missing or empty. -pub fn read_session_version_sync() -> Option { +pub(crate) fn read_session_version_sync() -> Option { let path = get_session_version_path().ok()?; let content = std::fs::read_to_string(path.as_path()).ok()?; let trimmed = content.trim().to_string(); @@ -164,7 +164,7 @@ pub fn read_session_version_sync() -> Option { } /// Write the resolved version to the session version file. -pub async fn write_session_version(version: &str) -> Result<(), Error> { +pub(crate) async fn write_session_version(version: &str) -> Result<(), Error> { let path = get_session_version_path()?; // Ensure parent directory exists if let Some(parent) = path.parent() { @@ -175,7 +175,7 @@ pub async fn write_session_version(version: &str) -> Result<(), Error> { } /// Delete the session version file. Ignores "not found" errors. -pub async fn delete_session_version() -> Result<(), Error> { +pub(crate) async fn delete_session_version() -> Result<(), Error> { let path = get_session_version_path()?; match tokio::fs::remove_file(&path).await { Ok(()) => Ok(()), @@ -195,7 +195,7 @@ pub async fn delete_session_version() -> Result<(), Error> { /// 5. `.nvmrc` file in current or parent directories /// 6. User default from config.json /// 7. Latest LTS version -pub async fn resolve_version(cwd: &AbsolutePath) -> Result { +pub(crate) async fn resolve_version(cwd: &AbsolutePath) -> Result { // Session override via environment variable (set by `vp env use`) if let Some(env_version) = vp_shared::EnvConfig::get().node_version.as_deref() { let env_version = env_version.trim(); @@ -225,10 +225,10 @@ pub async fn resolve_version(cwd: &AbsolutePath) -> Result Result { +pub(crate) async fn resolve_version_from_files( + cwd: &AbsolutePath, +) -> Result { let provider = NodeProvider::new(); if let Some(project_source) = resolve_project_version_source(cwd, true).await? { @@ -413,7 +415,7 @@ async fn resolve_version_string(version: &str, provider: &NodeProvider) -> Resul /// Resolve version alias (lts, latest) to an exact version. /// /// Wraps resolution errors with a user-friendly message showing valid examples. -pub async fn resolve_version_alias( +pub(crate) async fn resolve_version_alias( version: &str, provider: &NodeProvider, ) -> Result { diff --git a/crates/vp_global_cli/src/commands/env/current.rs b/crates/vp_global_cli/src/commands/env/current.rs index 94757c95c8..ff9112da80 100644 --- a/crates/vp_global_cli/src/commands/env/current.rs +++ b/crates/vp_global_cli/src/commands/env/current.rs @@ -71,7 +71,7 @@ fn print_rows(title: &str, rows: &[(&str, String)]) { } /// Execute the current command. -pub async fn execute(cwd: AbsolutePathBuf, json: bool) -> Result { +pub(crate) async fn execute(cwd: AbsolutePathBuf, json: bool) -> Result { let resolution = resolve_version(&cwd).await?; let package_manager = resolve_package_manager_info(&cwd); diff --git a/crates/vp_global_cli/src/commands/env/default.rs b/crates/vp_global_cli/src/commands/env/default.rs index 6638469eb7..b6cd2960c5 100644 --- a/crates/vp_global_cli/src/commands/env/default.rs +++ b/crates/vp_global_cli/src/commands/env/default.rs @@ -10,7 +10,10 @@ use super::config::{get_config_path, load_config, save_config}; use crate::error::Error; /// Execute the default command. -pub async fn execute(_cwd: AbsolutePathBuf, version: Option) -> Result { +pub(crate) async fn execute( + _cwd: AbsolutePathBuf, + version: Option, +) -> Result { match version { Some(v) => set_default(&v).await, None => show_default().await, diff --git a/crates/vp_global_cli/src/commands/env/doctor.rs b/crates/vp_global_cli/src/commands/env/doctor.rs index 4ddcd6564f..551711ddbf 100644 --- a/crates/vp_global_cli/src/commands/env/doctor.rs +++ b/crates/vp_global_cli/src/commands/env/doctor.rs @@ -74,7 +74,7 @@ fn abbreviate_home(path: &str) -> String { } /// Execute the doctor command. -pub async fn execute(cwd: AbsolutePathBuf) -> Result { +pub(crate) async fn execute(cwd: AbsolutePathBuf) -> Result { let mut has_errors = false; // Section: Installation diff --git a/crates/vp_global_cli/src/commands/env/exec.rs b/crates/vp_global_cli/src/commands/env/exec.rs index 10a82a9d1e..69e5ab8799 100644 --- a/crates/vp_global_cli/src/commands/env/exec.rs +++ b/crates/vp_global_cli/src/commands/env/exec.rs @@ -23,7 +23,7 @@ use crate::{ /// When `--node` is provided, runs a command with the specified Node.js version. /// When `--node` is not provided and the command is a shim tool (node/npm/npx or global package), /// uses the same shim dispatch logic as Unix symlinks. -pub async fn execute( +pub(crate) async fn execute( node_version: Option<&str>, npm_version: Option<&str>, command: &[String], diff --git a/crates/vp_global_cli/src/commands/env/list.rs b/crates/vp_global_cli/src/commands/env/list.rs index 27cf29b774..7b19a3f194 100644 --- a/crates/vp_global_cli/src/commands/env/list.rs +++ b/crates/vp_global_cli/src/commands/env/list.rs @@ -43,7 +43,7 @@ pub(super) fn list_installed_versions(node_dir: &std::path::Path) -> Vec } /// Execute the list command (local installed versions). -pub async fn execute(cwd: AbsolutePathBuf, json_output: bool) -> Result { +pub(crate) async fn execute(cwd: AbsolutePathBuf, json_output: bool) -> Result { let node_dir = vp_shared::EnvConfig::get().dirs.data.join("js_runtime").join("node"); let versions = list_installed_versions(node_dir.as_path()); diff --git a/crates/vp_global_cli/src/commands/env/list_remote.rs b/crates/vp_global_cli/src/commands/env/list_remote.rs index aacfc99797..cfcfbb5359 100644 --- a/crates/vp_global_cli/src/commands/env/list_remote.rs +++ b/crates/vp_global_cli/src/commands/env/list_remote.rs @@ -44,7 +44,7 @@ struct LocalMarkers { } /// Execute the list-remote command. -pub async fn execute( +pub(crate) async fn execute( cwd: AbsolutePathBuf, pattern: Option, lts_only: bool, diff --git a/crates/vp_global_cli/src/commands/env/mod.rs b/crates/vp_global_cli/src/commands/env/mod.rs index f68d8169e5..3f8f1a2c28 100644 --- a/crates/vp_global_cli/src/commands/env/mod.rs +++ b/crates/vp_global_cli/src/commands/env/mod.rs @@ -3,9 +3,9 @@ //! This module provides the `vp env` command for managing Node.js environments //! through shim-based version management. -pub mod bin_config; +pub(crate) mod bin_config; mod clean; -pub mod config; +pub(crate) mod config; mod current; mod default; mod doctor; @@ -14,7 +14,7 @@ mod list; mod list_remote; mod off; mod on; -pub mod package_metadata; +pub(crate) mod package_metadata; mod pin; pub(crate) mod setup; mod unpin; @@ -76,7 +76,7 @@ fn is_installable_version_source(source: &str) -> bool { } /// Execute the env command based on the provided arguments. -pub async fn execute(cwd: AbsolutePathBuf, args: EnvArgs) -> Result { +pub(crate) async fn execute(cwd: AbsolutePathBuf, args: EnvArgs) -> Result { // Handle subcommands first if let Some(subcommand) = args.command { if should_print_env_header(&subcommand) { diff --git a/crates/vp_global_cli/src/commands/env/off.rs b/crates/vp_global_cli/src/commands/env/off.rs index 461fdef0bf..85ccbcdec8 100644 --- a/crates/vp_global_cli/src/commands/env/off.rs +++ b/crates/vp_global_cli/src/commands/env/off.rs @@ -9,7 +9,7 @@ use super::config::{ShimMode, load_config, save_config}; use crate::{error::Error, help}; /// Execute the `vp env off` command. -pub async fn execute() -> Result { +pub(crate) async fn execute() -> Result { let mut config = load_config().await?; if config.shim_mode == ShimMode::SystemFirst { diff --git a/crates/vp_global_cli/src/commands/env/on.rs b/crates/vp_global_cli/src/commands/env/on.rs index 6ded635215..602f1acf8c 100644 --- a/crates/vp_global_cli/src/commands/env/on.rs +++ b/crates/vp_global_cli/src/commands/env/on.rs @@ -8,7 +8,7 @@ use super::config::{ShimMode, load_config, save_config}; use crate::{error::Error, help}; /// Execute the `vp env on` command. -pub async fn execute() -> Result { +pub(crate) async fn execute() -> Result { let mut config = load_config().await?; if config.shim_mode == ShimMode::Managed { diff --git a/crates/vp_global_cli/src/commands/env/package_metadata.rs b/crates/vp_global_cli/src/commands/env/package_metadata.rs index c17b035f6a..3c177b9cd8 100644 --- a/crates/vp_global_cli/src/commands/env/package_metadata.rs +++ b/crates/vp_global_cli/src/commands/env/package_metadata.rs @@ -26,45 +26,45 @@ pub(crate) fn is_legacy_install_id(value: &str) -> bool { /// Metadata for a globally installed package. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct PackageMetadata { +pub(crate) struct PackageMetadata { /// Package name - pub name: String, + pub(crate) name: String, /// Package version - pub version: String, + pub(crate) version: String, /// Directory identifier for this installation. Empty or `#`-prefixed for legacy installs. #[serde(default)] - pub install_id: String, + pub(crate) install_id: String, /// Platform versions used during installation - pub platform: Platform, + pub(crate) platform: Platform, /// Binary names provided by this package - pub bins: Vec, + pub(crate) bins: Vec, /// Binary names that are JavaScript files (need Node.js to run). #[serde(default)] - pub js_bins: HashSet, + pub(crate) js_bins: HashSet, /// Version spec the package was installed with (a dist-tag like /// `nightly`, a range, or an exact version), so `vp update -g` keeps /// resolving within it. `None` means the implicit `latest` tag. #[serde(default, skip_serializing_if = "Option::is_none")] - pub version_spec: Option, + pub(crate) version_spec: Option, /// Package manager used for installation (npm, yarn, pnpm) - pub manager: String, + pub(crate) manager: String, /// Installation timestamp - pub installed_at: DateTime, + pub(crate) installed_at: DateTime, } /// Platform versions pinned to this package. #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Platform { +pub(crate) struct Platform { /// Node.js version - pub node: String, + pub(crate) node: String, /// npm version (if applicable) #[serde(skip_serializing_if = "Option::is_none")] - pub npm: Option, + pub(crate) npm: Option, } impl PackageMetadata { /// Create new package metadata. - pub fn new( + pub(crate) fn new( name: String, version: String, node_version: String, @@ -89,7 +89,7 @@ impl PackageMetadata { /// Registry spec update flows should reinstall this package with: /// qualified with the recorded version spec when the install had one, /// the bare name (implicit `latest`) otherwise. - pub fn update_spec(&self) -> String { + pub(crate) fn update_spec(&self) -> String { match &self.version_spec { Some(spec) => format!("{}@{spec}", self.name), None => self.name.clone(), @@ -97,17 +97,17 @@ impl PackageMetadata { } /// Check if a binary requires Node.js to run. - pub fn is_js_binary(&self, bin_name: &str) -> bool { + pub(crate) fn is_js_binary(&self, bin_name: &str) -> bool { self.js_bins.contains(bin_name) } /// Get the package installation prefix. - pub fn installation_dir(&self) -> Result { + pub(crate) fn installation_dir(&self) -> Result { Self::installation_dir_for(&self.name, &self.install_id) } /// Resolve an installation prefix, including both legacy layouts. - pub fn installation_dir_for( + pub(crate) fn installation_dir_for( package_name: &str, install_id: &str, ) -> Result { @@ -127,13 +127,13 @@ impl PackageMetadata { } /// Get the metadata file path for a package. - pub fn metadata_path(package_name: &str) -> Result { + pub(crate) fn metadata_path(package_name: &str) -> Result { let packages_dir = get_packages_dir()?; Ok(packages_dir.join(format!("{package_name}.json"))) } /// Load metadata for a package. - pub async fn load(package_name: &str) -> Result, Error> { + pub(crate) async fn load(package_name: &str) -> Result, Error> { let path = Self::metadata_path(package_name)?; if !tokio::fs::try_exists(&path).await.unwrap_or(false) { return Ok(None); @@ -144,7 +144,7 @@ impl PackageMetadata { } /// Save metadata for a package. - pub async fn save(&self) -> Result<(), Error> { + pub(crate) async fn save(&self) -> Result<(), Error> { let path = Self::metadata_path(&self.name)?; // Create parent directory (handles scoped packages like @scope/pkg.json) if let Some(parent) = path.parent() { @@ -157,7 +157,7 @@ impl PackageMetadata { } /// Delete metadata for a package. - pub async fn delete(package_name: &str) -> Result<(), Error> { + pub(crate) async fn delete(package_name: &str) -> Result<(), Error> { let path = Self::metadata_path(package_name)?; if tokio::fs::try_exists(&path).await.unwrap_or(false) { tokio::fs::remove_file(&path).await?; @@ -166,7 +166,7 @@ impl PackageMetadata { } /// List all installed packages. - pub async fn list_all() -> Result, Error> { + pub(crate) async fn list_all() -> Result, Error> { let packages_dir = get_packages_dir()?; if !tokio::fs::try_exists(&packages_dir).await.unwrap_or(false) { return Ok(Vec::new()); diff --git a/crates/vp_global_cli/src/commands/env/pin.rs b/crates/vp_global_cli/src/commands/env/pin.rs index 84b42ceb0c..bba9a5dc0a 100644 --- a/crates/vp_global_cli/src/commands/env/pin.rs +++ b/crates/vp_global_cli/src/commands/env/pin.rs @@ -23,7 +23,7 @@ const NODE_VERSION_FILE: &str = ".node-version"; const PACKAGE_JSON_FILE: &str = "package.json"; /// Execute the pin command. -pub async fn execute( +pub(crate) async fn execute( cwd: AbsolutePathBuf, version: Option, unpin: bool, @@ -532,7 +532,7 @@ async fn resolve_version_for_pin( /// Removes the same source that `vp env pin` would write: `.node-version` when /// present, otherwise the node entry from `package.json#devEngines.runtime`. /// An explicit `target` overrides the selection. -pub async fn do_unpin( +pub(crate) async fn do_unpin( cwd: &AbsolutePathBuf, target: Option, ) -> Result { diff --git a/crates/vp_global_cli/src/commands/env/setup.rs b/crates/vp_global_cli/src/commands/env/setup.rs index 40d6e9d59d..3fa40c6fd5 100644 --- a/crates/vp_global_cli/src/commands/env/setup.rs +++ b/crates/vp_global_cli/src/commands/env/setup.rs @@ -57,7 +57,7 @@ pub(crate) const SHIM_TOOLS: &[&str] = &["node", "npm", "npx", "pnpm", "pnpx", "yarn", "yarnpkg", "bun", "bunx", "vpx", "vpr"]; /// Execute the setup command. -pub async fn execute(refresh: bool, env_only: bool) -> Result { +pub(crate) async fn execute(refresh: bool, env_only: bool) -> Result { let config = vp_shared::EnvConfig::get(); let dirs = &config.dirs; diff --git a/crates/vp_global_cli/src/commands/env/unpin.rs b/crates/vp_global_cli/src/commands/env/unpin.rs index 234bac5202..19552d09ef 100644 --- a/crates/vp_global_cli/src/commands/env/unpin.rs +++ b/crates/vp_global_cli/src/commands/env/unpin.rs @@ -11,6 +11,9 @@ use vt_path::AbsolutePathBuf; use crate::{cli::PinTarget, error::Error}; /// Execute the unpin command. -pub async fn execute(cwd: AbsolutePathBuf, target: Option) -> Result { +pub(crate) async fn execute( + cwd: AbsolutePathBuf, + target: Option, +) -> Result { super::pin::do_unpin(&cwd, target).await } diff --git a/crates/vp_global_cli/src/commands/env/use.rs b/crates/vp_global_cli/src/commands/env/use.rs index 405d322575..aee4595439 100644 --- a/crates/vp_global_cli/src/commands/env/use.rs +++ b/crates/vp_global_cli/src/commands/env/use.rs @@ -70,7 +70,7 @@ fn print_windows_eval_wrapper_required() { } /// Execute the `vp env use` command. -pub async fn execute( +pub(crate) async fn execute( cwd: AbsolutePathBuf, version: Option, unset: bool, diff --git a/crates/vp_global_cli/src/commands/env/which.rs b/crates/vp_global_cli/src/commands/env/which.rs index 6b6a3a3091..139e575199 100644 --- a/crates/vp_global_cli/src/commands/env/which.rs +++ b/crates/vp_global_cli/src/commands/env/which.rs @@ -31,7 +31,7 @@ const CORE_TOOLS: &[&str] = &["node", "npm", "npx"]; const LABEL_WIDTH: usize = 10; /// Execute the which command. -pub async fn execute(cwd: AbsolutePathBuf, tool: &str) -> Result { +pub(crate) async fn execute(cwd: AbsolutePathBuf, tool: &str) -> Result { if let Some(status) = execute_package_manager_tool(&cwd, tool).await? { return Ok(status); } diff --git a/crates/vp_global_cli/src/commands/global/install.rs b/crates/vp_global_cli/src/commands/global/install.rs index ccbaa443c1..a81f0b8a73 100644 --- a/crates/vp_global_cli/src/commands/global/install.rs +++ b/crates/vp_global_cli/src/commands/global/install.rs @@ -114,20 +114,20 @@ pub(crate) fn is_protected_shim(bin_name: &str, ignore_case: bool) -> bool { } /// Options for [`install`]. -pub struct InstallOptions<'a> { +pub(crate) struct InstallOptions<'a> { /// Node.js version to install with; resolved from the current directory /// when `None`. - pub node_version: Option<&'a str>, + pub(crate) node_version: Option<&'a str>, /// Auto-uninstall packages whose binaries conflict. - pub force: bool, + pub(crate) force: bool, /// Number of packages to install in parallel. - pub concurrency: usize, + pub(crate) concurrency: usize, /// Whether this is a `vp update -g` operation. - pub update: bool, + pub(crate) update: bool, } /// Install global packages in parallel. -pub async fn install( +pub(crate) async fn install( package_specs: &[String], options: InstallOptions<'_>, ) -> Result<(), InstallError> { @@ -952,7 +952,7 @@ async fn stale_bin_names_for_package( /// Uses two-phase uninstall: /// 1. Try to use PackageMetadata for binary list /// 2. Fallback to scanning BinConfig files for orphaned binaries -pub async fn uninstall(package_name: &str, dry_run: bool) -> Result<(), Error> { +pub(crate) async fn uninstall(package_name: &str, dry_run: bool) -> Result<(), Error> { if is_local_package_spec(package_name) { // We can't resolve local packages for uninstall, follow npm's behavior return Err(Error::Other( diff --git a/crates/vp_global_cli/src/commands/global/mod.rs b/crates/vp_global_cli/src/commands/global/mod.rs index 6e9431fb96..47e7d2ad34 100644 --- a/crates/vp_global_cli/src/commands/global/mod.rs +++ b/crates/vp_global_cli/src/commands/global/mod.rs @@ -12,9 +12,9 @@ use vt_path::{AbsolutePathBuf, current_dir}; use crate::{commands::env::config::resolve_version, error::Error}; -pub mod install; -pub mod outdated; -pub mod packages; +pub(crate) mod install; +pub(crate) mod outdated; +pub(crate) mod packages; /// Core shims that should not be overwritten by package binaries. pub(crate) const CORE_SHIMS: &[&str] = diff --git a/crates/vp_global_cli/src/commands/global/outdated.rs b/crates/vp_global_cli/src/commands/global/outdated.rs index 178b5df238..8f5d630142 100644 --- a/crates/vp_global_cli/src/commands/global/outdated.rs +++ b/crates/vp_global_cli/src/commands/global/outdated.rs @@ -17,15 +17,15 @@ use crate::{ }; #[derive(Debug)] -pub struct OutdatedPackage { - pub name: String, - pub current: String, +pub(crate) struct OutdatedPackage { + pub(crate) name: String, + pub(crate) current: String, /// Newest version within the version spec recorded at install time (or /// given on the command line); what an update would install. - pub wanted: String, + pub(crate) wanted: String, /// Newest version on the registry's `latest` dist-tag. - pub latest: String, - pub spec: Option, + pub(crate) latest: String, + pub(crate) spec: Option, install_id: String, node: String, bins: Vec, @@ -36,13 +36,13 @@ pub struct OutdatedPackage { /// whose registry lookup failed (so callers can warn and continue instead of /// aborting, and can tell which packages were left unresolved). #[derive(Debug)] -pub struct OutdatedReport { - pub outdated: Vec, - pub failures: Vec<(String, String)>, +pub(crate) struct OutdatedReport { + pub(crate) outdated: Vec, + pub(crate) failures: Vec<(String, String)>, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum LookupMode { +pub(crate) enum LookupMode { /// Resolve only the version an update would install. WantedOnly, /// Also resolve the registry's `latest` dist-tag for outdated reporting. @@ -113,7 +113,7 @@ struct OutdatedPackageJson { location: String, } -pub async fn get_outdated_packages( +pub(crate) async fn get_outdated_packages( packages: &[String], concurrency: usize, latest: bool, @@ -211,7 +211,7 @@ pub async fn get_outdated_packages( Ok(OutdatedReport { outdated, failures }) } -pub async fn execute( +pub(crate) async fn execute( packages: &[String], long: bool, format: Option, diff --git a/crates/vp_global_cli/src/commands/global/packages.rs b/crates/vp_global_cli/src/commands/global/packages.rs index 149d96cda2..6acf95fbaf 100644 --- a/crates/vp_global_cli/src/commands/global/packages.rs +++ b/crates/vp_global_cli/src/commands/global/packages.rs @@ -7,7 +7,7 @@ use owo_colors::OwoColorize; use crate::{commands::env::package_metadata::PackageMetadata, error::Error}; /// Execute the packages command. -pub async fn execute(json: bool, pattern: Option<&str>) -> Result { +pub(crate) async fn execute(json: bool, pattern: Option<&str>) -> Result { let all_packages = PackageMetadata::list_all().await?; let packages: Vec<_> = if let Some(pat) = pattern { diff --git a/crates/vp_global_cli/src/commands/hooks.rs b/crates/vp_global_cli/src/commands/hooks.rs index 1949c55c83..08ab15745b 100644 --- a/crates/vp_global_cli/src/commands/hooks.rs +++ b/crates/vp_global_cli/src/commands/hooks.rs @@ -7,7 +7,7 @@ use vt_path::AbsolutePathBuf; use crate::error::Error; /// Execute the `hooks` command by delegating to local or global vite-plus. -pub async fn execute( +pub(crate) async fn execute( cwd: AbsolutePathBuf, args: &[String], raw_subcommand: Option<&str>, diff --git a/crates/vp_global_cli/src/commands/implode.rs b/crates/vp_global_cli/src/commands/implode.rs index 9ad0244734..9366f3d634 100644 --- a/crates/vp_global_cli/src/commands/implode.rs +++ b/crates/vp_global_cli/src/commands/implode.rs @@ -49,7 +49,7 @@ fn lexical_path(path: &Path) -> PathBuf { normalized } -pub fn execute(yes: bool) -> Result { +pub(crate) fn execute(yes: bool) -> Result { let env_config = vp_shared::EnvConfig::get(); let dirs = &env_config.dirs; @@ -1363,25 +1363,35 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let home = AbsolutePathBuf::new(temp_dir.path().to_path_buf()).unwrap(); let home_dir = home.join(".vite-plus"); + let xdg_config = home.join(".config"); + let xdg_data = home.join(".local/share"); let matcher = VitePlusSourceMatcher::new(&home_dir, &home); - // Clear env overrides so the test environment doesn't affect results - temp_env::with_vars_unset(["ZDOTDIR", "XDG_CONFIG_HOME", "XDG_DATA_HOME"], || { - // Main profile with vite-plus line - std::fs::write(home.join(".zshrc"), ". \"$HOME/.vite-plus/env\"\n").unwrap(); - // Unrelated profile (should be ignored) - std::fs::write(home.join(".bashrc"), "export PATH=/usr/bin\n").unwrap(); - // Snippet file with a matching Vite+ source line - let fish_dir = home.join(".config/fish/conf.d"); - std::fs::create_dir_all(&fish_dir).unwrap(); - std::fs::write(fish_dir.join("vite-plus.fish"), "source ~/.vite-plus/env.fish\n") - .unwrap(); + // Keep every profile root inside the fixture so the user's real Nushell + // directories cannot affect the result. + temp_env::with_vars( + [ + ("ZDOTDIR", None), + ("XDG_CONFIG_HOME", Some(xdg_config.as_path().as_os_str())), + ("XDG_DATA_HOME", Some(xdg_data.as_path().as_os_str())), + ], + || { + // Main profile with vite-plus line + std::fs::write(home.join(".zshrc"), ". \"$HOME/.vite-plus/env\"\n").unwrap(); + // Unrelated profile (should be ignored) + std::fs::write(home.join(".bashrc"), "export PATH=/usr/bin\n").unwrap(); + // Snippet file with a matching Vite+ source line + let fish_dir = home.join(".config/fish/conf.d"); + std::fs::create_dir_all(&fish_dir).unwrap(); + std::fs::write(fish_dir.join("vite-plus.fish"), "source ~/.vite-plus/env.fish\n") + .unwrap(); - let profiles = collect_affected_profiles(&home, &matcher); - assert_eq!(profiles.len(), 2); - assert!(matches!(&profiles[0].kind, AffectedProfileKind::Main { .. })); - assert!(matches!(&profiles[1].kind, AffectedProfileKind::Snippet)); - }); + let profiles = collect_affected_profiles(&home, &matcher); + assert_eq!(profiles.len(), 2); + assert!(matches!(&profiles[0].kind, AffectedProfileKind::Main { .. })); + assert!(matches!(&profiles[1].kind, AffectedProfileKind::Snippet)); + }, + ); } #[test] diff --git a/crates/vp_global_cli/src/commands/migrate.rs b/crates/vp_global_cli/src/commands/migrate.rs index 3b7ca3116e..e84d3d6aec 100644 --- a/crates/vp_global_cli/src/commands/migrate.rs +++ b/crates/vp_global_cli/src/commands/migrate.rs @@ -11,7 +11,7 @@ use crate::{error::Error, js_executor::JsExecutor}; /// Routes through [`JsExecutor::delegate_migrate`], which escalates to the /// global CLI when the project's local `vite-plus` is older than this global /// `vp` (the upgrade scenario). Otherwise it keeps local-first semantics. -pub async fn execute(cwd: AbsolutePathBuf, args: &[String]) -> Result { +pub(crate) async fn execute(cwd: AbsolutePathBuf, args: &[String]) -> Result { let mut executor = JsExecutor::new(None).without_missing_local_cli_warning(); let mut full_args = vec!["migrate".to_string()]; full_args.extend(args.iter().cloned()); diff --git a/crates/vp_global_cli/src/commands/mod.rs b/crates/vp_global_cli/src/commands/mod.rs index a07b462ca4..a36be22629 100644 --- a/crates/vp_global_cli/src/commands/mod.rs +++ b/crates/vp_global_cli/src/commands/mod.rs @@ -77,7 +77,7 @@ fn find_vite_plus_dependency(cwd: &AbsolutePath) -> Option { /// /// Returns `true` if vite-plus is found, `false` if not found /// or if no package.json exists. -pub fn has_vite_plus_dependency(cwd: &AbsolutePath) -> bool { +pub(crate) fn has_vite_plus_dependency(cwd: &AbsolutePath) -> bool { find_nearest_package_json(cwd) .is_some_and(|package_json_path| package_json_has_vite_plus_dependency(&package_json_path)) } @@ -113,7 +113,9 @@ pub(crate) fn warn_missing_local_cli_if_project(cwd: &AbsolutePath) { /// /// If `project_path` contains a package.json, uses the project's runtime /// (based on devEngines.runtime). Otherwise, falls back to the CLI's runtime. -pub async fn prepend_js_runtime_to_path_env(project_path: &AbsolutePath) -> Result<(), Error> { +pub(crate) async fn prepend_js_runtime_to_path_env( + project_path: &AbsolutePath, +) -> Result<(), Error> { let mut executor = JsExecutor::new(None); // Use project runtime if package.json exists, otherwise use CLI runtime @@ -135,31 +137,31 @@ pub async fn prepend_js_runtime_to_path_env(project_path: &AbsolutePath) -> Resu } // Global package management -pub mod global; +pub(crate) mod global; // Category B: JS Script Commands -pub mod config; -pub mod create; -pub mod hooks; -pub mod migrate; -pub mod staged; -pub mod toolchain; -pub mod version; +pub(crate) mod config; +pub(crate) mod create; +pub(crate) mod hooks; +pub(crate) mod migrate; +pub(crate) mod staged; +pub(crate) mod toolchain; +pub(crate) mod version; // Category D: Environment Management -pub mod env; -pub mod shell; +pub(crate) mod env; +pub(crate) mod shell; // Standalone binary commands -pub mod vpr; -pub mod vpx; +pub(crate) mod vpr; +pub(crate) mod vpx; // Self-Management -pub mod implode; -pub mod upgrade; +pub(crate) mod implode; +pub(crate) mod upgrade; // Category C: Local CLI Delegation -pub mod delegate; +pub(crate) mod delegate; #[cfg(test)] mod tests { diff --git a/crates/vp_global_cli/src/commands/shell.rs b/crates/vp_global_cli/src/commands/shell.rs index 3717672d08..652777b8a6 100644 --- a/crates/vp_global_cli/src/commands/shell.rs +++ b/crates/vp_global_cli/src/commands/shell.rs @@ -8,7 +8,7 @@ use vt_str::Str; /// Detected shell type for output formatting. #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Shell { +pub(crate) enum Shell { /// POSIX shell (bash, zsh, sh) Posix, /// Fish shell @@ -40,7 +40,7 @@ impl FromStr for Shell { /// 1. `VP_SHELL` environment variable /// 2. Platform default #[must_use] -pub fn detect_shell() -> Shell { +pub(crate) fn detect_shell() -> Shell { let config = vp_shared::EnvConfig::get(); // 1. Check VP_SHELL environment variable @@ -57,7 +57,7 @@ pub fn detect_shell() -> Shell { /// All shell profile files that interactive terminal sessions may source. /// This matches the files that `install.sh` writes to and `vp implode` cleans. #[cfg(not(windows))] -pub const ALL_SHELL_PROFILES: &[ShellProfile] = &[ +pub(crate) const ALL_SHELL_PROFILES: &[ShellProfile] = &[ ShellProfile { root: ShellProfileRoot::Zsh, path: ".zshenv", @@ -121,7 +121,7 @@ pub const ALL_SHELL_PROFILES: &[ShellProfile] = &[ ]; #[cfg(windows)] -pub const ALL_SHELL_PROFILES: &[ShellProfile] = &[ +pub(crate) const ALL_SHELL_PROFILES: &[ShellProfile] = &[ ShellProfile { root: ShellProfileRoot::NushellConfig, path: "nushell/config.nu", @@ -148,7 +148,7 @@ pub const ALL_SHELL_PROFILES: &[ShellProfile] = &[ /// - macOS: `.zshenv` is sourced for all zsh invocations (including IDE env resolution) /// - Linux: `.profile` is sourced by X11 display managers; `.zshenv` covers Wayland + zsh #[cfg(target_os = "macos")] -pub const IDE_SHELL_PROFILES: &[ShellProfile] = &[ +pub(crate) const IDE_SHELL_PROFILES: &[ShellProfile] = &[ ShellProfile { root: ShellProfileRoot::Zsh, path: ".zshenv", @@ -164,7 +164,7 @@ pub const IDE_SHELL_PROFILES: &[ShellProfile] = &[ ]; #[cfg(target_os = "linux")] -pub const IDE_SHELL_PROFILES: &[ShellProfile] = &[ +pub(crate) const IDE_SHELL_PROFILES: &[ShellProfile] = &[ ShellProfile { root: ShellProfileRoot::Home, path: ".profile", @@ -180,25 +180,25 @@ pub const IDE_SHELL_PROFILES: &[ShellProfile] = &[ ]; #[cfg(windows)] -pub const IDE_SHELL_PROFILES: &[ShellProfile] = &[]; +pub(crate) const IDE_SHELL_PROFILES: &[ShellProfile] = &[]; #[cfg(not(any(target_os = "macos", target_os = "linux", windows)))] -pub const IDE_SHELL_PROFILES: &[ShellProfile] = &[ShellProfile { +pub(crate) const IDE_SHELL_PROFILES: &[ShellProfile] = &[ShellProfile { root: ShellProfileRoot::Home, path: ".profile", env_file: "env", kind: ShellProfileKind::Main, }]; -pub struct ShellProfile { - pub root: ShellProfileRoot, - pub path: &'static str, - pub env_file: &'static str, - pub kind: ShellProfileKind, +pub(crate) struct ShellProfile { + pub(crate) root: ShellProfileRoot, + pub(crate) path: &'static str, + pub(crate) env_file: &'static str, + pub(crate) kind: ShellProfileKind, } #[derive(Clone, Copy)] -pub enum ShellProfileRoot { +pub(crate) enum ShellProfileRoot { #[cfg_attr(windows, allow(dead_code))] Home, #[cfg_attr(windows, allow(dead_code))] @@ -210,7 +210,7 @@ pub enum ShellProfileRoot { } #[derive(Clone, Copy)] -pub enum ShellProfileKind { +pub(crate) enum ShellProfileKind { Main, Snippet, } diff --git a/crates/vp_global_cli/src/commands/staged.rs b/crates/vp_global_cli/src/commands/staged.rs index e1b273cb0f..af43d5671a 100644 --- a/crates/vp_global_cli/src/commands/staged.rs +++ b/crates/vp_global_cli/src/commands/staged.rs @@ -7,7 +7,7 @@ use vt_path::AbsolutePathBuf; use crate::error::Error; /// Execute the `staged` command by delegating to local or global vite-plus. -pub async fn execute( +pub(crate) async fn execute( cwd: AbsolutePathBuf, args: &[String], raw_subcommand: Option<&str>, diff --git a/crates/vp_global_cli/src/commands/toolchain.rs b/crates/vp_global_cli/src/commands/toolchain.rs index 0f118cc56c..e71e6b2227 100644 --- a/crates/vp_global_cli/src/commands/toolchain.rs +++ b/crates/vp_global_cli/src/commands/toolchain.rs @@ -4,7 +4,7 @@ use vt_path::AbsolutePathBuf; use crate::{commands::delegate, error::Error, js_executor::JsExecutor}; -pub async fn execute( +pub(crate) async fn execute( cwd: AbsolutePathBuf, tools: Vec, json: bool, diff --git a/crates/vp_global_cli/src/commands/upgrade/mod.rs b/crates/vp_global_cli/src/commands/upgrade/mod.rs index 11a43f7873..77f91e6ab8 100644 --- a/crates/vp_global_cli/src/commands/upgrade/mod.rs +++ b/crates/vp_global_cli/src/commands/upgrade/mod.rs @@ -14,28 +14,28 @@ use vt_path::AbsolutePathBuf; use crate::error::Error; /// Options for the upgrade command. -pub struct UpgradeOptions { +pub(crate) struct UpgradeOptions { /// Target version (e.g., "0.2.0"). None means use the tag. - pub version: Option, + pub(crate) version: Option, /// npm dist-tag (default: "latest") - pub tag: String, + pub(crate) tag: String, /// Check for updates without installing - pub check: bool, + pub(crate) check: bool, /// Revert to previous version - pub rollback: bool, + pub(crate) rollback: bool, /// Force reinstall even if already on the target version - pub force: bool, + pub(crate) force: bool, /// Suppress output - pub silent: bool, + pub(crate) silent: bool, /// Custom npm registry URL - pub registry: Option, + pub(crate) registry: Option, /// Refresh cached update status in a background helper - pub background_check: bool, + pub(crate) background_check: bool, } /// Execute the upgrade command. #[allow(clippy::print_stdout, clippy::print_stderr)] -pub async fn execute(options: UpgradeOptions) -> Result { +pub(crate) async fn execute(options: UpgradeOptions) -> Result { if options.background_check { crate::upgrade_check::run_background_check().await; return Ok(ExitStatus::default()); diff --git a/crates/vp_global_cli/src/commands/version.rs b/crates/vp_global_cli/src/commands/version.rs index 26cff89b7e..e507c241b2 100644 --- a/crates/vp_global_cli/src/commands/version.rs +++ b/crates/vp_global_cli/src/commands/version.rs @@ -166,7 +166,7 @@ fn detect_system_node_version() -> Option { } /// Execute the `--version` command. -pub async fn execute(cwd: AbsolutePathBuf) -> Result { +pub(crate) async fn execute(cwd: AbsolutePathBuf) -> Result { vp_shared::header::print_header(); println!("vp v{}", env!("CARGO_PKG_VERSION")); diff --git a/crates/vp_global_cli/src/commands/vpr.rs b/crates/vp_global_cli/src/commands/vpr.rs index 699689537b..76f79b03c1 100644 --- a/crates/vp_global_cli/src/commands/vpr.rs +++ b/crates/vp_global_cli/src/commands/vpr.rs @@ -9,7 +9,7 @@ use vt_path::AbsolutePath; /// Main entry point for vpr execution. /// /// Called from shim dispatch when `argv[0]` is `vpr`. -pub async fn execute_vpr(args: &[String], cwd: &AbsolutePath) -> i32 { +pub(crate) async fn execute_vpr(args: &[String], cwd: &AbsolutePath) -> i32 { // `vpr -C ` mirrors `vp -C run `: consume the // global flag before treating the rest as run arguments. There is no // clap parse on this path, so a missing value is reported here. diff --git a/crates/vp_global_cli/src/commands/vpx.rs b/crates/vp_global_cli/src/commands/vpx.rs index 529d8bb3e4..dfeea86497 100644 --- a/crates/vp_global_cli/src/commands/vpx.rs +++ b/crates/vp_global_cli/src/commands/vpx.rs @@ -14,15 +14,15 @@ use crate::{commands::env::config, shim::dispatch}; /// Parsed vpx flags. #[derive(Debug, Default)] -pub struct VpxFlags { +pub(crate) struct VpxFlags { /// Packages to install (from --package/-p) - pub packages: Vec, + pub(crate) packages: Vec, /// Execute within a shell environment (-c/--shell-mode) - pub shell_mode: bool, + pub(crate) shell_mode: bool, /// Suppress output (-s/--silent) - pub silent: bool, + pub(crate) silent: bool, /// Show help (-h/--help) - pub help: bool, + pub(crate) help: bool, } /// Help text for vpx. @@ -57,7 +57,7 @@ struct GlobalBinary { /// Main entry point for vpx execution. /// /// Called from shim dispatch when `argv[0]` is `vpx`. -pub async fn execute_vpx(args: &[String], cwd: &AbsolutePath) -> i32 { +pub(crate) async fn execute_vpx(args: &[String], cwd: &AbsolutePath) -> i32 { let (flags, positional) = parse_vpx_args(args); // Show help @@ -233,7 +233,7 @@ fn prepend_node_modules_bin_to_path(cwd: &AbsolutePath) { /// /// On Windows, also checks for `.cmd` extension. /// Returns the absolute path to the binary if found. -pub fn find_local_binary(cwd: &AbsolutePath, cmd: &str) -> Option { +pub(crate) fn find_local_binary(cwd: &AbsolutePath, cmd: &str) -> Option { let mut current = cwd; loop { let bin_dir = current.join("node_modules").join(".bin"); @@ -266,7 +266,7 @@ pub fn find_local_binary(cwd: &AbsolutePath, cmd: &str) -> Option bool { +pub(crate) fn has_version_spec(spec: &str) -> bool { if spec.starts_with('@') { // Scoped package: @scope/pkg@version if let Some(slash_pos) = spec.find('/') { @@ -307,7 +307,7 @@ fn extract_command_name(spec: &str) -> String { /// /// All flags must come before the first positional argument (npx-style). /// Returns the parsed flags and remaining positional arguments. -pub fn parse_vpx_args(args: &[String]) -> (VpxFlags, Vec) { +pub(crate) fn parse_vpx_args(args: &[String]) -> (VpxFlags, Vec) { let mut flags = VpxFlags::default(); let mut positional = Vec::new(); let mut i = 0; diff --git a/crates/vp_global_cli/src/error.rs b/crates/vp_global_cli/src/error.rs index 1cd52d92aa..e119130ea4 100644 --- a/crates/vp_global_cli/src/error.rs +++ b/crates/vp_global_cli/src/error.rs @@ -6,11 +6,7 @@ use vt_str::Str; /// Error type for the global CLI. #[derive(Debug, thiserror::Error)] -pub enum Error { - #[allow(dead_code)] // Will be used for better error messages - #[error("No package manager detected. Please run in a project directory with a package.json.")] - NoPackageManager, - +pub(crate) enum Error { #[error("Failed to download Node.js runtime: {0}")] RuntimeDownload(#[from] vp_js_runtime::Error), @@ -65,7 +61,7 @@ pub enum Error { impl Error { /// Whether this error should be printed without the "error: " prefix /// (a friendly user-facing message, not a stack trace). - pub fn is_user_message(&self) -> bool { + pub(crate) fn is_user_message(&self) -> bool { matches!(self, Self::UserMessage(_) | Self::PmCli(vp_pm_cli::Error::UserMessage(_))) } } diff --git a/crates/vp_global_cli/src/help.rs b/crates/vp_global_cli/src/help.rs index 6445cc204b..84bcdf709f 100644 --- a/crates/vp_global_cli/src/help.rs +++ b/crates/vp_global_cli/src/help.rs @@ -1,7 +1,7 @@ //! Global help documents and routing. use clap::{CommandFactory, error::ErrorKind}; -pub use vp_cli_help::{ +pub(crate) use vp_cli_help::{ HelpDoc, HelpRow, HelpSection, accent, accent_command, print_help_doc, render_heading, render_help_doc, }; @@ -249,7 +249,7 @@ fn parse_clap_help_to_doc(raw_help: &str) -> Option { }) } -pub fn top_level_help_doc() -> HelpDoc { +pub(crate) fn top_level_help_doc() -> HelpDoc { HelpDoc { usage: "vp [COMMAND]".into(), summary: Vec::new(), @@ -455,7 +455,7 @@ fn should_skip_parent_help_for_unknown_direct_nested_child( && has_help_flag_before_terminator(&argv[index..]) } -pub fn maybe_print_unified_clap_subcommand_help(argv: &[String]) -> bool { +pub(crate) fn maybe_print_unified_clap_subcommand_help(argv: &[String]) -> bool { if argv.len() < 3 { return false; } @@ -524,7 +524,7 @@ pub fn maybe_print_unified_clap_subcommand_help(argv: &[String]) -> bool { print_unified_clap_help_for_path(&command_path_refs) } -pub fn print_unified_clap_help_for_path(command_path: &[&str]) -> bool { +pub(crate) fn print_unified_clap_help_for_path(command_path: &[&str]) -> bool { if command_path == ["env"] { print_help_doc(&env_help_doc()); return true; diff --git a/crates/vp_global_cli/src/js_executor.rs b/crates/vp_global_cli/src/js_executor.rs index 31459ed92b..9a1bb5b4ea 100644 --- a/crates/vp_global_cli/src/js_executor.rs +++ b/crates/vp_global_cli/src/js_executor.rs @@ -24,7 +24,7 @@ use crate::{ /// Handles two runtime resolution strategies: /// - CLI runtime: For package manager commands and bundled JS scripts (Categories A & B) /// - Project runtime: For delegating to local vite-plus CLI (Category C) -pub struct JsExecutor { +pub(crate) struct JsExecutor { /// Cached runtime for CLI commands (Categories A & B) cli_runtime: Option, /// Cached runtime for project delegation (Category C) @@ -46,7 +46,7 @@ impl JsExecutor { /// * `scripts_dir` - Optional path to the JS scripts directory. /// If not provided, will be auto-detected from the binary location. #[must_use] - pub const fn new(scripts_dir: Option) -> Self { + pub(crate) const fn new(scripts_dir: Option) -> Self { Self { cli_runtime: None, project_runtime: None, @@ -61,13 +61,13 @@ impl JsExecutor { /// /// A command runs under its canonical name, so the spelling the user used is /// otherwise lost on the way down. - pub fn with_raw_subcommand(mut self, raw_subcommand: Option<&str>) -> Self { + pub(crate) fn with_raw_subcommand(mut self, raw_subcommand: Option<&str>) -> Self { self.raw_subcommand = raw_subcommand.map(ToOwned::to_owned); self } /// Preserve an explicit `-C` after the global CLI changes the child cwd. - pub const fn with_explicit_chdir(mut self, explicit_chdir: bool) -> Self { + pub(crate) const fn with_explicit_chdir(mut self, explicit_chdir: bool) -> Self { self.explicit_chdir = explicit_chdir; self } @@ -83,7 +83,7 @@ impl JsExecutor { /// 1. Explicitly provided `scripts_dir` /// 2. `VP_GLOBAL_CLI_JS_SCRIPTS_DIR` environment variable /// 3. Auto-detect from binary location (../dist relative to binary) - pub fn get_scripts_dir(&self) -> Result { + pub(crate) fn get_scripts_dir(&self) -> Result { // 1. Use explicitly provided scripts_dir if let Some(dir) = &self.scripts_dir { return Ok(dir.clone()); @@ -165,7 +165,7 @@ impl JsExecutor { /// /// When system-first mode is active (`vp env off`), prefers the /// system-installed Node.js found in PATH. - pub async fn ensure_cli_runtime(&mut self) -> Result<&JsRuntime, Error> { + pub(crate) async fn ensure_cli_runtime(&mut self) -> Result<&JsRuntime, Error> { if self.cli_runtime.is_none() { if let Some(system_runtime) = find_system_node_runtime().await { return Ok(self.cli_runtime.insert(system_runtime)); @@ -188,7 +188,7 @@ impl JsExecutor { /// delegates to `download_runtime_for_project()` for cache-aware resolution /// 4. User default from config.json /// 5. Latest LTS - pub async fn ensure_project_runtime( + pub(crate) async fn ensure_project_runtime( &mut self, project_path: &AbsolutePath, ) -> Result<&JsRuntime, Error> { @@ -237,15 +237,6 @@ impl JsExecutor { Ok(self.project_runtime.as_ref().unwrap()) } - /// Download a specific Node.js version. - /// - /// This is used when we need a specific version regardless of - /// package.json configuration. - #[allow(dead_code)] // Will be used in future phases - pub async fn download_node(&self, version: &str) -> Result { - Ok(download_runtime(JsRuntimeType::Node, version).await?) - } - /// Delegate to local or global vite-plus CLI. /// /// Uses `oxc_resolver` to find the project's local vite-plus installation. @@ -253,12 +244,10 @@ impl JsExecutor { /// to the global installation's `dist/bin.js`. /// /// Uses the project's runtime resolved via `config::resolve_version()`. - /// For side-effect-free commands like `--version`, use [`delegate_with_cli_runtime`] instead. - /// /// # Arguments /// * `project_path` - Path to the project directory /// * `args` - Arguments to pass to the local CLI - pub async fn delegate_to_local_cli( + pub(crate) async fn delegate_to_local_cli( &mut self, project_path: &AbsolutePath, args: &[String], @@ -270,7 +259,7 @@ impl JsExecutor { self.run_js_entry(project_path, &node_binary, &bin_prefix, args).await } - pub async fn delegate_to_local_cli_output( + pub(crate) async fn delegate_to_local_cli_output( &mut self, project_path: &AbsolutePath, args: &[String], @@ -289,7 +278,7 @@ impl JsExecutor { /// (or local is newer, or none is installed) keep local-first semantics /// (`delegate_to_local_cli` already falls back to the global bin when no local /// vite-plus is resolvable). - pub async fn delegate_migrate( + pub(crate) async fn delegate_migrate( &mut self, project_path: &AbsolutePath, args: &[String], @@ -315,7 +304,7 @@ impl JsExecutor { /// /// Unlike [`delegate_to_local_cli`], this bypasses project-local resolution and always runs /// the global installation's `dist/bin.js`. - pub async fn delegate_to_global_cli( + pub(crate) async fn delegate_to_global_cli( &mut self, project_path: &AbsolutePath, args: &[String], @@ -333,26 +322,6 @@ impl JsExecutor { Ok(vp_command::execute_with_terminal_guard(cmd).await?) } - /// Delegate to local or global vite-plus CLI using the CLI's own runtime. - /// - /// Like [`delegate_to_local_cli`], but uses the CLI's bundled runtime - /// (from its own `devEngines.runtime` in `package.json`) instead of the - /// project's runtime. This avoids side effects like writing `.node-version` - /// when no version source exists in the project directory. - /// - /// Use this for read-only / side-effect-free commands like `--version`. - #[allow(dead_code)] // kept for future read-only delegations - pub async fn delegate_with_cli_runtime( - &mut self, - project_path: &AbsolutePath, - args: &[String], - ) -> Result { - let runtime = self.ensure_cli_runtime().await?; - let node_binary = runtime.get_binary_path(); - let bin_prefix = runtime.get_bin_prefix(); - self.run_js_entry(project_path, &node_binary, &bin_prefix, args).await - } - /// Prepare a JS command with the entry point resolved. fn prepare_js_entry( &self, diff --git a/crates/vp_global_cli/src/main.rs b/crates/vp_global_cli/src/main.rs index bad8e805a7..5c0794bbb6 100644 --- a/crates/vp_global_cli/src/main.rs +++ b/crates/vp_global_cli/src/main.rs @@ -33,7 +33,7 @@ use clap_complete::env::CompleteEnv; use owo_colors::OwoColorize; use vp_shared::{exit_code_from_status, output}; -pub use crate::cli::try_parse_args_from; +pub(crate) use crate::cli::try_parse_args_from; use crate::cli::{ RenderOptions, command_with_help, raw_subcommand, run_command, run_command_with_options, try_parse_args_from_with_options, diff --git a/crates/vp_global_cli/src/shim/cache.rs b/crates/vp_global_cli/src/shim/cache.rs index 98ff40d63d..8d715af570 100644 --- a/crates/vp_global_cli/src/shim/cache.rs +++ b/crates/vp_global_cli/src/shim/cache.rs @@ -20,28 +20,28 @@ const DEFAULT_MAX_ENTRIES: usize = 4096; /// A single cache entry for a resolved version. #[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ResolveCacheEntry { +pub(crate) struct ResolveCacheEntry { /// The resolved version string (e.g., "20.18.0") - pub version: String, + pub(crate) version: String, /// The source of the version (e.g., ".node-version", "engines.node") - pub source: String, + pub(crate) source: String, /// Project root directory (if applicable) - pub project_root: Option, + pub(crate) project_root: Option, /// Unix timestamp when this entry was resolved - pub resolved_at: u64, + pub(crate) resolved_at: u64, /// Mtime of the version source file (for invalidation) - pub version_file_mtime: u64, + pub(crate) version_file_mtime: u64, /// Path to the version source file - pub source_path: Option, + pub(crate) source_path: Option, /// Whether the original version spec was a range (e.g., "20", "^20.0.0", "lts/*") /// Range versions use time-based expiry (1 hour) instead of mtime-only validation #[serde(default)] - pub is_range: bool, + pub(crate) is_range: bool, } /// Resolution cache stored in `/resolve_cache.json`. #[derive(Serialize, Deserialize, Debug)] -pub struct ResolveCache { +pub(crate) struct ResolveCache { /// Cache format version for upgrade compatibility version: u32, /// Cache entries keyed by current working directory @@ -56,7 +56,7 @@ impl Default for ResolveCache { impl ResolveCache { /// Load cache from disk. - pub fn load(cache_path: &AbsolutePath) -> Self { + pub(crate) fn load(cache_path: &AbsolutePath) -> Self { match std::fs::read_to_string(cache_path) { Ok(content) => { match serde_json::from_str::(&content) { @@ -77,7 +77,7 @@ impl ResolveCache { } /// Save cache to disk. - pub fn save(&self, cache_path: &AbsolutePath) { + pub(crate) fn save(&self, cache_path: &AbsolutePath) { // Ensure parent directory exists if let Some(parent) = cache_path.parent() { std::fs::create_dir_all(parent).ok(); @@ -89,7 +89,7 @@ impl ResolveCache { } /// Get a cache entry if valid. - pub fn get(&self, cwd: &AbsolutePath) -> Option<&ResolveCacheEntry> { + pub(crate) fn get(&self, cwd: &AbsolutePath) -> Option<&ResolveCacheEntry> { let key = cwd.as_path().to_string_lossy().to_string(); let entry = self.entries.get(&key)?; @@ -102,7 +102,7 @@ impl ResolveCache { } /// Insert a cache entry. - pub fn insert(&mut self, cwd: &AbsolutePath, entry: ResolveCacheEntry) { + pub(crate) fn insert(&mut self, cwd: &AbsolutePath, entry: ResolveCacheEntry) { let key = cwd.as_path().to_string_lossy().to_string(); // LRU eviction if needed @@ -183,27 +183,27 @@ impl ResolveCache { } /// Get the cache file path (`/resolve_cache.json`). -pub fn get_cache_path() -> Option { +pub(crate) fn get_cache_path() -> Option { Some(vp_shared::EnvConfig::get().dirs.cache.join("resolve_cache.json")) } /// Invalidate the entire resolve cache by deleting the cache file. /// Called after version configuration changes (e.g., `vp env default`, `vp env pin`, `vp env unpin`). -pub fn invalidate_cache() { +pub(crate) fn invalidate_cache() { if let Some(cache_path) = get_cache_path() { std::fs::remove_file(cache_path.as_path()).ok(); } } /// Get the mtime of a file as Unix timestamp. -pub fn get_file_mtime(path: &AbsolutePath) -> Option { +pub(crate) fn get_file_mtime(path: &AbsolutePath) -> Option { let metadata = std::fs::metadata(path).ok()?; let mtime = metadata.modified().ok()?; mtime.duration_since(UNIX_EPOCH).map(|d| d.as_secs()).ok() } /// Get the current Unix timestamp. -pub fn now_timestamp() -> u64 { +pub(crate) fn now_timestamp() -> u64 { SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0) } diff --git a/crates/vp_global_cli/src/shim/dispatch.rs b/crates/vp_global_cli/src/shim/dispatch.rs index 807e64af0d..bda289931c 100644 --- a/crates/vp_global_cli/src/shim/dispatch.rs +++ b/crates/vp_global_cli/src/shim/dispatch.rs @@ -695,7 +695,7 @@ async fn prepend_js_child_process_path_env( /// /// Called when the binary is invoked as a core shim or package binary. /// Returns an exit code to be used with std::process::exit. -pub async fn dispatch(tool: &str, args: &[String]) -> i32 { +pub(crate) async fn dispatch(tool: &str, args: &[String]) -> i32 { tracing::debug!("dispatch: tool: {tool}, args: {:?}", args); // Handle vpx — standalone command, doesn't need recursion/bypass/shim-mode checks diff --git a/crates/vp_global_cli/src/shim/exec.rs b/crates/vp_global_cli/src/shim/exec.rs index 2fe5d0b7f9..2c6da69d3e 100644 --- a/crates/vp_global_cli/src/shim/exec.rs +++ b/crates/vp_global_cli/src/shim/exec.rs @@ -23,7 +23,7 @@ fn sync_child_pwd(cmd: &mut std::process::Command) { /// /// Unlike `exec_tool()`, this does NOT replace the current process on Unix, /// allowing the caller to run code after the tool exits. -pub fn spawn_tool(path: &AbsolutePath, args: &[String]) -> i32 { +pub(crate) fn spawn_tool(path: &AbsolutePath, args: &[String]) -> i32 { let mut cmd = std::process::Command::new(path.as_path()); cmd.args(args); sync_child_pwd(&mut cmd); @@ -39,7 +39,7 @@ pub fn spawn_tool(path: &AbsolutePath, args: &[String]) -> i32 { /// Execute a tool, replacing the current process on Unix. /// /// Returns an exit code on Windows or if exec fails on Unix. -pub fn exec_tool(path: &AbsolutePath, args: &[String]) -> i32 { +pub(crate) fn exec_tool(path: &AbsolutePath, args: &[String]) -> i32 { #[cfg(unix)] { exec_unix(path, args) diff --git a/crates/vp_global_cli/src/shim/mod.rs b/crates/vp_global_cli/src/shim/mod.rs index 10b58551be..0bd16bd763 100644 --- a/crates/vp_global_cli/src/shim/mod.rs +++ b/crates/vp_global_cli/src/shim/mod.rs @@ -15,14 +15,13 @@ pub(crate) mod exec; use std::fs; pub(crate) use cache::invalidate_cache; -pub use dispatch::dispatch; -pub(crate) use dispatch::find_system_tool; +pub(crate) use dispatch::{dispatch, find_system_tool}; use vp_shared::env_vars; use crate::commands::env::config::get_bin_dir; /// Core shim tools managed directly by the main dispatch path. -pub const CORE_SHIM_TOOLS: &[&str] = +pub(crate) const CORE_SHIM_TOOLS: &[&str] = &["node", "npm", "npx", "pnpm", "pnpx", "yarn", "yarnpkg", "bun", "bunx"]; /// Extract the tool name from argv[0]. @@ -33,7 +32,7 @@ pub const CORE_SHIM_TOOLS: &[&str] = /// - `/usr/bin/node` (Unix full path) /// - `node.exe` (Windows) /// - `C:\path\node.exe` (Windows full path) -pub fn extract_tool_name(argv0: &str) -> String { +pub(crate) fn extract_tool_name(argv0: &str) -> String { let path = std::path::Path::new(argv0); // Handle Windows: strip .exe, .cmd extensions if present in stem @@ -66,7 +65,7 @@ pub fn extract_tool_name(argv0: &str) -> String { /// Check if the given tool name is managed directly by the core shim path. #[must_use] -pub fn is_core_shim_tool(tool: &str) -> bool { +pub(crate) fn is_core_shim_tool(tool: &str) -> bool { CORE_SHIM_TOOLS.contains(&tool) } @@ -76,7 +75,7 @@ pub fn is_core_shim_tool(tool: &str) -> bool { /// 1. The tool is a core shim, OR /// 2. The tool name is not "vp" (package binaries are detected later via metadata) #[must_use] -pub fn is_shim_tool(tool: &str) -> bool { +pub(crate) fn is_shim_tool(tool: &str) -> bool { // Core tools are always shims if is_core_shim_tool(tool) { return true; @@ -144,7 +143,7 @@ const LEGACY_SHIM_TOOL_ENV_VAR: &str = "VITE_PLUS_SHIM_TOOL"; /// /// IMPORTANT: This function clears both env vars after reading to /// prevent them from leaking to child processes. -pub fn detect_shim_tool(argv0: &str) -> Option { +pub(crate) fn detect_shim_tool(argv0: &str) -> Option { // Always clear both env vars to prevent them from leaking to child processes. // We read them first, then clear immediately. // SAFETY: We're at program startup before any threads are spawned. diff --git a/crates/vp_global_cli/src/upgrade_check.rs b/crates/vp_global_cli/src/upgrade_check.rs index 61a09516ed..0ea92c15b0 100644 --- a/crates/vp_global_cli/src/upgrade_check.rs +++ b/crates/vp_global_cli/src/upgrade_check.rs @@ -227,7 +227,7 @@ fn configure_background_process(command: &mut Command) { /// Refresh the cached update status. This function intentionally runs in the /// helper process so the foreground command never waits for registry I/O. -pub async fn run_background_check() { +pub(crate) async fn run_background_check() { let config = vp_shared::EnvConfig::get(); let cache_dir = &config.dirs.cache; let data_dir = &config.dirs.data; @@ -285,7 +285,7 @@ pub async fn run_background_check() { /// Print a one-line upgrade notice from cache and record the prompt time. #[expect(clippy::print_stderr, clippy::disallowed_macros)] -pub fn display_cached_upgrade_notice() { +pub(crate) fn display_cached_upgrade_notice() { if checks_disabled() { return; } @@ -323,7 +323,7 @@ pub fn display_cached_upgrade_notice() { /// Whether a foreground command may run the upgrade check and display its cached notice. /// Returns `false` for commands excluded by design, quiet modes, and /// machine-readable output flags (--silent, -s, --json, --parseable, --format json). -pub fn should_run_for_command(args: &crate::cli::Args) -> bool { +pub(crate) fn should_run_for_command(args: &crate::cli::Args) -> bool { if !cfg!(test) && !vp_shared::is_stderr_terminal() { return false; } diff --git a/crates/vp_installer/src/cli.rs b/crates/vp_installer/src/cli.rs index db5f8a4eec..cf24f4cf80 100644 --- a/crates/vp_installer/src/cli.rs +++ b/crates/vp_installer/src/cli.rs @@ -5,39 +5,39 @@ use clap::Parser; /// Vite+ Installer — standalone installer for the vp CLI. #[derive(Parser, Debug)] #[command(name = "vp-setup", about = "Install the Vite+ CLI")] -pub struct Options { +pub(crate) struct Options { /// Accept defaults without prompting (for CI/unattended installs) #[arg(short = 'y', long = "yes")] - pub yes: bool, + pub(crate) yes: bool, /// Suppress all output except errors #[arg(short = 'q', long = "quiet")] - pub quiet: bool, + pub(crate) quiet: bool, /// Install a specific version (default: latest) #[arg(long = "version")] - pub version: Option, + pub(crate) version: Option, /// npm dist-tag to install (default: latest) #[arg(long = "tag", default_value = "latest")] - pub tag: String, + pub(crate) tag: String, /// Custom npm registry URL #[arg(long = "registry")] - pub registry: Option, + pub(crate) registry: Option, /// Skip Node.js version manager setup #[arg(long = "no-node-manager")] - pub no_node_manager: bool, + pub(crate) no_node_manager: bool, /// Do not modify the User PATH #[arg(long = "no-modify-path")] - pub no_modify_path: bool, + pub(crate) no_modify_path: bool, } /// Parse CLI arguments, merging with environment variables. /// CLI flags take precedence over environment variables. -pub fn parse() -> Options { +pub(crate) fn parse() -> Options { let mut opts = Options::parse(); // Merge env var overrides (CLI flags already set take precedence) diff --git a/crates/vp_installer/src/windows_path.rs b/crates/vp_installer/src/windows_path.rs index 3bbe1212e8..b33c276cbb 100644 --- a/crates/vp_installer/src/windows_path.rs +++ b/crates/vp_installer/src/windows_path.rs @@ -44,7 +44,7 @@ fn broadcast_settings_change() { } /// Add a directory to the User PATH (`HKCU\Environment\Path`) if not already present. -pub fn add_to_user_path(bin_dir: &str) -> io::Result<()> { +pub(crate) fn add_to_user_path(bin_dir: &str) -> io::Result<()> { let hkcu = RegKey::predef(HKEY_CURRENT_USER); let env = hkcu.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)?; diff --git a/crates/vp_js_runtime/src/cache.rs b/crates/vp_js_runtime/src/cache.rs index ed2b250ae7..c44c795db3 100644 --- a/crates/vp_js_runtime/src/cache.rs +++ b/crates/vp_js_runtime/src/cache.rs @@ -7,6 +7,6 @@ use crate::Error; /// Get the cache directory for JavaScript runtimes. /// /// Returns `/js_runtime`. -pub fn get_cache_dir() -> Result { +pub(crate) fn get_cache_dir() -> Result { Ok(vp_shared::EnvConfig::get().dirs.data.join("js_runtime")) } diff --git a/crates/vp_js_runtime/src/dev_engines.rs b/crates/vp_js_runtime/src/dev_engines.rs index 418454d280..ea4470c871 100644 --- a/crates/vp_js_runtime/src/dev_engines.rs +++ b/crates/vp_js_runtime/src/dev_engines.rs @@ -6,7 +6,7 @@ //! For `PackageJson` types (devEngines, engines), see `vp_shared::package_json`. // Re-export shared types for internal use -pub use vp_shared::PackageJson; +pub(crate) use vp_shared::PackageJson; use vt_path::AbsolutePath; use vt_str::Str; @@ -26,7 +26,7 @@ use vt_str::Str; /// LTS aliases are preserved as-is (e.g., `lts/iron` stays `lts/iron`). /// Returns `None` if the content is empty or contains only whitespace. #[must_use] -pub fn parse_node_version_content(content: &str) -> Option { +fn parse_node_version_content(content: &str) -> Option { let version = content.lines().next()?.trim(); if version.is_empty() { return None; @@ -49,7 +49,7 @@ pub fn parse_node_version_content(content: &str) -> Option { /// /// # Returns /// The version string if the file exists and contains a valid version. -pub async fn read_node_version_file(project_path: &AbsolutePath) -> Option { +pub(super) async fn read_node_version_file(project_path: &AbsolutePath) -> Option { let path = project_path.join(".node-version"); let content = tokio::fs::read_to_string(&path).await.ok()?; parse_node_version_content(&content) diff --git a/crates/vp_js_runtime/src/download.rs b/crates/vp_js_runtime/src/download.rs index 89779ba829..b6a7bb25c9 100644 --- a/crates/vp_js_runtime/src/download.rs +++ b/crates/vp_js_runtime/src/download.rs @@ -20,22 +20,22 @@ use vt_str::Str; use crate::{Error, provider::ArchiveFormat}; /// Response from a cached fetch operation -pub struct CachedFetchResponse { +pub(crate) struct CachedFetchResponse { /// Deserialized response body (None if 304 Not Modified) - pub body: Option, + pub(crate) body: Option, /// `ETag` header value - pub etag: Option, + pub(crate) etag: Option, /// Cache max-age in seconds (from Cache-Control header) - pub max_age: Option, + pub(crate) max_age: Option, /// Whether this was a 304 Not Modified response - pub not_modified: bool, + pub(crate) not_modified: bool, } /// Download a file with retry logic and progress bar /// /// The `message` parameter is displayed to the user to indicate what is being downloaded /// (e.g., "Downloading Node.js v22.13.1"). -pub async fn download_file( +pub(crate) async fn download_file( url: &str, target_path: &AbsolutePath, message: &str, @@ -277,7 +277,7 @@ fn full_response(response: reqwest::Response) -> Result Result { +pub(crate) async fn download_text(url: &str) -> Result { let client = vp_shared::shared_http_client()?; tracing::debug!("Downloading text from {url}"); @@ -303,7 +303,7 @@ pub async fn download_text(url: &str) -> Result { /// If `if_none_match` is provided, sends `If-None-Match` header for conditional request. /// The request, response body, and JSON decoding are retried as one operation so a /// truncated body cannot escape the retry boundary as a deserialization error. -pub async fn fetch_json_with_cache_headers( +pub(crate) async fn fetch_json_with_cache_headers( url: &str, if_none_match: Option<&str>, ) -> Result, Error> { @@ -379,7 +379,7 @@ fn parse_max_age(cache_control: &str) -> Option { } /// Verify file hash against expected SHA256 hash -pub async fn verify_file_hash( +pub(crate) async fn verify_file_hash( file_path: &AbsolutePath, expected_hash: &str, filename: &str, @@ -405,7 +405,7 @@ pub async fn verify_file_hash( } /// Extract archive based on format -pub async fn extract_archive( +pub(crate) async fn extract_archive( archive_path: &AbsolutePath, target_dir: &AbsolutePath, format: ArchiveFormat, @@ -458,7 +458,7 @@ fn extract_zip(archive_path: &AbsolutePath, target_dir: &AbsolutePath) -> Result /// /// Uses a file-based lock to ensure atomicity when multiple processes/threads /// try to install the same runtime version concurrently. -pub async fn move_to_cache( +pub(crate) async fn move_to_cache( source: &AbsolutePath, target: &AbsolutePathBuf, binary_path: &AbsolutePath, diff --git a/crates/vp_js_runtime/src/lib.rs b/crates/vp_js_runtime/src/lib.rs index fb899f64e5..d6fa2d9205 100644 --- a/crates/vp_js_runtime/src/lib.rs +++ b/crates/vp_js_runtime/src/lib.rs @@ -53,13 +53,13 @@ mod provider; mod providers; mod runtime; -pub use dev_engines::{parse_node_version_content, read_node_version_file, read_nvmrc_file}; +pub use dev_engines::read_nvmrc_file; pub use error::Error; pub use platform::{Arch, Os, Platform}; pub use provider::{ ArchiveFormat, DownloadInfo, HashVerification, JsRuntimeProvider, ShasumsSignature, }; -pub use providers::{LtsInfo, NodeProvider, NodeVersionEntry, resolve_version_from_list}; +pub use providers::{LtsInfo, NodeProvider, NodeVersionEntry}; pub use runtime::{ JsRuntime, JsRuntimeType, VersionResolution, VersionSource, download_runtime, download_runtime_for_project, download_runtime_with_provider, is_valid_version, diff --git a/crates/vp_js_runtime/src/pgp_verify.rs b/crates/vp_js_runtime/src/pgp_verify.rs index 0674355dde..970cd02f00 100644 --- a/crates/vp_js_runtime/src/pgp_verify.rs +++ b/crates/vp_js_runtime/src/pgp_verify.rs @@ -61,7 +61,10 @@ const NODE_RELEASE_KEYS_ARMOR: &str = include_str!("assets/node-release-keys.asc /// /// Returns [`Error::SignatureVerificationFailed`] if the message cannot be /// parsed or no embedded release key produced a valid signature. -pub async fn verify_signed_shasums(signed_armor: String, filename: &str) -> Result { +pub(crate) async fn verify_signed_shasums( + signed_armor: String, + filename: &str, +) -> Result { let filename: Str = filename.into(); tokio::task::spawn_blocking(move || { verify_clearsigned(&signed_armor, node_release_keys()).map_err(|reason| { diff --git a/crates/vp_js_runtime/src/providers/mod.rs b/crates/vp_js_runtime/src/providers/mod.rs index 866c88b415..96230597d7 100644 --- a/crates/vp_js_runtime/src/providers/mod.rs +++ b/crates/vp_js_runtime/src/providers/mod.rs @@ -5,4 +5,4 @@ mod node; -pub use node::{LtsInfo, NodeProvider, NodeVersionEntry, resolve_version_from_list}; +pub use node::{LtsInfo, NodeProvider, NodeVersionEntry}; diff --git a/crates/vp_js_runtime/src/providers/node.rs b/crates/vp_js_runtime/src/providers/node.rs index abb97890ba..faf39da753 100644 --- a/crates/vp_js_runtime/src/providers/node.rs +++ b/crates/vp_js_runtime/src/providers/node.rs @@ -496,7 +496,7 @@ fn find_absolute_latest_version(versions: &[NodeVersionEntry]) -> Result Result { diff --git a/crates/vp_migration/src/ast_grep.rs b/crates/vp_migration/src/ast_grep.rs index 8dc829d90e..5e74065f0d 100644 --- a/crates/vp_migration/src/ast_grep.rs +++ b/crates/vp_migration/src/ast_grep.rs @@ -18,7 +18,7 @@ use vp_error::Error; /// # Returns /// /// A tuple of (`transformed_content`, `was_updated`) -pub fn apply_rules(content: &str, rule_yaml: &str) -> Result<(String, bool), Error> { +pub(crate) fn apply_rules(content: &str, rule_yaml: &str) -> Result<(String, bool), Error> { let rules = load_rules(rule_yaml)?; let result = apply_loaded_rules(content, &rules); let updated = result != content; @@ -26,7 +26,7 @@ pub fn apply_rules(content: &str, rule_yaml: &str) -> Result<(String, bool), Err } /// Load ast-grep rules from YAML string -pub fn load_rules(yaml: &str) -> Result>, Error> { +pub(crate) fn load_rules(yaml: &str) -> Result>, Error> { let globals = GlobalRules::default(); let rules: Vec> = from_yaml_string::(yaml, &globals)?; Ok(rules) @@ -45,7 +45,7 @@ pub fn load_rules(yaml: &str) -> Result>, Error> { /// # Returns /// /// The transformed content (always returns a new string, even if unchanged) -pub fn apply_loaded_rules(content: &str, rules: &[RuleConfig]) -> String { +pub(crate) fn apply_loaded_rules(content: &str, rules: &[RuleConfig]) -> String { let mut current = content.to_string(); for rule in rules { diff --git a/crates/vp_migration/src/eslint.rs b/crates/vp_migration/src/eslint.rs index 2dfb8d96c2..6d71f154b3 100644 --- a/crates/vp_migration/src/eslint.rs +++ b/crates/vp_migration/src/eslint.rs @@ -24,7 +24,7 @@ const ESLINT_CONFIG: ScriptRewriteConfig = ScriptRewriteConfig { }; /// Rewrite a single script: rename `eslint` → `vp lint` and strip ESLint-only flags. -pub fn rewrite_eslint_script(script: &str) -> String { +pub(crate) fn rewrite_eslint_script(script: &str) -> String { rewrite_script(script, &ESLINT_CONFIG) } diff --git a/crates/vp_migration/src/file_walker.rs b/crates/vp_migration/src/file_walker.rs index fe847ffc45..35be97d44c 100644 --- a/crates/vp_migration/src/file_walker.rs +++ b/crates/vp_migration/src/file_walker.rs @@ -12,9 +12,9 @@ const TS_JS_EXTENSIONS: &[&str] = &["ts", "tsx", "mts", "cts", "js", "jsx", "mjs /// Result of walking TypeScript/JavaScript files #[derive(Debug)] -pub struct WalkResult { +pub(crate) struct WalkResult { /// List of file paths found - pub files: Vec, + pub(crate) files: Vec, } /// Find all TypeScript/JavaScript files in a directory, respecting gitignore @@ -48,7 +48,7 @@ pub struct WalkResult { /// println!("Found: {}", file.display()); /// } /// ``` -pub fn find_ts_files(root: &Path) -> Result { +pub(crate) fn find_ts_files(root: &Path) -> Result { let mut files = Vec::new(); let walker = WalkBuilder::new(root) diff --git a/crates/vp_migration/src/import_rewriter.rs b/crates/vp_migration/src/import_rewriter.rs index 62f377d750..559c3e9eeb 100644 --- a/crates/vp_migration/src/import_rewriter.rs +++ b/crates/vp_migration/src/import_rewriter.rs @@ -2165,7 +2165,8 @@ enum FileResult { /// println!(" {}", file.display()); /// } /// ``` -pub fn rewrite_imports_in_directory(root: &Path) -> Result { +#[cfg(test)] +fn rewrite_imports_in_directory(root: &Path) -> Result { rewrite_imports_in_directory_with_options(root, RewriteImportsOptions::default()) } diff --git a/crates/vp_migration/src/lib.rs b/crates/vp_migration/src/lib.rs index 855f23cd9b..d3e077f8bf 100644 --- a/crates/vp_migration/src/lib.rs +++ b/crates/vp_migration/src/lib.rs @@ -15,10 +15,8 @@ mod prettier; mod script_rewrite; mod vite_config; -pub use file_walker::{WalkResult, find_ts_files}; pub use import_rewriter::{ - BatchRewriteResult, RewriteImportsOptions, rewrite_imports_in_directory, - rewrite_imports_in_directory_with_options, + BatchRewriteResult, RewriteImportsOptions, rewrite_imports_in_directory_with_options, }; pub use package::{rewrite_eslint, rewrite_prettier, rewrite_scripts}; pub use vite_config::{ diff --git a/crates/vp_migration/src/prettier.rs b/crates/vp_migration/src/prettier.rs index d4505cb68d..4fc8fef920 100644 --- a/crates/vp_migration/src/prettier.rs +++ b/crates/vp_migration/src/prettier.rs @@ -60,7 +60,7 @@ const PRETTIER_CONFIG: ScriptRewriteConfig = ScriptRewriteConfig { /// Rewrite a single script: rename `prettier` → `vp fmt`, strip Prettier-only flags, /// and convert `--list-different`/`-l` → `--check`. -pub fn rewrite_prettier_script(script: &str) -> String { +pub(crate) fn rewrite_prettier_script(script: &str) -> String { rewrite_script(script, &PRETTIER_CONFIG) } diff --git a/crates/vp_migration/src/script_rewrite.rs b/crates/vp_migration/src/script_rewrite.rs index 753ed60f4c..d7e23fb690 100644 --- a/crates/vp_migration/src/script_rewrite.rs +++ b/crates/vp_migration/src/script_rewrite.rs @@ -2,7 +2,7 @@ use brush_parser::ast; /// Configuration for converting one flag (or set of aliases) into a different flag. /// Example: Prettier's `--list-different`/`-l` → `--check`. -pub struct FlagConversion { +pub(crate) struct FlagConversion { /// Source flags that should be converted (e.g. `["--list-different", "-l"]`). pub(crate) source_flags: &'static [&'static str], /// The target flag to emit instead (e.g. `"--check"`). @@ -13,7 +13,7 @@ pub struct FlagConversion { } /// Tool-specific configuration for script rewriting. -pub struct ScriptRewriteConfig { +pub(crate) struct ScriptRewriteConfig { /// The source command name to match (e.g. `"prettier"`, `"eslint"`). pub(crate) source_command: &'static str, /// The `vp` subcommand to emit (e.g. `"fmt"`, `"lint"`). @@ -31,7 +31,7 @@ const SHELL_CONTINUATION_KEYWORDS: &[&str] = &["then", "do", "else", "elif", "in /// Rewrite a shell script: find `source_command`, rename to `vp `, /// strip tool-specific flags, and normalize the output. -pub fn rewrite_script(script: &str, config: &ScriptRewriteConfig) -> String { +pub(crate) fn rewrite_script(script: &str, config: &ScriptRewriteConfig) -> String { let rewritten_bunx = rewrite_bunx_commands(script, |inner| rewrite_direct_script(inner, config)); rewrite_direct_script(&rewritten_bunx, config) diff --git a/crates/vp_pm_cli/src/helpers.rs b/crates/vp_pm_cli/src/helpers.rs index ae47fb238d..8033455e75 100644 --- a/crates/vp_pm_cli/src/helpers.rs +++ b/crates/vp_pm_cli/src/helpers.rs @@ -6,7 +6,7 @@ use crate::{PackageManager, PackageManagerType, error::Error}; /// Build a `PackageManager`, converting `PackageJsonNotFound` into a /// friendly error message. -pub async fn build_package_manager(cwd: &AbsolutePath) -> Result { +pub(crate) async fn build_package_manager(cwd: &AbsolutePath) -> Result { match PackageManager::builder(cwd).build_with_default().await { Ok(pm) => Ok(pm), Err(vp_error::Error::WorkspaceError(vt_workspace::Error::PackageJsonNotFound(_))) => { @@ -23,7 +23,7 @@ pub async fn build_package_manager(cwd: &AbsolutePath) -> Result Result { match PackageManager::builder(cwd).build().await { @@ -44,7 +44,7 @@ pub(crate) fn default_npm_package_manager(cwd: &AbsolutePath) -> PackageManager /// Ensure a package.json exists in the given directory. /// If it doesn't exist, create a minimal one with `{ "type": "module" }`. -pub async fn ensure_package_json(project_path: &AbsolutePath) -> Result<(), Error> { +pub(crate) async fn ensure_package_json(project_path: &AbsolutePath) -> Result<(), Error> { use tokio::io::AsyncWriteExt; let package_json_path = project_path.join("package.json"); diff --git a/crates/vp_pm_cli/src/lib.rs b/crates/vp_pm_cli/src/lib.rs index 9105a7e8ea..ee29979d99 100644 --- a/crates/vp_pm_cli/src/lib.rs +++ b/crates/vp_pm_cli/src/lib.rs @@ -29,9 +29,9 @@ pub use package_manager::{ }; pub use request::HttpClient; pub use resolution::{ - AddArgs, ApproveBuildsArgs, AuditArgs, CacheArgs, ConfigCommand, DedupeArgs, DeprecateArgs, - DistTagCommand, DlxArgs, FundArgs, InstallArgs, LinkArgs, ListArgs, LoginArgs, LogoutArgs, - OutdatedArgs, OutdatedFormat, OwnerCommand, PackArgs, PingArgs, PruneArgs, PublishArgs, - RebuildArgs, RemoveArgs, SearchArgs, StageCommand, TokenCommand, UnlinkArgs, UpdateArgs, - VersionArgs, ViewArgs, WhoamiArgs, WhyArgs, + AddArgs, ApproveBuildsArgs, AuditArgs, CacheArgs, CiArgs, ConfigCommand, DedupeArgs, + DeprecateArgs, DistTagCommand, DlxArgs, FundArgs, InstallArgs, LinkArgs, ListArgs, LoginArgs, + LogoutArgs, OutdatedArgs, OutdatedFormat, OwnerCommand, PackArgs, PatchArgs, PatchCommitArgs, + PingArgs, PruneArgs, PublishArgs, RebuildArgs, RemoveArgs, SearchArgs, StageCommand, + TokenCommand, UnlinkArgs, UpdateArgs, VersionArgs, ViewArgs, WhoamiArgs, WhyArgs, }; diff --git a/crates/vp_shared/src/dirs/resolution.rs b/crates/vp_shared/src/dirs/resolution.rs index d2d4b4f6d5..0419194a51 100644 --- a/crates/vp_shared/src/dirs/resolution.rs +++ b/crates/vp_shared/src/dirs/resolution.rs @@ -185,7 +185,7 @@ macro_rules! resolutions { /// Resolve this category through the source chain. The first candidate /// wins. The caller resolves and provides `home`. Sources that do not /// need it ignore it. - pub fn $method(home: &AbsolutePath) -> Option { + pub(super) fn $method(home: &AbsolutePath) -> Option { $({ let source = <$resolution>::resolver(home); if let Some(dir) = source.$method() { diff --git a/crates/vp_trampoline/Cargo.toml b/crates/vp_trampoline/Cargo.toml index 60d51c8cdb..4d7c7f743e 100644 --- a/crates/vp_trampoline/Cargo.toml +++ b/crates/vp_trampoline/Cargo.toml @@ -38,6 +38,10 @@ path = "src/main.rs" # This crate does not inherit workspace lints. # It uses std types and macros directly to keep the binary small. # Thus, allow the .clippy.toml rules that require shared project abstractions. +[lints.rust] +dead_code = "deny" +unreachable_pub = "deny" + [lints.clippy] disallowed_macros = "allow" disallowed_types = "allow" diff --git a/crates/vp_trampoline/src/cmdline.rs b/crates/vp_trampoline/src/cmdline.rs index 9941a590fa..7f60a5f9fa 100644 --- a/crates/vp_trampoline/src/cmdline.rs +++ b/crates/vp_trampoline/src/cmdline.rs @@ -18,18 +18,18 @@ const VERBATIM_PREFIX: &[u16] = &[BACKSLASH, BACKSLASH, QUESTION, BACKSLASH]; const UNC_PREFIX: &[u16] = &[BACKSLASH, BACKSLASH, QUESTION, BACKSLASH, U, N, C, BACKSLASH]; /// Must match `vp_shared::SHIM_POINTER_HEADER`. -pub const SHIM_POINTER_HEADER: &str = "vite-plus-shim-v1"; +pub(crate) const SHIM_POINTER_HEADER: &str = "vite-plus-shim-v1"; #[derive(Debug, PartialEq, Eq)] -pub enum ShimLayout<'a> { +pub(crate) enum ShimLayout<'a> { SingleRoot, Split { cache: &'a str }, } #[derive(Debug, PartialEq, Eq)] -pub struct ShimPointer<'a> { - pub data: &'a str, - pub layout: ShimLayout<'a>, +pub(crate) struct ShimPointer<'a> { + pub(crate) data: &'a str, + pub(crate) layout: ShimLayout<'a>, } /// Parse the UTF-8 `.shim` sidecar written by `vp_shared::VpDirs`. @@ -37,7 +37,7 @@ pub struct ShimPointer<'a> { /// A sidecar records the directory layout, data root, and cache root. /// The parser requires the versioned header. /// The parser supports a UTF-8 BOM and CRLF line endings, as `vp_shared` does. -pub fn parse_shim_pointer(bytes: &[u8]) -> Option> { +pub(crate) fn parse_shim_pointer(bytes: &[u8]) -> Option> { let bytes = bytes.strip_prefix(&[0xEF, 0xBB, 0xBF]).unwrap_or(bytes); let text = core::str::from_utf8(bytes).ok()?.trim(); let mut lines = text.lines(); @@ -75,7 +75,7 @@ pub fn parse_shim_pointer(bytes: &[u8]) -> Option> { /// Leading whitespace ends an empty program argument. /// Forward `&cmdline[result..]` to the child without changes. /// This remaining text includes its leading whitespace. -pub fn skip_program_argument(cmdline: &[u16]) -> usize { +pub(crate) fn skip_program_argument(cmdline: &[u16]) -> usize { let mut i = 0; let mut quoted = false; while i < cmdline.len() { @@ -99,7 +99,7 @@ fn is_path_separator(unit: u16) -> bool { /// Before the call, resolve `.` and `..` components. /// Before the call, replace `/` separators. /// The extended-length namespace uses the remaining path without changes. -pub fn verbatim_path(path: &[u16]) -> Vec { +pub(crate) fn verbatim_path(path: &[u16]) -> Vec { let (prefix, tail) = match path { // Keep an existing extended-length or NT namespace. [BACKSLASH, BACKSLASH, QUESTION, BACKSLASH, ..] @@ -125,7 +125,7 @@ pub fn verbatim_path(path: &[u16]) -> Vec { /// Without the separator, `C:\vp.exe` produces the drive-relative path `C:`. /// Device roots such as `\\?\Volume{...}\vp.exe` have the same constraint. /// Other parent paths omit the final separator, as `Path::parent` does. -pub fn parent_dir_len(path: &[u16], last_separator: usize) -> usize { +pub(crate) fn parent_dir_len(path: &[u16], last_separator: usize) -> usize { let drive_root = last_separator >= 1 && path[last_separator - 1] == COLON; let device_root = last_separator >= 4 && is_path_separator(path[0]) @@ -143,7 +143,7 @@ pub fn parent_dir_len(path: &[u16], last_separator: usize) -> usize { /// Return the file-stem length, as `Path::file_stem` does. /// The stem ends before the last `.`, but a leading `.` does not start an extension. -pub fn file_stem_len(name: &[u16]) -> usize { +pub(crate) fn file_stem_len(name: &[u16]) -> usize { match name.iter().skip(1).rposition(|&c| c == DOT) { Some(pos) => pos + 1, None => name.len(), @@ -151,13 +151,13 @@ pub fn file_stem_len(name: &[u16]) -> usize { } /// Case-sensitive comparison of a UTF-16 slice against an ASCII string. -pub fn eq_ascii(wide: &[u16], ascii: &[u8]) -> bool { +pub(crate) fn eq_ascii(wide: &[u16], ascii: &[u8]) -> bool { wide.len() == ascii.len() && wide.iter().zip(ascii).all(|(&w, &a)| w == u16::from(a)) } /// Format `value` as decimal ASCII in `buf`. /// Return the used suffix. -pub fn format_u32(mut value: u32, buf: &mut [u8; 10]) -> &[u8] { +pub(crate) fn format_u32(mut value: u32, buf: &mut [u8; 10]) -> &[u8] { let mut i = buf.len(); loop { i -= 1; diff --git a/crates/vp_trampoline/src/main.rs b/crates/vp_trampoline/src/main.rs index 317e8393ed..d44b09fcc4 100644 --- a/crates/vp_trampoline/src/main.rs +++ b/crates/vp_trampoline/src/main.rs @@ -100,7 +100,7 @@ mod portable { Some(ShimPointer { data: PathBuf::from(parsed.data), layout }) } - pub fn run() { + pub(super) fn run() { // 1. Determine the tool name from our own executable filename. let exe_path = env::current_exe().unwrap_or_else(|_| process::exit(1)); let tool_name = diff --git a/crates/vp_trampoline/src/win.rs b/crates/vp_trampoline/src/win.rs index 26faa1a869..10851f91d5 100644 --- a/crates/vp_trampoline/src/win.rs +++ b/crates/vp_trampoline/src/win.rs @@ -142,7 +142,7 @@ unsafe extern "system" { // ExitProcess does not run TLS destructors. // Thus, this process uses a successful no-op implementation. #[unsafe(no_mangle)] -pub extern "C" fn atexit(_f: Option) -> i32 { +pub(crate) extern "C" fn atexit(_f: Option) -> i32 { 0 } @@ -441,7 +441,7 @@ unsafe extern "system" fn ignore_ctrl(_ctrl_type: u32) -> i32 { 1 } -pub fn run() -> ! { +pub(super) fn run() -> ! { // 1. Resolve the tool, bin directory, and per-tool sidecar from our path. let exe = module_path(); let Some(last_separator) = exe.iter().rposition(|&unit| is_separator(unit)) else { diff --git a/justfile b/justfile index d2fed929b5..54b00d0e17 100644 --- a/justfile +++ b/justfile @@ -57,7 +57,7 @@ watch *args='': watchexec --no-vcs-ignore {{args}} fmt: - cargo shear --fix + cargo shear --fix --exclude 'rolldown*' --exclude string_wizard cargo fmt --all cargo fmt --manifest-path crates/vp_trampoline/Cargo.toml pnpm fmt @@ -123,7 +123,7 @@ snapshot-test-global *args='': $Env:VP_SNAP_SKIP_FLAVORS='local'; just snapshot-test {{args}} # Single source of truth for clippy, used by CI too. The `-A` flags allow -# new toolchain lints that fire in upstream rolldown crates without a `[lints]` table. +# new toolchain lints that fire in upstream dependencies. lint: cargo clippy --workspace --all-targets --all-features -- --deny warnings \ -A clippy::byte_char_slices \ diff --git a/packages/cli/binding/src/check/analysis.rs b/packages/cli/binding/src/check/analysis.rs index ae94393b48..a17c8cebad 100644 --- a/packages/cli/binding/src/check/analysis.rs +++ b/packages/cli/binding/src/check/analysis.rs @@ -3,34 +3,34 @@ use vp_shared::output; #[derive(Debug, Clone)] pub(super) struct CheckSummary { - pub duration: String, - pub files: usize, - pub threads: usize, + pub(crate) duration: String, + pub(crate) files: usize, + pub(crate) threads: usize, } #[derive(Debug)] pub(super) struct FmtSuccess { - pub summary: CheckSummary, + pub(crate) summary: CheckSummary, } #[derive(Debug)] pub(super) struct FmtFailure { - pub summary: CheckSummary, - pub issue_files: Vec, - pub issue_count: usize, + pub(crate) summary: CheckSummary, + pub(crate) issue_files: Vec, + pub(crate) issue_count: usize, } #[derive(Debug)] pub(super) struct LintSuccess { - pub summary: CheckSummary, + pub(crate) summary: CheckSummary, } #[derive(Debug)] pub(super) struct LintFailure { - pub summary: CheckSummary, - pub warnings: usize, - pub errors: usize, - pub diagnostics: String, + pub(crate) summary: CheckSummary, + pub(crate) warnings: usize, + pub(crate) errors: usize, + pub(crate) diagnostics: String, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] diff --git a/packages/cli/binding/src/cli/mod.rs b/packages/cli/binding/src/cli/mod.rs index e370d1eaea..fa899fb882 100644 --- a/packages/cli/binding/src/cli/mod.rs +++ b/packages/cli/binding/src/cli/mod.rs @@ -17,15 +17,14 @@ use clap::Parser; use cow_utils::CowUtils; pub(crate) use execution::resolve_and_capture_output; // Re-exports for lib.rs and check/mod.rs -pub use resolver::SubcommandResolver; +pub(crate) use resolver::SubcommandResolver; use rustc_hash::FxHashMap; -pub(crate) use types::CapturedCommandOutput; -pub use types::{ - BoxedResolverFn, CliOptions, ResolveCommandResult, SynthesizableSubcommand, ToolchainArgs, - ViteConfigResolverFn, +pub(crate) use types::{ + BoxedResolverFn, CapturedCommandOutput, CliOptions, ResolveCommandResult, + SynthesizableSubcommand, ToolchainArgs, ViteConfigResolverFn, }; use vp_error::Error; -pub use vp_shared::init_tracing; +pub(crate) use vp_shared::init_tracing; use vp_shared::{PrependOptions, env_vars, prepend_to_path_env}; use vt::{ExitStatus, Session, SessionConfig}; use vt_path::{AbsolutePath, AbsolutePathBuf}; @@ -364,7 +363,7 @@ fn print_toolchain_why_hint(options: Option<&CliOptions>, packages: &[String]) { /// * `args` - Optional CLI arguments. If None, uses env::args(). This allows NAPI bindings /// to pass process.argv.slice(2) to avoid including node binary and script path. #[tracing::instrument(skip(options))] -pub async fn main( +pub(crate) async fn main( cwd: AbsolutePathBuf, options: Option, args: Option>, diff --git a/packages/cli/binding/src/cli/resolver.rs b/packages/cli/binding/src/cli/resolver.rs index d5130042ab..d920435914 100644 --- a/packages/cli/binding/src/cli/resolver.rs +++ b/packages/cli/binding/src/cli/resolver.rs @@ -14,7 +14,7 @@ use super::{ /// Resolves synthesizable subcommands to concrete programs and arguments. /// Used by both direct CLI execution and CommandHandler. -pub struct SubcommandResolver { +pub(crate) struct SubcommandResolver { cli_options: Option, workspace_path: Arc, } @@ -29,11 +29,11 @@ impl std::fmt::Debug for SubcommandResolver { } impl SubcommandResolver { - pub fn new(workspace_path: Arc) -> Self { + pub(crate) fn new(workspace_path: Arc) -> Self { Self { cli_options: None, workspace_path } } - pub fn with_cli_options(mut self, cli_options: CliOptions) -> Self { + pub(crate) fn with_cli_options(mut self, cli_options: CliOptions) -> Self { self.cli_options = Some(cli_options); self } diff --git a/packages/cli/binding/src/cli/types.rs b/packages/cli/binding/src/cli/types.rs index c2fda6c821..c0792463bc 100644 --- a/packages/cli/binding/src/cli/types.rs +++ b/packages/cli/binding/src/cli/types.rs @@ -19,14 +19,14 @@ pub(crate) struct ResolvedUniversalViteConfig { /// Result type for resolved commands from JavaScript #[derive(Debug, Clone)] -pub struct ResolveCommandResult { - pub bin_path: Arc, - pub envs: Vec<(String, String)>, +pub(crate) struct ResolveCommandResult { + pub(crate) bin_path: Arc, + pub(crate) envs: Vec<(String, String)>, } /// Built-in subcommands that resolve to a concrete tool (oxlint, vitest, vite, etc.) #[derive(Debug, Clone, Subcommand)] -pub enum SynthesizableSubcommand { +pub(crate) enum SynthesizableSubcommand { /// Lint code #[command(disable_help_flag = true)] Lint { @@ -96,18 +96,18 @@ pub enum SynthesizableSubcommand { } #[derive(Debug, clap::Args)] -pub struct ToolchainArgs { +pub(crate) struct ToolchainArgs { /// Tool or package names to show #[arg(value_name = "TOOLS")] - pub tools: Vec, + pub(crate) tools: Vec, /// Print the graph as JSON #[arg(long)] - pub json: bool, + pub(crate) json: bool, /// Use the global Vite+ toolchain #[arg(long)] - pub global: bool, + pub(crate) global: bool, } /// Top-level CLI argument parser for vite-plus. @@ -135,28 +135,28 @@ pub(super) enum CLIArgs { /// Type alias for boxed async resolver function /// NOTE: Uses anyhow::Error to avoid NAPI type inference issues -pub type BoxedResolverFn = +pub(crate) type BoxedResolverFn = Box Pin> + 'static>>>; /// Type alias for vite config resolver function (takes package path, returns JSON string) /// Uses Arc for cloning and Send + Sync for use in UserConfigLoader -pub type ViteConfigResolverFn = Arc< +pub(crate) type ViteConfigResolverFn = Arc< dyn Fn(String) -> Pin> + Send + 'static>> + Send + Sync, >; /// CLI options containing JavaScript resolver functions (using boxed futures for simplicity) -pub struct CliOptions { - pub lint: BoxedResolverFn, - pub fmt: BoxedResolverFn, - pub vite: BoxedResolverFn, - pub test: BoxedResolverFn, - pub pack: BoxedResolverFn, - pub doc: BoxedResolverFn, - pub toolchain_manifest_path: String, - pub vite_plus_package_path: String, - pub resolve_universal_vite_config: ViteConfigResolverFn, +pub(crate) struct CliOptions { + pub(crate) lint: BoxedResolverFn, + pub(crate) fmt: BoxedResolverFn, + pub(crate) vite: BoxedResolverFn, + pub(crate) test: BoxedResolverFn, + pub(crate) pack: BoxedResolverFn, + pub(crate) doc: BoxedResolverFn, + pub(crate) toolchain_manifest_path: String, + pub(crate) vite_plus_package_path: String, + pub(crate) resolve_universal_vite_config: ViteConfigResolverFn, } /// A resolved subcommand ready for execution. diff --git a/packages/cli/binding/src/exec/args.rs b/packages/cli/binding/src/exec/args.rs index 57ede5dc56..33133e3a45 100644 --- a/packages/cli/binding/src/exec/args.rs +++ b/packages/cli/binding/src/exec/args.rs @@ -14,29 +14,29 @@ Examples: )] pub(crate) struct ExecArgs { #[clap(flatten)] - pub packages: PackageQueryArgs, + pub(crate) packages: PackageQueryArgs, /// Execute the command within a shell environment #[clap(short = 'c', long = "shell-mode")] - pub shell_mode: bool, + pub(crate) shell_mode: bool, /// Run concurrently without topological ordering #[clap(long)] - pub parallel: bool, + pub(crate) parallel: bool, /// Reverse execution order #[clap(long)] - pub reverse: bool, + pub(crate) reverse: bool, /// Resume from a specific package #[clap(long = "resume-from")] - pub resume_from: Option, + pub(crate) resume_from: Option, /// Save results to vp-exec-summary.json #[clap(long = "report-summary")] - pub report_summary: bool, + pub(crate) report_summary: bool, /// Command and arguments to execute #[arg(trailing_var_arg = true, allow_hyphen_values = true)] - pub command: Vec, + pub(crate) command: Vec, } diff --git a/packages/cli/binding/src/exec/mod.rs b/packages/cli/binding/src/exec/mod.rs index 67fbb48da9..175e0009f4 100644 --- a/packages/cli/binding/src/exec/mod.rs +++ b/packages/cli/binding/src/exec/mod.rs @@ -12,7 +12,10 @@ use self::workspace::execute_exec_workspace; /// /// Resolves the workspace, selects packages (defaulting to the current package /// when no flags are given), and executes the command in each selected package. -pub async fn execute(exec_args: ExecArgs, cwd: &AbsolutePathBuf) -> Result { +pub(crate) async fn execute( + exec_args: ExecArgs, + cwd: &AbsolutePathBuf, +) -> Result { // No command specified if exec_args.command.is_empty() { vp_shared::output::error( diff --git a/packages/cli/binding/src/js_command_args/commands/config.rs b/packages/cli/binding/src/js_command_args/commands/config.rs index 2212be2efd..67bfdb2ef3 100644 --- a/packages/cli/binding/src/js_command_args/commands/config.rs +++ b/packages/cli/binding/src/js_command_args/commands/config.rs @@ -59,7 +59,7 @@ fn config_command() -> Command { } #[napi(object, object_from_js = false)] -pub struct ConfigArgs { +pub(crate) struct ConfigArgs { pub hooks_dir: Option, pub hooks: Option, pub agent: Option, @@ -75,6 +75,7 @@ impl From for ConfigArgs { } } +#[expect(unreachable_pub, reason = "NAPI requires public enums for JavaScript exports")] #[napi(discriminant = "status", discriminant_case = "camelCase", object_from_js = false)] pub enum ParseConfigArgsOutcome { Ok { value: ConfigArgs }, @@ -83,7 +84,7 @@ pub enum ParseConfigArgsOutcome { } #[napi] -pub fn parse_config_args(argv: Vec) -> ParseConfigArgsOutcome { +pub(crate) fn parse_config_args(argv: Vec) -> ParseConfigArgsOutcome { match parse_args::(config_command(), argv) { ParseResult::Ok(value) => ParseConfigArgsOutcome::Ok { value: value.into() }, ParseResult::Help(command) => { diff --git a/packages/cli/binding/src/js_command_args/commands/create.rs b/packages/cli/binding/src/js_command_args/commands/create.rs index 08ab8d6e05..5ee3673cda 100644 --- a/packages/cli/binding/src/js_command_args/commands/create.rs +++ b/packages/cli/binding/src/js_command_args/commands/create.rs @@ -153,7 +153,7 @@ fn create_command() -> Command { } #[napi(object, object_from_js = false)] -pub struct CreateArgs { +pub(crate) struct CreateArgs { pub template_name: Option, pub directory: Option, #[napi(ts_type = "false | string | Array")] @@ -192,6 +192,7 @@ impl From for CreateArgs { } } +#[expect(unreachable_pub, reason = "NAPI requires public enums for JavaScript exports")] #[napi(discriminant = "status", discriminant_case = "camelCase", object_from_js = false)] pub enum ParseCreateArgsOutcome { Ok { value: CreateArgs }, @@ -200,7 +201,7 @@ pub enum ParseCreateArgsOutcome { } #[napi] -pub fn parse_create_args(argv: Vec) -> ParseCreateArgsOutcome { +pub(crate) fn parse_create_args(argv: Vec) -> ParseCreateArgsOutcome { match parse_args::(create_command(), argv) { ParseResult::Ok(value) => ParseCreateArgsOutcome::Ok { value: value.into() }, ParseResult::Help(command) => { diff --git a/packages/cli/binding/src/js_command_args/commands/hooks.rs b/packages/cli/binding/src/js_command_args/commands/hooks.rs index 74d840ae94..25ae8fc537 100644 --- a/packages/cli/binding/src/js_command_args/commands/hooks.rs +++ b/packages/cli/binding/src/js_command_args/commands/hooks.rs @@ -51,7 +51,7 @@ fn hooks_command() -> Command { } #[napi(object, object_from_js = false)] -pub struct HooksArgs { +pub(crate) struct HooksArgs { #[napi(ts_type = "'enable' | 'disable' | 'status'")] pub command: String, pub hooks_dir: Option, @@ -68,6 +68,7 @@ impl From for HooksArgs { } } +#[expect(unreachable_pub, reason = "NAPI requires public enums for JavaScript exports")] #[napi(discriminant = "status", discriminant_case = "camelCase", object_from_js = false)] pub enum ParseHooksArgsOutcome { Ok { value: HooksArgs }, @@ -86,7 +87,7 @@ fn command_for_help(mut command: Command, argv: &[String]) -> Command { } #[napi] -pub fn parse_hooks_args(argv: Vec) -> ParseHooksArgsOutcome { +pub(crate) fn parse_hooks_args(argv: Vec) -> ParseHooksArgsOutcome { let help_argv = argv.clone(); match parse_args::(hooks_command(), argv) { ParseResult::Ok(value) => ParseHooksArgsOutcome::Ok { value: value.into() }, diff --git a/packages/cli/binding/src/js_command_args/commands/migrate.rs b/packages/cli/binding/src/js_command_args/commands/migrate.rs index f6a845ee5e..742f624d02 100644 --- a/packages/cli/binding/src/js_command_args/commands/migrate.rs +++ b/packages/cli/binding/src/js_command_args/commands/migrate.rs @@ -100,7 +100,7 @@ fn migrate_command() -> Command { } #[napi(object, object_from_js = false)] -pub struct MigrateArgs { +pub(crate) struct MigrateArgs { pub path: Option, #[napi(ts_type = "false | string | Array")] pub agent: Option>>, @@ -124,6 +124,7 @@ impl From for MigrateArgs { } } +#[expect(unreachable_pub, reason = "NAPI requires public enums for JavaScript exports")] #[napi(discriminant = "status", discriminant_case = "camelCase", object_from_js = false)] pub enum ParseMigrateArgsOutcome { Ok { value: MigrateArgs }, @@ -132,7 +133,7 @@ pub enum ParseMigrateArgsOutcome { } #[napi] -pub fn parse_migrate_args(argv: Vec) -> ParseMigrateArgsOutcome { +pub(crate) fn parse_migrate_args(argv: Vec) -> ParseMigrateArgsOutcome { match parse_args::(migrate_command(), argv) { ParseResult::Ok(value) => ParseMigrateArgsOutcome::Ok { value: value.into() }, ParseResult::Help(command) => { diff --git a/packages/cli/binding/src/js_command_args/commands/staged.rs b/packages/cli/binding/src/js_command_args/commands/staged.rs index 19d228ee41..bbc58a0aef 100644 --- a/packages/cli/binding/src/js_command_args/commands/staged.rs +++ b/packages/cli/binding/src/js_command_args/commands/staged.rs @@ -150,7 +150,7 @@ fn staged_command() -> Command { } #[napi(object, object_from_js = false)] -pub struct StagedArgs { +pub(crate) struct StagedArgs { pub allow_empty: Option, pub concurrent: Option>, pub continue_on_error: Option, @@ -200,6 +200,7 @@ impl From for StagedArgs { } } +#[expect(unreachable_pub, reason = "NAPI requires public enums for JavaScript exports")] #[napi(discriminant = "status", discriminant_case = "camelCase", object_from_js = false)] pub enum ParseStagedArgsOutcome { Ok { value: StagedArgs }, @@ -208,7 +209,7 @@ pub enum ParseStagedArgsOutcome { } #[napi] -pub fn parse_staged_args(argv: Vec) -> ParseStagedArgsOutcome { +pub(crate) fn parse_staged_args(argv: Vec) -> ParseStagedArgsOutcome { match parse_args::(staged_command(), argv) { ParseResult::Ok(value) => ParseStagedArgsOutcome::Ok { value: value.into() }, ParseResult::Help(command) => { diff --git a/packages/cli/binding/src/js_command_args/parser.rs b/packages/cli/binding/src/js_command_args/parser.rs index 11a8190246..03899a2894 100644 --- a/packages/cli/binding/src/js_command_args/parser.rs +++ b/packages/cli/binding/src/js_command_args/parser.rs @@ -4,7 +4,7 @@ use clap::{Arg, ArgAction, Args, Command, FromArgMatches, error::ErrorKind}; use napi_derive::napi; #[napi(object, object_from_js = false)] -pub struct CliParseError { +pub(crate) struct CliParseError { pub kind: String, pub message: String, } diff --git a/packages/cli/binding/src/lib.rs b/packages/cli/binding/src/lib.rs index 0c8f00ff66..d9bf1e5fd1 100644 --- a/packages/cli/binding/src/lib.rs +++ b/packages/cli/binding/src/lib.rs @@ -14,7 +14,7 @@ )] #[cfg(feature = "rolldown")] -pub extern crate rolldown_binding; +pub(crate) extern crate rolldown_binding; mod check; mod cli; @@ -25,9 +25,7 @@ mod exec; mod js_command_args; #[allow(dead_code)] mod migration; -#[allow(dead_code)] mod package_manager; -#[allow(dead_code)] mod utils; use std::{collections::HashMap, error::Error as StdError, ffi::OsStr, fmt::Write as _, sync::Arc}; @@ -42,8 +40,12 @@ use crate::cli::{ /// Module initialization - sets up tracing and panic hook #[napi_derive::module_init] +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] #[allow(clippy::disallowed_macros)] -pub fn init() { +pub(crate) fn init() { vp_shared::ensure_blocking_stdio(); crate::cli::init_tracing(); @@ -60,13 +62,17 @@ pub fn init() { /// Re-enable blocking stdio after Node.js has initialized its lazy standard streams. #[napi] -pub fn ensure_blocking_stdio() { +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) fn ensure_blocking_stdio() { vp_shared::ensure_blocking_stdio(); } /// Configuration options passed from JavaScript to Rust. #[napi(object, object_to_js = false)] -pub struct CliOptions { +pub(crate) struct CliOptions { pub lint: Arc>>, pub fmt: Arc>>, pub vite: Arc>>, @@ -88,7 +94,7 @@ pub struct CliOptions { /// Result returned by JavaScript resolver functions. #[napi(object, object_to_js = false)] -pub struct JsCommandResolvedResult { +pub(crate) struct JsCommandResolvedResult { pub bin_path: String, pub envs: HashMap, } @@ -165,7 +171,11 @@ fn format_error_message(error: &(dyn StdError + 'static)) -> String { /// from vt, while allowing the NAPI async context to continue running /// and process JavaScript callbacks (via ThreadsafeFunction). #[napi] -pub async fn run(options: CliOptions) -> Result { +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) async fn run(options: CliOptions) -> Result { // Use provided cwd or current directory let mut cwd = current_dir()?; if let Some(options_cwd) = options.cwd { @@ -242,7 +252,7 @@ pub async fn run(options: CliOptions) -> Result { /// Resolved on-disk category roots from [`vp_shared::EnvConfig`]. #[napi(object)] -pub struct VpDirsJs { +pub(crate) struct VpDirsJs { pub bin: String, pub data: String, pub cache: String, @@ -255,7 +265,11 @@ pub struct VpDirsJs { /// JavaScript must not read `VP_HOME` / `VP_*_DIR` / `XDG_*` itself; /// this is the JS surface of the same `EnvConfig::get().dirs` Rust uses. #[napi] -pub fn get_vp_dirs() -> VpDirsJs { +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) fn get_vp_dirs() -> VpDirsJs { let dirs = &vp_shared::EnvConfig::get().dirs; VpDirsJs { bin: dirs.bin.as_path().to_string_lossy().into_owned(), @@ -268,7 +282,11 @@ pub fn get_vp_dirs() -> VpDirsJs { /// Render the Vite+ header using the Rust implementation. #[napi] -pub fn vite_plus_header() -> String { +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) fn vite_plus_header() -> String { vp_shared::header::vite_plus_header() } @@ -277,6 +295,10 @@ pub fn vite_plus_header() -> String { /// Mirrors `vp_shared::header::should_print_header` so both CLIs apply /// the same TTY + git-hook gating without duplicating the rules in JS. #[napi] -pub fn should_print_vite_plus_header() -> bool { +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) fn should_print_vite_plus_header() -> bool { vp_shared::header::should_print_header() } diff --git a/packages/cli/binding/src/migration.rs b/packages/cli/binding/src/migration.rs index 4d19833e8c..df69512b85 100644 --- a/packages/cli/binding/src/migration.rs +++ b/packages/cli/binding/src/migration.rs @@ -21,7 +21,11 @@ use napi_derive::napi; /// console.log(`Updated: ${updated}`); /// ``` #[napi] -pub fn rewrite_scripts(scripts_json: String, rules_yaml: String) -> Result> { +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) fn rewrite_scripts(scripts_json: String, rules_yaml: String) -> Result> { let updated = vp_migration::rewrite_scripts(&scripts_json, &rules_yaml).map_err(anyhow::Error::from)?; Ok(updated) @@ -40,7 +44,11 @@ pub fn rewrite_scripts(scripts_json: String, rules_yaml: String) -> Result Result> { +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) fn rewrite_eslint(scripts_json: String) -> Result> { let updated = vp_migration::rewrite_eslint(&scripts_json).map_err(anyhow::Error::from)?; Ok(updated) } @@ -58,14 +66,18 @@ pub fn rewrite_eslint(scripts_json: String) -> Result> { /// /// * `updated` - The updated scripts JSON string, or `null` if no changes were made #[napi] -pub fn rewrite_prettier(scripts_json: String) -> Result> { +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) fn rewrite_prettier(scripts_json: String) -> Result> { let updated = vp_migration::rewrite_prettier(&scripts_json).map_err(anyhow::Error::from)?; Ok(updated) } /// Result of merging JSON config into vite config #[napi(object)] -pub struct MergeJsonConfigResult { +pub(crate) struct MergeJsonConfigResult { /// The updated vite config content pub content: String, /// Whether any changes were made @@ -101,7 +113,11 @@ pub struct MergeJsonConfigResult { /// } /// ``` #[napi] -pub fn merge_json_config( +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) fn merge_json_config( vite_config_path: String, json_config_path: String, config_key: String, @@ -152,7 +168,11 @@ pub fn merge_json_config( /// } /// ``` #[napi] -pub fn upsert_json_config( +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) fn upsert_json_config( vite_config_path: String, json_config_path: String, config_key: String, @@ -178,14 +198,18 @@ pub fn upsert_json_config( /// comments, string literal occurrences, and nested keys. Returns `false` /// for unrecognized shapes (e.g. `return $VAR` from a callback). #[napi] -pub fn has_config_key(vite_config_path: String, config_key: String) -> Result { +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) fn has_config_key(vite_config_path: String, config_key: String) -> Result { let content = std::fs::read_to_string(&vite_config_path).map_err(anyhow::Error::from)?; Ok(vp_migration::has_config_key(&content, &config_key).map_err(anyhow::Error::from)?) } /// Error from batch import rewriting #[napi(object)] -pub struct BatchRewriteError { +pub(crate) struct BatchRewriteError { /// The file path that had an error pub path: String, /// The error message @@ -194,7 +218,7 @@ pub struct BatchRewriteError { /// Result of rewriting imports in multiple files #[napi(object)] -pub struct BatchRewriteResult { +pub(crate) struct BatchRewriteResult { /// Files that were modified pub modified_files: Vec, /// Files in Nuxt test-utils packages where upstream `vitest` imports were preserved @@ -229,7 +253,11 @@ pub struct BatchRewriteResult { /// } /// ``` #[napi] -pub fn merge_tsdown_config( +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) fn merge_tsdown_config( vite_config_path: String, tsdown_config_path: String, ) -> Result { @@ -248,7 +276,11 @@ pub fn merge_tsdown_config( /// with `lazyPlugins(() => [...])` and add a `lazyPlugins` import from /// `vite-plus` when needed. #[napi] -pub fn wrap_lazy_plugins(vite_config_path: String) -> Result { +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) fn wrap_lazy_plugins(vite_config_path: String) -> Result { let result = vp_migration::wrap_lazy_plugins(Path::new(&vite_config_path)) .map_err(anyhow::Error::from)?; @@ -287,7 +319,11 @@ pub fn wrap_lazy_plugins(vite_config_path: String) -> Result, ) -> Result { diff --git a/packages/cli/binding/src/package_manager.rs b/packages/cli/binding/src/package_manager.rs index 6278d5c007..d88712a1d3 100644 --- a/packages/cli/binding/src/package_manager.rs +++ b/packages/cli/binding/src/package_manager.rs @@ -7,7 +7,7 @@ use vt_workspace::{Error::PackageJsonNotFound, WorkspaceFile, find_workspace_roo #[napi(object)] #[derive(Debug)] -pub struct DownloadPackageManagerOptions { +pub(crate) struct DownloadPackageManagerOptions { pub name: String, pub version: String, pub expected_hash: Option, @@ -15,7 +15,7 @@ pub struct DownloadPackageManagerOptions { #[napi(object)] #[derive(Debug)] -pub struct DownloadPackageManagerResult { +pub(crate) struct DownloadPackageManagerResult { pub name: String, pub install_dir: String, pub bin_prefix: String, @@ -55,7 +55,11 @@ pub struct DownloadPackageManagerResult { /// console.log(`Package manager version: ${result.version}`); /// ``` #[napi] -pub async fn download_package_manager( +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) async fn download_package_manager( options: DownloadPackageManagerOptions, ) -> Result { let package_manager_type = match options.name.as_str() { @@ -90,7 +94,7 @@ pub async fn download_package_manager( #[napi(object)] #[derive(Debug)] -pub struct DetectWorkspaceResult { +pub(crate) struct DetectWorkspaceResult { pub package_manager_name: Option, pub package_manager_version: Option, pub is_monorepo: bool, @@ -121,7 +125,11 @@ pub struct DetectWorkspaceResult { /// console.log(`Workspace root: ${result.root}`); /// ``` #[napi] -pub async fn detect_workspace(cwd: String) -> Result { +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) async fn detect_workspace(cwd: String) -> Result { let cwd = AbsolutePathBuf::new(cwd.into()).ok_or(Error::from_reason("invalid cwd"))?; let (workspace_root, _relative_path) = match find_workspace_root(&cwd) { Ok(result) => result, diff --git a/packages/cli/binding/src/utils.rs b/packages/cli/binding/src/utils.rs index ad1acbd80b..f676b7bc2f 100644 --- a/packages/cli/binding/src/utils.rs +++ b/packages/cli/binding/src/utils.rs @@ -15,7 +15,7 @@ use vt_path::AbsolutePathBuf; /// - `cwd`: The current working directory for the command #[napi(object, object_to_js = false)] #[derive(Debug)] -pub struct RunCommandOptions { +pub(crate) struct RunCommandOptions { /// The name of the binary to execute pub bin_name: String, /// Command line arguments to pass to the binary @@ -29,7 +29,7 @@ pub struct RunCommandOptions { /// Access modes for a path. #[napi(object)] #[derive(Debug)] -pub struct PathAccess { +pub(crate) struct PathAccess { /// Whether the path was read pub read: bool, /// Whether the path was written @@ -45,7 +45,7 @@ pub struct PathAccess { /// - `path_accesses`: A map of relative paths to their access modes #[napi(object)] #[derive(Debug)] -pub struct RunCommandResult { +pub(crate) struct RunCommandResult { /// The exit code of the command pub exit_code: i32, /// Map of relative paths to their access modes @@ -85,7 +85,11 @@ pub struct RunCommandResult { /// console.log(`Path accesses:`, result.pathAccesses); /// ``` #[napi] -pub async fn run_command(options: RunCommandOptions) -> Result { +#[cfg_attr( + test, + expect(dead_code, reason = "NAPI exports are called by JavaScript, not Rust tests") +)] +pub(crate) async fn run_command(options: RunCommandOptions) -> Result { tracing::debug!("Run command options: {:?}", options); // Parse and validate the working directory let cwd = AbsolutePathBuf::new(PathBuf::from(&options.cwd)).ok_or_else(|| { diff --git a/packages/cli/build.ts b/packages/cli/build.ts index 7f2f80002f..38c36c33ef 100644 --- a/packages/cli/build.ts +++ b/packages/cli/build.ts @@ -33,9 +33,11 @@ import { generateLicenseFile } from '../../scripts/generate-license.js'; import corePkg from '../core/package.json' with { type: 'json' }; const projectDir = dirname(fileURLToPath(import.meta.url)); +const repoDir = join(projectDir, '..', '..'); const TEST_PACKAGE_NAME = 'vitest'; const CORE_PACKAGE_NAME = '@voidzero-dev/vite-plus-core'; const NATIVE_BUILD_TIME_PATH = join(projectDir, 'binding', 'vite-plus.build-time'); +const CARGO_MANIFEST_PATHS = [join(repoDir, 'Cargo.toml'), join(repoDir, 'rolldown', 'Cargo.toml')]; const UTC_BUILD_TIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/; // Browser providers projected under ./test/* and ./test/browser/providers/* so the @@ -636,12 +638,14 @@ interface ToolchainConfig { }>; } +interface CargoPackage { + name: string; + version: string; + source: string | null; +} + interface CargoMetadata { - packages: Array<{ - name: string; - version: string; - source: string | null; - }>; + packages: CargoPackage[]; } interface ResolvedToolchainNode { @@ -663,16 +667,29 @@ async function readPackageVersion(packageJsonPath: string, label: string): Promi return pkg.version; } -function readCargoMetadata(): CargoMetadata { - const repoDir = join(projectDir, '..', '..'); - const stdout = execFileSync('cargo', ['metadata', '--locked', '--format-version', '1'], { - cwd: repoDir, - encoding: 'utf8', - maxBuffer: 64 * 1024 * 1024, - }); +function readCargoMetadata(manifestPath: string): CargoMetadata { + const stdout = execFileSync( + 'cargo', + ['metadata', '--locked', '--format-version', '1', '--manifest-path', manifestPath], + { + cwd: repoDir, + encoding: 'utf8', + maxBuffer: 64 * 1024 * 1024, + }, + ); return JSON.parse(stdout) as CargoMetadata; } +function readCargoPackageMetadata(): CargoMetadata { + const packages = new Map(); + for (const manifestPath of CARGO_MANIFEST_PATHS) { + for (const pkg of readCargoMetadata(manifestPath).packages) { + packages.set(`${pkg.name}\0${pkg.version}\0${pkg.source ?? ''}`, pkg); + } + } + return { packages: [...packages.values()] }; +} + function resolveCargoPackage(metadata: CargoMetadata, packageName: string) { const matches = metadata.packages.filter((pkg) => pkg.name === packageName); if (matches.length !== 1) { @@ -833,7 +850,7 @@ async function syncToolchainExports() { validateToolchainConfig(config); const cliVersion = await readPackageVersion(join(projectDir, 'package.json'), 'vite-plus'); - const cargoMetadata = readCargoMetadata(); + const cargoMetadata = readCargoPackageMetadata(); const buildTime = await resolveNativeBuildTime(); const nodes = await Promise.all( config.nodes.map((node) => resolveToolchainNode(node, cargoMetadata, cliVersion, buildTime)),