diff --git a/Cargo.lock b/Cargo.lock index d9a3fa87a6..112a4f6f01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2235,7 +2235,7 @@ dependencies = [ [[package]] name = "fspy" version = "0.1.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "anyhow", "bstr", @@ -2270,7 +2270,7 @@ dependencies = [ [[package]] name = "fspy_detours_sys" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "cc", "winapi", @@ -2279,7 +2279,7 @@ dependencies = [ [[package]] name = "fspy_preload_unix" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "anyhow", "bstr", @@ -2294,7 +2294,7 @@ dependencies = [ [[package]] name = "fspy_preload_windows" version = "0.1.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "constcat", "fspy_detours_sys", @@ -2311,7 +2311,7 @@ dependencies = [ [[package]] name = "fspy_seccomp_unotify" version = "0.1.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "futures-util", "libc", @@ -2328,7 +2328,7 @@ dependencies = [ [[package]] name = "fspy_shared" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "bitflags 2.13.1", "bstr", @@ -2347,7 +2347,7 @@ dependencies = [ [[package]] name = "fspy_shared_unix" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "anyhow", "base64 0.22.1", @@ -2365,7 +2365,7 @@ dependencies = [ [[package]] name = "fspy_shm" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "base64 0.22.1", "memfd", @@ -3513,7 +3513,7 @@ dependencies = [ [[package]] name = "materialized_artifact" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "tempfile", ] @@ -3521,7 +3521,7 @@ dependencies = [ [[package]] name = "materialized_artifact_build" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "xxhash-rust", ] @@ -3754,7 +3754,7 @@ dependencies = [ [[package]] name = "native_str" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "bumpalo", "bytemuck", @@ -5473,7 +5473,7 @@ dependencies = [ [[package]] name = "pty_terminal" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "anyhow", "portable-pty", @@ -5483,7 +5483,7 @@ dependencies = [ [[package]] name = "pty_terminal_test" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "anyhow", "portable-pty", @@ -5494,7 +5494,7 @@ dependencies = [ [[package]] name = "pty_terminal_test_client" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "base64 0.22.1", "getrandom 0.4.3", @@ -7348,7 +7348,7 @@ dependencies = [ [[package]] name = "snapshot_test" version = "0.1.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "serde", "serde_json", @@ -8407,7 +8407,7 @@ dependencies = [ [[package]] name = "vite_glob" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "globset", "thiserror 2.0.19", @@ -8455,7 +8455,7 @@ dependencies = [ [[package]] name = "vite_graph_ser" version = "0.1.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "petgraph 0.8.3", "serde", @@ -8555,7 +8555,7 @@ dependencies = [ [[package]] name = "vite_path" version = "0.1.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "diff-struct", "os_str_bytes", @@ -8587,7 +8587,7 @@ dependencies = [ [[package]] name = "vite_powershell" version = "0.1.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "vite_path", "which", @@ -8596,7 +8596,7 @@ dependencies = [ [[package]] name = "vite_select" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "anyhow", "crossterm", @@ -8649,7 +8649,7 @@ dependencies = [ [[package]] name = "vite_shell" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "brush-parser 0.4.0", "diff-struct", @@ -8676,7 +8676,7 @@ dependencies = [ [[package]] name = "vite_str" version = "0.1.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "compact_str", "diff-struct", @@ -8687,7 +8687,7 @@ dependencies = [ [[package]] name = "vite_task" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "anstream", "anyhow", @@ -8736,7 +8736,7 @@ dependencies = [ [[package]] name = "vite_task_client" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "native_str", "rustc-hash", @@ -8749,7 +8749,7 @@ dependencies = [ [[package]] name = "vite_task_client_napi" version = "0.1.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "napi", "napi-build", @@ -8761,7 +8761,7 @@ dependencies = [ [[package]] name = "vite_task_graph" version = "0.1.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "anyhow", "async-trait", @@ -8783,7 +8783,7 @@ dependencies = [ [[package]] name = "vite_task_ipc_shared" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "native_str", "rustc-hash", @@ -8793,7 +8793,7 @@ dependencies = [ [[package]] name = "vite_task_plan" version = "0.1.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "anyhow", "async-trait", @@ -8821,7 +8821,7 @@ dependencies = [ [[package]] name = "vite_task_server" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "futures", "native_str", @@ -8845,7 +8845,7 @@ version = "0.0.0" [[package]] name = "vite_workspace" version = "0.0.0" -source = "git+https://github.com/voidzero-dev/vite-task.git?rev=85d4e734c64c96c2ce60e734b3e202b5add53696#85d4e734c64c96c2ce60e734b3e202b5add53696" +source = "git+https://github.com/voidzero-dev/vite-task.git?rev=5c1d02c750ac21c6f4cf0528062590a145e87fd1#5c1d02c750ac21c6f4cf0528062590a145e87fd1" dependencies = [ "clap", "petgraph 0.8.3", diff --git a/Cargo.toml b/Cargo.toml index d1528b63a6..45bee4283e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -193,7 +193,7 @@ dunce = "1.0.5" fast-glob = "1.0.0" flate2 = { version = "=1.1.9", features = ["zlib-rs"] } form_urlencoded = "1.2.1" -fspy = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "85d4e734c64c96c2ce60e734b3e202b5add53696" } +fspy = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "5c1d02c750ac21c6f4cf0528062590a145e87fd1" } futures = "0.3.31" futures-util = "0.3.31" glob = "0.3.2" @@ -250,8 +250,8 @@ pretty_assertions = "1.4.1" phf = "0.14.0" prettyplease = "0.2.32" proc-macro2 = "1" -pty_terminal_test = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "85d4e734c64c96c2ce60e734b3e202b5add53696" } -pty_terminal_test_client = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "85d4e734c64c96c2ce60e734b3e202b5add53696" } +pty_terminal_test = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "5c1d02c750ac21c6f4cf0528062590a145e87fd1" } +pty_terminal_test_client = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "5c1d02c750ac21c6f4cf0528062590a145e87fd1" } quote = "1" rayon = "1.10.0" regex = "1.11.1" @@ -274,7 +274,7 @@ sha2 = "0.10.9" shell-escape = "0.1.5" simdutf8 = "0.1.5" smallvec = { version = "1.15.1", features = ["union"] } -snapshot_test = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "85d4e734c64c96c2ce60e734b3e202b5add53696" } +snapshot_test = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "5c1d02c750ac21c6f4cf0528062590a145e87fd1" } string_cache = "0.9.0" sugar_path = { version = "3", features = ["cached_current_dir"] } supports-color = "3" @@ -305,11 +305,11 @@ vite_pm_cli = { path = "crates/vite_pm_cli" } vite_setup = { path = "crates/vite_setup" } vite_shared = { path = "crates/vite_shared" } vite_static_config = { path = "crates/vite_static_config" } -vite_path = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "85d4e734c64c96c2ce60e734b3e202b5add53696" } -vite_powershell = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "85d4e734c64c96c2ce60e734b3e202b5add53696" } -vite_str = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "85d4e734c64c96c2ce60e734b3e202b5add53696" } -vite_task = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "85d4e734c64c96c2ce60e734b3e202b5add53696" } -vite_workspace = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "85d4e734c64c96c2ce60e734b3e202b5add53696" } +vite_path = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "5c1d02c750ac21c6f4cf0528062590a145e87fd1" } +vite_powershell = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "5c1d02c750ac21c6f4cf0528062590a145e87fd1" } +vite_str = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "5c1d02c750ac21c6f4cf0528062590a145e87fd1" } +vite_task = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "5c1d02c750ac21c6f4cf0528062590a145e87fd1" } +vite_workspace = { git = "https://github.com/voidzero-dev/vite-task.git", rev = "5c1d02c750ac21c6f4cf0528062590a145e87fd1" } walkdir = "2.5.0" which = "8.0.0" winreg = "0.56.0" diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/index.html b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/index.html new file mode 100644 index 0000000000..13ee08c5d6 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/index.html @@ -0,0 +1 @@ + diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/package.json new file mode 100644 index 0000000000..81503a3995 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/package.json @@ -0,0 +1,13 @@ +{ + "name": "@test/builtin-script-note", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "vp build", + "dev": "vite --host --port 5000", + "lint": "vp lint --deny-warnings src/", + "format": "prettier --write src/", + "help": "vpt echo help" + } +} diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots.toml b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots.toml new file mode 100644 index 0000000000..533ef5c07f --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots.toml @@ -0,0 +1,39 @@ +# A built-in command never runs a `package.json` script of the same name, so it +# points at `vpr ` instead. This project defines `build`, `dev`, `lint`, +# `format` and `help` scripts, but no `fmt` one. +[[case]] +name = "builtin_script_note" +vp = ["local", "global"] +skip-platforms = ["windows"] +steps = [ + { argv = ["vp", "dev", "--port", "12312312312"], comment = "`vp dev` points at the `dev` script (invalid port exits the server immediately)", continue-on-failure = true }, + { argv = ["vp", "build"], comment = "`vp build` points at the same build script used by the suppression cases" }, + { argv = ["vp", "lint", "src/"], comment = "every built-in that can be mistaken for a script gets the same note", continue-on-failure = true }, + { argv = ["vp", "lint", "."], cwd = "src", comment = "note reaches the enclosing package from a subdirectory, like `vpr` does", continue-on-failure = true }, + { argv = ["vp", "format", "src/"], comment = "the `format` alias reaches the local CLI as typed, so its own script gets the note", continue-on-failure = true }, + { argv = ["vp", "fmt", "src/"], comment = "no note: only `format` is a script here, and that is not the name this ran under", continue-on-failure = true }, + { argv = ["vp", "help", "dev"], comment = "the local path checks the original `help` spelling; the global CLI renders help before local delegation", continue-on-failure = true }, + { argv = ["vp", "preview", "--port", "12312312312"], comment = "no note: this project has no `preview` script", continue-on-failure = true }, + { argv = ["vp", "lint", "src/"], tty = false, comment = "the note still reaches piped output, such as an AI agent capturing the command; it goes to stderr, so parsed stdout stays intact", continue-on-failure = true }, +] + +# Vite Task sets `VP_RUN` for a package script's child process. The nested +# built-in should not point back at the `vp run` already in progress. +[[case]] +name = "builtin_script_note_inside_task" +vp = "local" +skip-platforms = ["windows"] +steps = [ + { argv = ["vp", "run", "build"], comment = "no note: a task-spawned `vp build` is already on the script path" }, +] + +# npm, pnpm and Yarn expose the active package script through the same +# `npm_lifecycle_event` compatibility variable. The note should not recommend +# switching runners when a package-manager script has already done that. +[[case]] +name = "builtin_script_note_inside_npm_script" +vp = "local" +skip-platforms = ["windows"] +steps = [ + { argv = ["npm", "run", "build"], comment = "no note: npm has already selected and started the build script" }, +] diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note.global.md new file mode 100644 index 0000000000..a679d283b0 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note.global.md @@ -0,0 +1,132 @@ +# builtin_script_note + +## `vp dev --port 12312312312` + +`vp dev` points at the `dev` script (invalid port exits the server immediately) + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +note: You are running `vp dev` as a Vite+ built-in command. If you meant to run the dev npm script, use `vpr dev` instead. +error when starting dev server: +Error: No available ports found between 12312312312 and 65535 +``` + +## `vp build` + +`vp build` points at the same build script used by the suppression cases + +``` +VITE+ - The Unified Toolchain for the Web + +note: You are running `vp build` as a Vite+ built-in command. If you meant to run the build npm script, use `vpr build` instead. +✓ 4 modules transformed. +computing gzip size... +dist/index.html kB │ gzip: kB +dist/assets/index-.js kB │ gzip: kB + +✓ built in +``` + +## `vp lint src/` + +every built-in that can be mistaken for a script gets the same note + +``` +VITE+ - The Unified Toolchain for the Web + +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `cd src && vp lint .` + +note reaches the enclosing package from a subdirectory, like `vpr` does + +``` +VITE+ - The Unified Toolchain for the Web + +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp format src/` + +the `format` alias reaches the local CLI as typed, so its own script gets the note + +``` +VITE+ - The Unified Toolchain for the Web + +note: You are running `vp format` as a Vite+ built-in command. If you meant to run the format npm script, use `vpr format` instead. +Finished in on 1 files using threads. +``` + +## `vp fmt src/` + +no note: only `format` is a script here, and that is not the name this ran under + +``` +VITE+ - The Unified Toolchain for the Web + +Finished in on 1 files using threads. +``` + +## `vp help dev` + +the local path checks the original `help` spelling; the global CLI renders help before local delegation + +``` +VITE+ - The Unified Toolchain for the Web + +Usage: vp dev [ROOT] [OPTIONS] + +Run the development server. +Options are forwarded to Vite. + +Arguments: + [ROOT] Project root directory (default: current directory) + +Options: + --host [HOST] Specify hostname + --port Specify port + --open [PATH] Open browser on startup + --strictPort Exit if specified port is already in use + -c, --config Use specified config file + --base Public base path + -m, --mode Set env mode + -h, --help Print help + +Examples: + vp dev + vp dev --open + vp dev --host localhost --port 5173 + +Documentation: https://viteplus.dev/guide/dev +``` + +## `vp preview --port 12312312312` + +no note: this project has no `preview` script + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +error when starting preview server: +Error: No available ports found between 12312312312 and 65535 +``` + +## `vp lint src/` + +the note still reaches piped output, such as an AI agent capturing the command; it goes to stderr, so parsed stdout stays intact + +``` +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. +``` diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note.local.md new file mode 100644 index 0000000000..358efb816b --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note.local.md @@ -0,0 +1,128 @@ +# builtin_script_note + +## `vp dev --port 12312312312` + +`vp dev` points at the `dev` script (invalid port exits the server immediately) + +**Exit code:** 1 + +``` +note: You are running `vp dev` as a Vite+ built-in command. If you meant to run the dev npm script, use `vpr dev` instead. +error when starting dev server: +Error: No available ports found between 12312312312 and 65535 +``` + +## `vp build` + +`vp build` points at the same build script used by the suppression cases + +``` +note: You are running `vp build` as a Vite+ built-in command. If you meant to run the build npm script, use `vpr build` instead. +✓ 4 modules transformed. +computing gzip size... +dist/index.html kB │ gzip: kB +dist/assets/index-.js kB │ gzip: kB + +✓ built in +``` + +## `vp lint src/` + +every built-in that can be mistaken for a script gets the same note + +``` +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `cd src && vp lint .` + +note reaches the enclosing package from a subdirectory, like `vpr` does + +``` +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp format src/` + +the `format` alias reaches the local CLI as typed, so its own script gets the note + +``` +note: You are running `vp format` as a Vite+ built-in command. If you meant to run the format npm script, use `vpr format` instead. +Finished in on 1 files using threads. +``` + +## `vp fmt src/` + +no note: only `format` is a script here, and that is not the name this ran under + +``` +Finished in on 1 files using threads. +``` + +## `vp help dev` + +the local path checks the original `help` spelling; the global CLI renders help before local delegation + +``` +note: You are running `vp help` as a Vite+ built-in command. If you meant to run the help npm script, use `vpr help` instead. +vp/ + +Usage: + $ vp [root] + +Commands: + [root] start dev server + build [root] build for production + optimize [root] pre-bundle dependencies (deprecated, the pre-bundle process runs automatically and does not need to be called) + preview [root] locally preview production build + +For more info, run any command with the `--help` flag: + $ vp --help + $ vp build --help + $ vp optimize --help + $ vp preview --help + +Options: + --host [host] [string] specify hostname + --port [number] specify port + --open [path] [boolean | string] open browser on startup + --cors [boolean] enable CORS + --strictPort [boolean] exit if specified port is already in use + --force [boolean] force the optimizer to ignore the cache and re-bundle + --experimentalBundle [boolean] use experimental full bundle mode (this is highly experimental) + -c, --config [string] use specified config file + --base [string] public base path (default: /) + -l, --logLevel [string] info | warn | error | silent + --clearScreen [boolean] allow/disable clear screen when logging + --configLoader [string] use 'bundle' to bundle the config with Rolldown, or 'runner' (experimental) to process it on the fly, or 'native' (experimental) to load using the native runtime (default: bundle) + -d, --debug [feat] [string | boolean] show debug logs + -f, --filter [string] filter debug logs + -m, --mode [string] set env mode + -h, --help Display this message + -v, --version Display version number +``` + +## `vp preview --port 12312312312` + +no note: this project has no `preview` script + +**Exit code:** 1 + +``` +error when starting preview server: +Error: No available ports found between 12312312312 and 65535 +``` + +## `vp lint src/` + +the note still reaches piped output, such as an AI agent capturing the command; it goes to stderr, so parsed stdout stays intact + +``` +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. +``` diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note_inside_npm_script.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note_inside_npm_script.md new file mode 100644 index 0000000000..984528898e --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note_inside_npm_script.md @@ -0,0 +1,18 @@ +# builtin_script_note_inside_npm_script + +## `npm run build` + +no note: npm has already selected and started the build script + +``` + +> @test/builtin-script-note@1.0.0 build +> vp build + +✓ 4 modules transformed. +computing gzip size... +dist/index.html kB │ gzip: kB +dist/assets/index-.js kB │ gzip: kB + +✓ built in +``` diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note_inside_task.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note_inside_task.md new file mode 100644 index 0000000000..aac6ca268e --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note_inside_task.md @@ -0,0 +1,15 @@ +# builtin_script_note_inside_task + +## `vp run build` + +no note: a task-spawned `vp build` is already on the script path + +``` +$ vp build ⊘ cache disabled +✓ 4 modules transformed. +computing gzip size... +dist/index.html kB │ gzip: kB +dist/assets/index-.js kB │ gzip: kB + +✓ built in +``` diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/src/valid.js b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/src/valid.js new file mode 100644 index 0000000000..95c37436b4 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/src/valid.js @@ -0,0 +1,6 @@ +// Lint-clean and already formatted, so the commands only print their summary. +function validCode() { + return 'hello'; +} + +export { validCode }; diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/vite.config.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/vite.config.ts new file mode 100644 index 0000000000..9590747fc7 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/vite.config.ts @@ -0,0 +1,8 @@ +export default { + lint: { + ignorePatterns: [], + }, + fmt: { + ignorePatterns: [], + }, +}; diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/migration_eslint_svelte_runes/snapshots/migration_eslint_svelte_runes.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/migration_eslint_svelte_runes/snapshots/migration_eslint_svelte_runes.md index 5453ad229c..b20ead3c64 100644 --- a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/migration_eslint_svelte_runes/snapshots/migration_eslint_svelte_runes.md +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/migration_eslint_svelte_runes/snapshots/migration_eslint_svelte_runes.md @@ -84,6 +84,7 @@ valid Svelte rune usage should pass no-undef ``` VITE+ - The Unified Toolchain for the Web +note: You are running `vp lint` as a Vite+ built-in command. If you meant to run the lint npm script, use `vpr lint` instead. Found 0 warnings and 0 errors. Finished in on 1 file with rules using threads. ``` diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_build_cache/snapshots/vp_build_cache.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_build_cache/snapshots/vp_build_cache.md index 1cf942bc84..cb03a130ba 100644 --- a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_build_cache/snapshots/vp_build_cache.md +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_build_cache/snapshots/vp_build_cache.md @@ -33,6 +33,7 @@ direct vp build should not be cached direct vp build has no cache ``` +note: You are running `vp build` as a Vite+ built-in command. If you meant to run the build npm script, use `vpr build` instead. ✓ 4 modules transformed. computing gzip size... dist/index.html kB │ gzip: kB diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_build_cache_monorepo/snapshots/vp_build_cache_monorepo.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_build_cache_monorepo/snapshots/vp_build_cache_monorepo.md index c57420d0d9..2dc2fab650 100644 --- a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_build_cache_monorepo/snapshots/vp_build_cache_monorepo.md +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_build_cache_monorepo/snapshots/vp_build_cache_monorepo.md @@ -60,6 +60,7 @@ direct vp build should not be cached direct vp build has no cache ``` +note: You are running `vp build` as a Vite+ built-in command. If you meant to run the build npm script, use `vpr build` instead. ✓ 4 modules transformed. computing gzip size... dist/index.html kB │ gzip: kB diff --git a/crates/vite_global_cli/src/cli.rs b/crates/vite_global_cli/src/cli.rs index 163c2ded7a..6ba7ebdc98 100644 --- a/crates/vite_global_cli/src/cli.rs +++ b/crates/vite_global_cli/src/cli.rs @@ -844,9 +844,22 @@ fn prompt_reinstall_node_mismatches( .unwrap_or(false) } +/// The subcommand as the user wrote it, taken from `argv` before any rewriting. +/// +/// 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> { + argv.iter().skip(1).map(String::as_str).find(|arg| !arg.starts_with('-')) +} + /// Run the CLI command. -pub async fn run_command(cwd: AbsolutePathBuf, args: Args) -> Result { - run_command_with_options(cwd, args, RenderOptions::default()).await +pub async fn run_command( + cwd: AbsolutePathBuf, + args: Args, + raw_subcommand: Option<&str>, +) -> Result { + run_command_with_options(cwd, args, RenderOptions::default(), raw_subcommand).await } /// Run the CLI command with rendering options. @@ -854,6 +867,7 @@ pub async fn run_command_with_options( cwd: AbsolutePathBuf, args: Args, render_options: RenderOptions, + raw_subcommand: Option<&str>, ) -> Result { // Handle --version flag (Category B: delegates to JS) if args.version { @@ -887,13 +901,13 @@ pub async fn run_command_with_options( } // Category B: JS Script Commands - Commands::Create { args } => commands::create::execute(cwd, &args).await, + Commands::Create { args } => commands::create::execute(cwd, &args, raw_subcommand).await, Commands::Migrate { args } => commands::migrate::execute(cwd, &args).await, - Commands::Config { args } => commands::config::execute(cwd, &args).await, + Commands::Config { args } => commands::config::execute(cwd, &args, raw_subcommand).await, - Commands::Staged { args } => commands::staged::execute(cwd, &args).await, + Commands::Staged { args } => commands::staged::execute(cwd, &args, raw_subcommand).await, // Category C: Local CLI Delegation (stubs) Commands::Dev { args } => { @@ -901,7 +915,7 @@ pub async fn run_command_with_options( return Ok(ExitStatus::default()); } print_runtime_header(render_options.show_header); - commands::delegate::execute(cwd, "dev", &args).await + commands::delegate::execute(cwd, "dev", &args, raw_subcommand).await } Commands::Build { args } => { @@ -909,7 +923,7 @@ pub async fn run_command_with_options( return Ok(ExitStatus::default()); } print_runtime_header(render_options.show_header); - commands::delegate::execute(cwd, "build", &args).await + commands::delegate::execute(cwd, "build", &args, raw_subcommand).await } Commands::Test { args } => { @@ -917,7 +931,7 @@ pub async fn run_command_with_options( return Ok(ExitStatus::default()); } print_runtime_header(render_options.show_header); - commands::delegate::execute(cwd, "test", &args).await + commands::delegate::execute(cwd, "test", &args, raw_subcommand).await } Commands::Lint { args } => { @@ -926,9 +940,9 @@ pub async fn run_command_with_options( } maybe_print_runtime_header("lint", &args, render_options.show_header); if should_force_global_delegate("lint", &args) { - commands::delegate::execute_global(cwd, "lint", &args).await + commands::delegate::execute_global(cwd, "lint", &args, raw_subcommand).await } else { - commands::delegate::execute(cwd, "lint", &args).await + commands::delegate::execute(cwd, "lint", &args, raw_subcommand).await } } @@ -938,9 +952,9 @@ pub async fn run_command_with_options( } maybe_print_runtime_header("fmt", &args, render_options.show_header); if should_force_global_delegate("fmt", &args) { - commands::delegate::execute_global(cwd, "fmt", &args).await + commands::delegate::execute_global(cwd, "fmt", &args, raw_subcommand).await } else { - commands::delegate::execute(cwd, "fmt", &args).await + commands::delegate::execute(cwd, "fmt", &args, raw_subcommand).await } } @@ -949,7 +963,7 @@ pub async fn run_command_with_options( return Ok(ExitStatus::default()); } print_runtime_header(render_options.show_header); - commands::delegate::execute(cwd, "check", &args).await + commands::delegate::execute(cwd, "check", &args, raw_subcommand).await } Commands::Pack { args } => { @@ -957,7 +971,7 @@ pub async fn run_command_with_options( return Ok(ExitStatus::default()); } print_runtime_header(render_options.show_header); - commands::delegate::execute(cwd, "pack", &args).await + commands::delegate::execute(cwd, "pack", &args, raw_subcommand).await } Commands::Run { args } => { @@ -965,7 +979,7 @@ pub async fn run_command_with_options( return Ok(ExitStatus::default()); } print_runtime_header(render_options.show_header); - commands::delegate::execute(cwd, "run", &args).await + commands::delegate::execute(cwd, "run", &args, raw_subcommand).await } Commands::Exec { args } => { @@ -973,7 +987,7 @@ pub async fn run_command_with_options( return Ok(ExitStatus::default()); } print_runtime_header(render_options.show_header); - commands::delegate::execute(cwd, "exec", &args).await + commands::delegate::execute(cwd, "exec", &args, raw_subcommand).await } Commands::Preview { args } => { @@ -982,7 +996,7 @@ pub async fn run_command_with_options( return Ok(ExitStatus::default()); } print_runtime_header(render_options.show_header); - commands::delegate::execute(cwd, "preview", &args).await + commands::delegate::execute(cwd, "preview", &args, raw_subcommand).await } Commands::Cache { args } => { @@ -990,7 +1004,7 @@ pub async fn run_command_with_options( return Ok(ExitStatus::default()); } print_runtime_header(render_options.show_header); - commands::delegate::execute(cwd, "cache", &args).await + commands::delegate::execute(cwd, "cache", &args, raw_subcommand).await } Commands::Env(args) => commands::env::execute(cwd, args).await, @@ -1086,10 +1100,31 @@ pub fn try_parse_args_from_with_options( #[cfg(test)] mod tests { use super::{ - display_node_version, has_flag_before_terminator, is_same_node_version, + display_node_version, has_flag_before_terminator, is_same_node_version, raw_subcommand, should_force_global_delegate, should_suppress_header_for_subcommand, }; + fn argv(args: &[&str]) -> Vec { + args.iter().map(|arg| (*arg).to_string()).collect() + } + + #[test] + fn raw_subcommand_is_the_token_as_written() { + assert_eq!(raw_subcommand(&argv(&["vp", "fmt", "src/"])), Some("fmt")); + assert_eq!(raw_subcommand(&argv(&["vp", "format", "src/"])), Some("format")); + } + + #[test] + fn raw_subcommand_skips_leading_flags() { + assert_eq!(raw_subcommand(&argv(&["vp", "--silent", "install"])), Some("install")); + } + + #[test] + fn raw_subcommand_is_none_without_a_subcommand() { + assert_eq!(raw_subcommand(&argv(&["vp"])), None); + assert_eq!(raw_subcommand(&argv(&["vp", "--version"])), None); + } + #[test] fn detects_global_update_node_version_mismatch() { assert!(is_same_node_version("21.0.0", "v21.0.0")); diff --git a/crates/vite_global_cli/src/commands/config.rs b/crates/vite_global_cli/src/commands/config.rs index fb3983e0f1..1e0d1252a1 100644 --- a/crates/vite_global_cli/src/commands/config.rs +++ b/crates/vite_global_cli/src/commands/config.rs @@ -7,6 +7,10 @@ use vite_path::AbsolutePathBuf; use crate::error::Error; /// Execute the `config` command by delegating to local or global vite-plus. -pub async fn execute(cwd: AbsolutePathBuf, args: &[String]) -> Result { - super::delegate::execute(cwd, "config", args).await +pub async fn execute( + cwd: AbsolutePathBuf, + args: &[String], + raw_subcommand: Option<&str>, +) -> Result { + super::delegate::execute(cwd, "config", args, raw_subcommand).await } diff --git a/crates/vite_global_cli/src/commands/create.rs b/crates/vite_global_cli/src/commands/create.rs index 61925ce7b3..e8c9e8d6a3 100644 --- a/crates/vite_global_cli/src/commands/create.rs +++ b/crates/vite_global_cli/src/commands/create.rs @@ -7,8 +7,12 @@ use vite_path::AbsolutePathBuf; use crate::error::Error; /// Execute the `create` command by delegating to local or global vite-plus. -pub async fn execute(cwd: AbsolutePathBuf, args: &[String]) -> Result { - super::delegate::execute(cwd, "create", args).await +pub async fn execute( + cwd: AbsolutePathBuf, + args: &[String], + raw_subcommand: Option<&str>, +) -> Result { + super::delegate::execute(cwd, "create", args, raw_subcommand).await } #[cfg(test)] diff --git a/crates/vite_global_cli/src/commands/delegate.rs b/crates/vite_global_cli/src/commands/delegate.rs index 726169810e..6684a7e71d 100644 --- a/crates/vite_global_cli/src/commands/delegate.rs +++ b/crates/vite_global_cli/src/commands/delegate.rs @@ -7,12 +7,16 @@ use vite_path::AbsolutePathBuf; use crate::{error::Error, js_executor::JsExecutor}; /// Execute a command by delegating to the local `vite-plus` CLI. +/// +/// `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( cwd: AbsolutePathBuf, command: &str, args: &[String], + raw_subcommand: Option<&str>, ) -> Result { - let mut executor = JsExecutor::new(None); + let mut executor = JsExecutor::new(None).with_raw_subcommand(raw_subcommand); let mut full_args = vec![command.to_string()]; full_args.extend(args.iter().cloned()); executor.delegate_to_local_cli(&cwd, &full_args).await @@ -31,12 +35,15 @@ 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( cwd: AbsolutePathBuf, command: &str, args: &[String], + raw_subcommand: Option<&str>, ) -> Result { - let mut executor = JsExecutor::new(None); + let mut executor = JsExecutor::new(None).with_raw_subcommand(raw_subcommand); let mut full_args = vec![command.to_string()]; full_args.extend(args.iter().cloned()); executor.delegate_to_global_cli(&cwd, &full_args).await diff --git a/crates/vite_global_cli/src/commands/staged.rs b/crates/vite_global_cli/src/commands/staged.rs index fa78bd1a24..0bbbcd17ff 100644 --- a/crates/vite_global_cli/src/commands/staged.rs +++ b/crates/vite_global_cli/src/commands/staged.rs @@ -7,6 +7,10 @@ use vite_path::AbsolutePathBuf; use crate::error::Error; /// Execute the `staged` command by delegating to local or global vite-plus. -pub async fn execute(cwd: AbsolutePathBuf, args: &[String]) -> Result { - super::delegate::execute(cwd, "staged", args).await +pub async fn execute( + cwd: AbsolutePathBuf, + args: &[String], + raw_subcommand: Option<&str>, +) -> Result { + super::delegate::execute(cwd, "staged", args, raw_subcommand).await } diff --git a/crates/vite_global_cli/src/commands/vpr.rs b/crates/vite_global_cli/src/commands/vpr.rs index e043b9c8b0..e304ce12ae 100644 --- a/crates/vite_global_cli/src/commands/vpr.rs +++ b/crates/vite_global_cli/src/commands/vpr.rs @@ -15,7 +15,8 @@ pub async fn execute_vpr(args: &[String], cwd: &AbsolutePath) -> i32 { } let cwd_buf = cwd.to_absolute_path_buf(); - match super::delegate::execute(cwd_buf, "run", args).await { + // `vpr` is a shim, not a subcommand, so no subcommand was written. + match super::delegate::execute(cwd_buf, "run", args, None).await { Ok(status) => status.code().unwrap_or(1), Err(e) => { output::error(&e.to_string()); diff --git a/crates/vite_global_cli/src/js_executor.rs b/crates/vite_global_cli/src/js_executor.rs index 74e15f260e..6e8842d278 100644 --- a/crates/vite_global_cli/src/js_executor.rs +++ b/crates/vite_global_cli/src/js_executor.rs @@ -31,6 +31,8 @@ pub struct JsExecutor { project_runtime: Option, /// Directory containing JS scripts (from `VITE_GLOBAL_CLI_JS_SCRIPTS_DIR`) scripts_dir: Option, + /// Subcommand as the user wrote it, forwarded to the CLI this one runs + raw_subcommand: Option, } impl JsExecutor { @@ -41,7 +43,16 @@ impl JsExecutor { /// If not provided, will be auto-detected from the binary location. #[must_use] pub const fn new(scripts_dir: Option) -> Self { - Self { cli_runtime: None, project_runtime: None, scripts_dir } + Self { cli_runtime: None, project_runtime: None, scripts_dir, raw_subcommand: None } + } + + /// Forward the subcommand as the user wrote it to the CLI this one runs. + /// + /// 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 { + self.raw_subcommand = raw_subcommand.map(ToOwned::to_owned); + self } /// Get the JS scripts directory. @@ -340,6 +351,9 @@ impl JsExecutor { let mut cmd = Self::create_js_command(node_binary, bin_prefix); cmd.arg(entry_point.as_path()).args(args).current_dir(project_path.as_path()); + if let Some(raw_subcommand) = &self.raw_subcommand { + cmd.env(vite_shared::env_vars::VP_RAW_SUBCOMMAND, raw_subcommand); + } Ok(cmd) } diff --git a/crates/vite_global_cli/src/main.rs b/crates/vite_global_cli/src/main.rs index 5a90eef107..aad56e9738 100644 --- a/crates/vite_global_cli/src/main.rs +++ b/crates/vite_global_cli/src/main.rs @@ -35,7 +35,7 @@ use vite_shared::output; pub use crate::cli::try_parse_args_from; use crate::cli::{ - RenderOptions, command_with_help, run_command, run_command_with_options, + RenderOptions, command_with_help, raw_subcommand, run_command, run_command_with_options, try_parse_args_from_with_options, }; @@ -224,9 +224,11 @@ fn clap_error_to_exit_code(e: &clap::Error) -> ExitCode { async fn run_corrected_args(cwd: &vite_path::AbsolutePathBuf, raw_args: &[String]) -> ExitCode { let render_options = RenderOptions { show_header: false }; - let args_with_program = std::iter::once("vp".to_string()).chain(raw_args.iter().cloned()); - let normalized_args = normalize_args(args_with_program.collect()); - + let args_with_program: Vec = + std::iter::once("vp".to_string()).chain(raw_args.iter().cloned()).collect(); + // The subcommand as written, taken before `normalize_args` can rewrite it. + let raw_subcommand = raw_subcommand(&args_with_program).map(str::to_owned); + let normalized_args = normalize_args(args_with_program); let parsed = match try_parse_args_from_with_options(normalized_args, render_options) { Ok(args) => args, Err(e) => { @@ -235,7 +237,9 @@ async fn run_corrected_args(cwd: &vite_path::AbsolutePathBuf, raw_args: &[String } }; - match run_command_with_options(cwd.clone(), parsed, render_options).await { + match run_command_with_options(cwd.clone(), parsed, render_options, raw_subcommand.as_deref()) + .await + { Ok(exit_status) => exit_status_to_exit_code(exit_status), Err(e) => { if e.is_user_message() { @@ -353,6 +357,8 @@ async fn main() -> ExitCode { // Capture user args (excluding argv0) before normalization. let raw_args = args[1..].to_vec(); + // The subcommand as written, taken before `normalize_args` can rewrite it. + let raw_subcommand = raw_subcommand(&args).map(str::to_owned); // Normalize arguments (list/ls aliases, help rewriting) let normalized_args = normalize_args(args); @@ -427,7 +433,7 @@ async fn main() -> ExitCode { clap_error_to_exit_code(&e) } } - Ok(args) => match run_command(cwd.clone(), args).await { + Ok(args) => match run_command(cwd.clone(), args, raw_subcommand.as_deref()).await { Ok(exit_status) => exit_status_to_exit_code(exit_status), Err(e) => { if e.is_user_message() { diff --git a/crates/vite_shared/src/env_vars.rs b/crates/vite_shared/src/env_vars.rs index 7e3de5b0dd..7f61af626c 100644 --- a/crates/vite_shared/src/env_vars.rs +++ b/crates/vite_shared/src/env_vars.rs @@ -74,6 +74,13 @@ pub const VP_SHIM_TOOL: &str = "VP_SHIM_TOOL"; /// before forwarding to the actual tool. pub const VP_SHIM_WRAPPER: &str = "VP_SHIM_WRAPPER"; +/// The subcommand as the user wrote it, passed from the global CLI to the local +/// one. +/// +/// A command runs under its canonical name (`vp format` runs `fmt`), which loses +/// the spelling. This carries the original alongside it. +pub const VP_RAW_SUBCOMMAND: &str = "VP_RAW_SUBCOMMAND"; + /// Path to the vp binary, passed to JS scripts so they can invoke CLI commands. pub const VP_CLI_BIN: &str = "VP_CLI_BIN"; diff --git a/packages/cli/binding/src/cli/mod.rs b/packages/cli/binding/src/cli/mod.rs index 5033a24339..294868a3b9 100644 --- a/packages/cli/binding/src/cli/mod.rs +++ b/packages/cli/binding/src/cli/mod.rs @@ -7,6 +7,7 @@ mod execution; mod handler; mod help; mod resolver; +mod script_note; mod types; use std::{borrow::Cow, env, ffi::OsStr, sync::Arc}; @@ -25,7 +26,7 @@ pub use types::{ use vite_error::Error; use vite_path::{AbsolutePath, AbsolutePathBuf}; pub use vite_shared::init_tracing; -use vite_shared::{PrependOptions, prepend_to_path_env}; +use vite_shared::{PrependOptions, env_vars, prepend_to_path_env}; use vite_str::Str; use vite_task::{ExitStatus, Session, SessionConfig}; @@ -263,8 +264,13 @@ pub async fn main( options: Option, args: Option>, ) -> Result { - let args_vec: Vec = args.unwrap_or_else(|| env::args().skip(1).collect()); - let args_vec = normalize_help_args(args_vec); + let raw_args: Vec = args.unwrap_or_else(|| env::args().skip(1).collect()); + // The global CLI resolves aliases to their canonical names before + // delegating, so prefer the original spelling it forwards. A direct local + // invocation can use its first, still-unnormalized argument. + let raw_subcommand = + env::var(env_vars::VP_RAW_SUBCOMMAND).ok().or_else(|| raw_args.first().cloned()); + let args_vec = normalize_help_args(raw_args); if should_print_help(&args_vec) { print_help(); return Ok(ExitStatus::SUCCESS); @@ -277,7 +283,15 @@ pub async fn main( }; match cli_args { - CLIArgs::Synthesizable(subcmd) => execute_direct_subcommand(subcmd, &cwd, options).await, + CLIArgs::Synthesizable(subcmd) => { + // Only the built-ins can be mistaken for a script. `run`/`cache` + // below are the script path itself; `install` and friends + // legitimately trigger a project's `install` lifecycle scripts + // through the package manager, so redirecting those to `vpr` would + // be wrong; and `exec` names a binary rather than a task. + script_note::print(raw_subcommand.as_deref(), &cwd); + execute_direct_subcommand(subcmd, &cwd, options).await + } CLIArgs::ViteTask(command) => execute_vite_task_command(command, cwd, options).await, CLIArgs::PackageManager(pm) => execute_pm_command(pm, &cwd).await, CLIArgs::Exec(exec_args) => crate::exec::execute(exec_args, &cwd).await, diff --git a/packages/cli/binding/src/cli/script_note.rs b/packages/cli/binding/src/cli/script_note.rs new file mode 100644 index 0000000000..8c519fc01a --- /dev/null +++ b/packages/cli/binding/src/cli/script_note.rs @@ -0,0 +1,59 @@ +//! Notes for built-in commands that share a name with a `package.json` script. +//! +//! `vp dev` always runs the built-in dev server; the project's `dev` script is +//! a separate thing, reached with `vpr dev`. Users regularly reach for the +//! built-in when they meant the script, so a built-in whose name a script also +//! uses points at `vpr`. + +use owo_colors::OwoColorize; +use vite_path::AbsolutePath; +use vite_shared::output; +use vite_task::MARKER_ENV_NAME; + +const NPM_LIFECYCLE_EVENT_ENV_NAME: &str = "npm_lifecycle_event"; + +/// Point a built-in command at `vpr ` when a script of that name +/// exists. +/// +/// `command` is the built-in as the user spelled it: the global binary forwards +/// the invoked subcommand, so `vp format` stays `format` here. A direct local +/// invocation takes the first argument before help normalization. +pub(super) fn print(command: Option<&str>, cwd: &AbsolutePath) { + let Some(command) = command else { return }; + // A task spawned this command, so the user is already on a script-running + // path. npm-compatible runners set `npm_lifecycle_event`; Vite Task uses + // its own marker. + if std::env::var_os(MARKER_ENV_NAME).is_some() + || std::env::var_os(NPM_LIFECYCLE_EVENT_ENV_NAME).is_some_and(|event| !event.is_empty()) + { + return; + } + if !has_package_json_script(cwd, command) { + return; + } + + let built_in = format!("`vp {command}`").bright_blue().to_string(); + let via_run = format!("`vpr {command}`").bright_blue().to_string(); + output::note(&format!( + "You are running {built_in} as a Vite+ built-in command. \ + If you meant to run the {command} npm script, use {via_run} instead." + )); +} + +/// Whether the package enclosing `cwd` defines a `` script. +/// +/// Walks up to the nearest `package.json`, which is the package `vp run` +/// resolves the task from, so the note holds when a built-in runs from a +/// subdirectory. It stops there rather than climbing to a package that happens +/// to define the script: `vpr ` would not reach that one either. +fn has_package_json_script(cwd: &AbsolutePath, name: &str) -> bool { + let Ok(package) = vite_workspace::find_package_root(cwd) else { return false }; + serde_json::from_slice::(package.package_json.content()).is_ok_and( + |manifest| { + manifest + .get("scripts") + .and_then(|scripts| scripts.get(name)) + .is_some_and(serde_json::Value::is_string) + }, + ) +} diff --git a/packages/cli/src/bin.ts b/packages/cli/src/bin.ts index 86f0d6f255..d5f5e5effb 100644 --- a/packages/cli/src/bin.ts +++ b/packages/cli/src/bin.ts @@ -42,7 +42,8 @@ function getErrorMessage(err: unknown): string { } // Parse command line arguments -let args = process.argv.slice(2); +const typedArgs = process.argv.slice(2); +let args = typedArgs; // Transform `vp help [command]` into `vp [command] --help` if (args[0] === 'help' && args[1]) { @@ -87,7 +88,9 @@ if (command === 'create') { test, doc, resolveUniversalViteConfig, - args: process.argv.slice(2), + // The Rust CLI applies the `help [command]` transform itself, and needs + // the untransformed list to tell `vp help fmt` apart from `vp fmt --help`. + args: typedArgs, }); let finalExitCode = exitCode;