From 496c873af5e909d3bd3e401d9eb71bd763c30b77 Mon Sep 17 00:00:00 2001 From: wan9chi Date: Mon, 27 Jul 2026 11:54:44 +0800 Subject: [PATCH] feat(cli): note when a built-in shares a name with a package.json script Built-in commands cannot be overridden, so `vp dev` runs the Vite+ dev server even in a project whose `dev` script does something else entirely. Users regularly reach for the built-in when they meant the script. Every built-in that can be mistaken for a script now prints: 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. It stays quiet when the project has no script of that name, when stdout is not a terminal (IDEs read it as formatted source or the LSP protocol), and when the task runner spawned the command, since the user is then already on the `vpr` path. That last case relies on `VP_RUN`, added in voidzero-dev/vite-task#570, which is why the vite-task revision moves with this change. Refs #2243 --- Cargo.lock | 60 ++++----- Cargo.toml | 18 +-- .../fixtures/builtin_script_note/package.json | 12 ++ .../builtin_script_note/snapshots.toml | 28 +++++ .../snapshots/builtin_script_note.global.md | 116 ++++++++++++++++++ .../snapshots/builtin_script_note.local.md | 113 +++++++++++++++++ .../builtin_script_note_inside_task.md | 13 ++ .../fixtures/builtin_script_note/src/valid.js | 6 + .../builtin_script_note/vite.config.ts | 8 ++ .../snapshots/vp_build_cache.md | 1 + .../snapshots/vp_build_cache_monorepo.md | 1 + packages/cli/binding/src/cli/mod.rs | 32 ++++- packages/cli/binding/src/cli/script_note.rs | 54 ++++++++ packages/cli/src/bin.ts | 7 +- 14 files changed, 424 insertions(+), 45 deletions(-) create mode 100644 crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/package.json create mode 100644 crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots.toml create mode 100644 crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note.global.md create mode 100644 crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note.local.md create mode 100644 crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note_inside_task.md create mode 100644 crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/src/valid.js create mode 100644 crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/vite.config.ts create mode 100644 packages/cli/binding/src/cli/script_note.rs 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/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/package.json new file mode 100644 index 0000000000..aaf8e6e2c4 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/package.json @@ -0,0 +1,12 @@ +{ + "name": "@test/builtin-script-note", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --host --port 5000", + "lint": "vp lint --deny-warnings src/", + "format": "prettier --write src/", + "wrapped-dev": "vpt pipe-stdin ignored -- vp dev --port 12312312312" + } +} 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..f3f00c6b6f --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots.toml @@ -0,0 +1,28 @@ +# A built-in command never runs a `package.json` script of the same name, so it +# points at `vpr ` instead. This project defines `dev`, `lint` and +# `format` 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", "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 = "no note: a help request runs nothing, and reaches the local CLI as `dev --help`", 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 }, +] + +# A script that wraps `vp dev` in another program is spawned verbatim, so the +# nested `vp` only knows it is inside a task from `VP_RUN`, which the task +# runner sets. Without it, this would point at the `vp run` already in progress. +[[case]] +name = "builtin_script_note_inside_task" +vp = "local" +skip-platforms = ["windows"] +steps = [ + { argv = ["vp", "run", "wrapped-dev"], comment = "no note: a task-spawned `vp dev` is already on the script path", continue-on-failure = true }, +] 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..c055305c37 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note.global.md @@ -0,0 +1,116 @@ +# 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 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` + +no note: a help request runs nothing, and reaches the local CLI as `dev --help` + +``` +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..82cd2bf716 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note.local.md @@ -0,0 +1,113 @@ +# 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 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` + +no note: a help request runs nothing, and reaches the local CLI as `dev --help` + +``` +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_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..9ebe9c1155 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/builtin_script_note/snapshots/builtin_script_note_inside_task.md @@ -0,0 +1,13 @@ +# builtin_script_note_inside_task + +## `vp run wrapped-dev` + +no note: a task-spawned `vp dev` is already on the script path + +**Exit code:** 1 + +``` +$ vpt pipe-stdin ignored -- vp dev --port 12312312312 ⊘ cache disabled +error when starting dev server: +Error: No available ports found between 12312312312 and 65535 +``` 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/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/packages/cli/binding/src/cli/mod.rs b/packages/cli/binding/src/cli/mod.rs index 5033a24339..06914371df 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,15 @@ 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 subcommand as the user wrote it. The global CLI resolves a command to + // its canonical name before running this one, so when it says what was + // written, that is the only accurate source. Otherwise take it from the + // command line, where `normalize_help_args` may replace it — `vp help fmt` + // runs as `fmt --help`, and writing `help` is not writing the built-in. + let raw_subcommand = env::var(env_vars::VP_RAW_SUBCOMMAND).ok(); + let written_locally = 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 +285,23 @@ 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. + // + // Without a name from the global CLI, a built-in parsed means an + // untouched token is that built-in's own name; anything + // normalization rewrote was a different request. + let written = raw_subcommand.or_else(|| { + written_locally + .filter(|name| Some(name.as_str()) == args_vec.first().map(String::as_str)) + }); + script_note::print(written.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..0cd907e375 --- /dev/null +++ b/packages/cli/binding/src/cli/script_note.rs @@ -0,0 +1,54 @@ +//! 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; + +/// 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, and `None` means +/// the user asked for something else that ran a built-in on their behalf. +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 the `vpr` path the + // note would recommend. + if std::env::var_os(MARKER_ENV_NAME).is_some() { + 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;