From d2b6ea16bf10e7dd54590630553fdc29ccb808f3 Mon Sep 17 00:00:00 2001 From: cpojer Date: Thu, 12 Mar 2026 09:43:43 +0900 Subject: [PATCH] chore: Minor CLI polish. --- crates/vite_global_cli/src/command_picker.rs | 19 ++++++++++++++----- packages/cli/binding/src/lib.rs | 16 ++++++++++++++-- .../run-task-command-conflict/package.json | 7 +++++++ .../run-task-command-conflict/snap.txt | 5 +++++ .../run-task-command-conflict/steps.json | 6 ++++++ .../run-task-command-conflict/vite.config.ts | 9 +++++++++ packages/cli/src/bin.ts | 16 ++++++++++++++-- 7 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 packages/cli/snap-tests/run-task-command-conflict/package.json create mode 100644 packages/cli/snap-tests/run-task-command-conflict/snap.txt create mode 100644 packages/cli/snap-tests/run-task-command-conflict/steps.json create mode 100644 packages/cli/snap-tests/run-task-command-conflict/vite.config.ts diff --git a/crates/vite_global_cli/src/command_picker.rs b/crates/vite_global_cli/src/command_picker.rs index 95fbbb63b5..2269a54e1d 100644 --- a/crates/vite_global_cli/src/command_picker.rs +++ b/crates/vite_global_cli/src/command_picker.rs @@ -304,10 +304,7 @@ fn render_picker( let pad = if vite_shared::header::is_warp_terminal() { " " } else { "" }; let max_width = usize::from(columns).saturating_sub(4 + pad.len()); let viewport_end = (viewport_start + viewport_size).min(filtered_indices.len()); - let instruction = truncate_line( - &format!("Select a command (↑/↓, Enter to run, Esc to cancel): {query}"), - max_width, - ); + let instruction = truncate_line(&picker_instruction(query), max_width); execute!(stdout, cursor::MoveTo(0, 0), terminal::Clear(ClearType::All),)?; if vite_shared::header::is_warp_terminal() { @@ -440,6 +437,10 @@ fn render_picker( stdout.flush() } +fn picker_instruction(query: &str) -> String { + format!("Select a command (↑/↓, Enter to run, type to search): {query}") +} + fn compute_viewport_size( terminal_rows: usize, total_commands: usize, @@ -552,7 +553,7 @@ fn filtered_command_indices(query: &str, command_order: &[usize]) -> Vec mod tests { use super::{ COMMANDS, align_viewport, compute_viewport_size, default_command_order, - filtered_command_indices, selected_command_parts, + filtered_command_indices, picker_instruction, selected_command_parts, }; #[test] @@ -670,4 +671,12 @@ mod tests { assert_eq!(help.label, "help"); assert_eq!(help.summary, "View all commands and details"); } + + #[test] + fn picker_instruction_mentions_search() { + assert_eq!( + picker_instruction(""), + "Select a command (↑/↓, Enter to run, type to search): " + ); + } } diff --git a/packages/cli/binding/src/lib.rs b/packages/cli/binding/src/lib.rs index 1b81da62da..4da1358464 100644 --- a/packages/cli/binding/src/lib.rs +++ b/packages/cli/binding/src/lib.rs @@ -18,7 +18,7 @@ mod package_manager; #[allow(dead_code)] mod utils; -use std::{collections::HashMap, ffi::OsStr, sync::Arc}; +use std::{collections::HashMap, error::Error as StdError, ffi::OsStr, fmt::Write as _, sync::Arc}; use napi::{anyhow, bindgen_prelude::*, threadsafe_function::ThreadsafeFunction}; use napi_derive::napi; @@ -111,6 +111,18 @@ fn create_vite_config_resolver( }) } +fn format_error_message(error: &(dyn StdError + 'static)) -> String { + let mut message = error.to_string(); + let mut source = error.source(); + + while let Some(current) = source { + let _ = write!(message, "\n* {current}"); + source = current.source(); + } + + message +} + /// Main entry point for the CLI, called from JavaScript. /// /// This is an async function that spawns a new thread for the non-Send async code @@ -180,7 +192,7 @@ pub async fn run(options: CliOptions) -> Result { vite_error::Error::UserCancelled => Ok(130), _ => { tracing::error!("Rust error: {:?}", e); - Err(anyhow::Error::from(e).into()) + Err(napi::Error::from_reason(format_error_message(&e))) } }, } diff --git a/packages/cli/snap-tests/run-task-command-conflict/package.json b/packages/cli/snap-tests/run-task-command-conflict/package.json new file mode 100644 index 0000000000..342d268139 --- /dev/null +++ b/packages/cli/snap-tests/run-task-command-conflict/package.json @@ -0,0 +1,7 @@ +{ + "name": "run-task-command-conflict-test", + "private": true, + "scripts": { + "build": "echo 'build from package.json'" + } +} diff --git a/packages/cli/snap-tests/run-task-command-conflict/snap.txt b/packages/cli/snap-tests/run-task-command-conflict/snap.txt new file mode 100644 index 0000000000..1e5b581396 --- /dev/null +++ b/packages/cli/snap-tests/run-task-command-conflict/snap.txt @@ -0,0 +1,5 @@ +> # Test that conflicting package.json and vite.config.ts task commands render cleanly +[1]> vp run build +error: Failed to load task graph +* Failed to resolve task config for task run-task-command-conflict-test#build +* Both package.json script and vite.config.* task define commands for the task diff --git a/packages/cli/snap-tests/run-task-command-conflict/steps.json b/packages/cli/snap-tests/run-task-command-conflict/steps.json new file mode 100644 index 0000000000..841a22dfbb --- /dev/null +++ b/packages/cli/snap-tests/run-task-command-conflict/steps.json @@ -0,0 +1,6 @@ +{ + "commands": [ + "# Test that conflicting package.json and vite.config.ts task commands render cleanly", + "vp run build" + ] +} diff --git a/packages/cli/snap-tests/run-task-command-conflict/vite.config.ts b/packages/cli/snap-tests/run-task-command-conflict/vite.config.ts new file mode 100644 index 0000000000..1f6f297857 --- /dev/null +++ b/packages/cli/snap-tests/run-task-command-conflict/vite.config.ts @@ -0,0 +1,9 @@ +export default { + run: { + tasks: { + build: { + command: "echo 'build from vite.config.ts'", + }, + }, + }, +}; diff --git a/packages/cli/src/bin.ts b/packages/cli/src/bin.ts index 9f9f520216..10d66a1b14 100644 --- a/packages/cli/src/bin.ts +++ b/packages/cli/src/bin.ts @@ -21,7 +21,19 @@ import { pack } from './resolve-pack.js'; import { test } from './resolve-test.js'; import { resolveUniversalViteConfig } from './resolve-vite-config.js'; import { vite } from './resolve-vite.js'; -import { accent, log } from './utils/terminal.js'; +import { accent, errorMsg, log } from './utils/terminal.js'; + +function getErrorMessage(err: unknown): string { + if (err instanceof Error) { + return err.message; + } + + if (typeof err === 'object' && err && 'message' in err && typeof err.message === 'string') { + return err.message; + } + + return String(err); +} // Parse command line arguments let args = process.argv.slice(2); @@ -113,7 +125,7 @@ if (command === 'create') { process.exit(finalExitCode); } catch (err) { - console.error('[Vite+] run error:', err); + errorMsg(getErrorMessage(err)); process.exit(1); } }