Skip to content

Commit c900e31

Browse files
authored
Merge branch 'main' into feat/task-command-shorthands
2 parents 0958327 + c945cc0 commit c900e31

19 files changed

Lines changed: 164 additions & 31 deletions

File tree

crates/vite_task/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ pub mod session;
44

55
// Public exports for vite_task_bin
66
pub use cli::{CacheSubcommand, Command, RunCommand, RunFlags};
7-
pub use session::{CommandHandler, ExitStatus, HandledCommand, Session, SessionConfig};
7+
pub use session::{
8+
CommandHandler, ExitStatus, HandledCommand, Session, SessionConfig, print_error,
9+
};
810
pub use vite_task_graph::{
911
config::{
1012
self,

crates/vite_task/src/session/mod.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -244,15 +244,19 @@ impl<'a> Session<'a> {
244244

245245
/// Primary entry point for CLI usage. Plans and executes the given command.
246246
///
247-
/// # Errors
248-
///
249-
/// Returns an error if planning or execution fails.
247+
/// Any error encountered during planning or execution is printed to stderr
248+
/// with a bold red `error:` prefix, with each level of the error chain on
249+
/// its own `* `-prefixed line. Returns the exit status — callers exit the
250+
/// process with it.
250251
#[tracing::instrument(level = "debug", skip_all)]
251-
pub async fn main(mut self, command: Command) -> anyhow::Result<ExitStatus> {
252+
pub async fn main(mut self, command: Command) -> ExitStatus {
252253
match self.main_inner(command).await {
253-
Ok(()) => Ok(ExitStatus::SUCCESS),
254-
Err(SessionError::EarlyExit(status)) => Ok(status),
255-
Err(SessionError::Anyhow(err)) => Err(err),
254+
Ok(()) => ExitStatus::SUCCESS,
255+
Err(SessionError::EarlyExit(status)) => status,
256+
Err(SessionError::Anyhow(err)) => {
257+
print_error(&err);
258+
ExitStatus::FAILURE
259+
}
256260
}
257261
}
258262

@@ -795,6 +799,29 @@ impl<'a> Session<'a> {
795799
}
796800
}
797801

802+
/// Print `error` to stderr formatted as the `vp` CLI does:
803+
///
804+
/// ```text
805+
/// error: <top-level message>
806+
/// * <source>
807+
/// * <source.source()>
808+
/// ```
809+
///
810+
/// The `error:` prefix is bold red when stderr supports ANSI colors.
811+
pub fn print_error(error: &anyhow::Error) {
812+
use std::io::Write as _;
813+
814+
use owo_colors::{OwoColorize as _, Stream, Style};
815+
816+
let prefix = "error:".if_supports_color(Stream::Stderr, |s| s.style(Style::new().red().bold()));
817+
let mut stderr = std::io::stderr().lock();
818+
let _ = write!(stderr, "{prefix} {error}");
819+
for source in error.chain().skip(1) {
820+
let _ = write!(stderr, "\n* {source}");
821+
}
822+
let _ = writeln!(stderr);
823+
}
824+
798825
/// Whether stdout supports ANSI color output for the current process. Honors
799826
/// `NO_COLOR`/`FORCE_COLOR` and detects TTY capability via the `supports-color`
800827
/// crate. Result is cached for the process lifetime.

crates/vite_task_bin/src/main.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,21 @@ use vite_task::{Command, ExitStatus, Session};
33
use vite_task_bin::OwnedSessionConfig;
44

55
fn main() -> ! {
6-
let exit_code: i32 =
7-
tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap().block_on(async {
8-
match run().await {
9-
Ok(status) => i32::from(status.0),
10-
#[expect(clippy::print_stderr, reason = "top-level error reporting")]
11-
Err(err) => {
12-
eprintln!("Error: {err:?}");
13-
1
14-
}
15-
}
16-
});
6+
let status: ExitStatus =
7+
tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap().block_on(run());
178

18-
std::process::exit(exit_code);
9+
std::process::exit(i32::from(status.0));
1910
}
2011

21-
async fn run() -> anyhow::Result<ExitStatus> {
12+
async fn run() -> ExitStatus {
2213
let args = Command::parse();
2314
let mut owned_config = OwnedSessionConfig::default();
24-
let session = Session::init(owned_config.as_config())?;
15+
let session = match Session::init(owned_config.as_config()) {
16+
Ok(session) => session,
17+
Err(err) => {
18+
vite_task::print_error(&err);
19+
return ExitStatus::FAILURE;
20+
}
21+
};
2522
session.main(args).await
2623
}

crates/vite_task_bin/tests/e2e_snapshots/fixtures/error_cycle_dependency/snapshots/cycle_dependency_error.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ task-a -> task-b -> task-a cycle
99
**Exit code:** 1
1010

1111
```
12-
Error: Cycle dependency detected: error-cycle-dependency-test#task-a -> error-cycle-dependency-test#task-b -> error-cycle-dependency-test#task-a
12+
error: Cycle dependency detected: error-cycle-dependency-test#task-a -> error-cycle-dependency-test#task-b -> error-cycle-dependency-test#task-a
1313
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const value = 'a';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const value = 'b';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const value = 'c';
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "node-compile-cache-outside-workspace-fixture",
3+
"private": true,
4+
"type": "module"
5+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Tiny Node script that turns on the v22 compile cache and imports a
2+
// few sibling modules so the cache directory actually gets some files
3+
// written to it. On a normally-configured machine the cache lives in
4+
// the OS temp directory (outside the workspace), so the runner doesn't
5+
// see those files when it decides whether the run can be cached.
6+
//
7+
// If the spawned process doesn't have LOCALAPPDATA (or TMP/TEMP/
8+
// USERPROFILE) set on Windows, Node ends up putting the cache inside
9+
// the workspace, the same files are both written and read in this one
10+
// run, and the runner refuses to cache it. That's the bug this fixture
11+
// catches.
12+
import { enableCompileCache } from 'node:module';
13+
14+
enableCompileCache();
15+
16+
await import('./a.mjs');
17+
await import('./b.mjs');
18+
await import('./c.mjs');
19+
20+
console.log('done');
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[[e2e]]
2+
name = "node_compile_cache_does_not_poison_workspace"
3+
comment = """
4+
Runs a small Node script that turns on Node's compile cache. The cache should land in the OS temp directory (outside the workspace), so two `vt run --cache build` calls should be a miss then a hit. On Windows, if the spawned task env doesn't have `LOCALAPPDATA`, Node puts the cache inside the workspace instead, the runner sees the same files both written and read, and refuses to cache the run — so the second call becomes another miss with a "not cached because it modified its input" message.
5+
"""
6+
ignore = true
7+
steps = [
8+
{ argv = [
9+
"vt",
10+
"run",
11+
"--cache",
12+
"build",
13+
], comment = "first run: cache miss" },
14+
{ argv = [
15+
"vt",
16+
"run",
17+
"--cache",
18+
"build",
19+
], comment = "second run: cache hit" },
20+
]

0 commit comments

Comments
 (0)