Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/vite_error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ pub enum Error {
#[error("Test failed")]
TestFailed { status: Str, reason: Str },

#[error("Doc failed")]
DocFailed { status: Str, reason: Str },

#[error(
"The stripped path ({stripped_path:?}) is not a valid relative path because: {invalid_path_data_error}"
)]
Expand Down
29 changes: 29 additions & 0 deletions crates/vite_task/src/doc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use std::future::Future;
use std::iter;

use petgraph::stable_graph::StableGraph;

use crate::config::ResolvedTask;
use crate::schedule::ExecutionPlan;
use crate::{Error, ResolveCommandResult, Workspace};

pub async fn doc<
Doc: Future<Output = Result<ResolveCommandResult, Error>>,
DocFn: Fn() -> Doc,
>(
resolve_doc_command: DocFn,
workspace: &mut Workspace,
args: &Vec<String>,
) -> Result<(), Error> {
let resolved_task = ResolvedTask::resolve_from_builtin(
workspace,
resolve_doc_command,
"doc",
iter::once("dev").chain(args.iter().map(std::string::String::as_str)),
)
.await?;
let mut task_graph: StableGraph<ResolvedTask, ()> = Default::default();
task_graph.add_node(resolved_task);
ExecutionPlan::plan(task_graph, false)?.execute(workspace).await?;
Ok(())
}
45 changes: 44 additions & 1 deletion crates/vite_task/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod cache;
mod cmd;
mod collections;
mod config;
mod doc;
mod execute;
mod fingerprint;
mod fmt;
Expand Down Expand Up @@ -100,6 +101,11 @@ pub enum Commands {
/// Arguments to pass to vite install
args: Vec<String>,
},
Doc {
#[clap(last = true)]
/// Arguments to pass to vitepress
args: Vec<String>,
},
/// Manage the task cache
Cache {
#[clap(subcommand)]
Expand Down Expand Up @@ -156,11 +162,16 @@ pub struct CliOptions<
Box<dyn Future<Output = Result<ResolveCommandResult, Error>>>,
>,
TestFn: Fn() -> Test = Box<dyn Fn() -> Test>,
Doc: Future<Output = Result<ResolveCommandResult, Error>> = Pin<
Box<dyn Future<Output = Result<ResolveCommandResult, Error>>>,
>,
DocFn: Fn() -> Doc = Box<dyn Fn() -> Doc>,
> {
pub lint: LintFn,
pub fmt: FmtFn,
pub vite: ViteFn,
pub test: TestFn,
pub doc: DocFn,
}

pub struct ResolveCommandResult {
Expand Down Expand Up @@ -207,10 +218,12 @@ pub async fn main<
ViteFn: Fn() -> Vite,
Test: Future<Output = Result<ResolveCommandResult, Error>>,
TestFn: Fn() -> Test,
Doc: Future<Output = Result<ResolveCommandResult, Error>>,
DocFn: Fn() -> Doc,
>(
cwd: AbsolutePathBuf,
args: Args,
options: Option<CliOptions<Lint, LintFn, Fmt, FmtFn, Vite, ViteFn, Test, TestFn>>,
options: Option<CliOptions<Lint, LintFn, Fmt, FmtFn, Vite, ViteFn, Test, TestFn, Doc, DocFn>>,
) -> Result<(), Error> {
// Auto-install dependencies if needed, but skip for install command itself, or if `VITE_DISABLE_AUTO_INSTALL=1` is set.
if !matches!(args.commands, Some(Commands::Install { .. }))
Expand Down Expand Up @@ -283,6 +296,14 @@ pub async fn main<
install::InstallCommand::builder(cwd).build().execute(&args).await?;
return Ok(());
}
Some(Commands::Doc { args }) => {
let mut workspace = Workspace::partial_load(cwd)?;
if let Some(doc_fn) = options.map(|o| o.doc) {
doc::doc(doc_fn, &mut workspace, args).await?;
workspace.unload().await?;
}
return Ok(());
}
Some(Commands::Cache { subcmd }) => {
let cache_path = Workspace::get_cache_path(&cwd)?;
match subcmd {
Expand Down Expand Up @@ -434,6 +455,28 @@ mod tests {
}
}

#[test]
fn test_args_doc_command() {
let args = Args::try_parse_from(&["vite-plus", "doc"]).unwrap();
assert_eq!(args.task, None);
assert!(args.task_args.is_empty());
assert!(matches!(args.commands, Some(Commands::Doc { .. })));
assert!(!args.debug);
}

#[test]
fn test_args_doc_command_with_args() {
let args =
Args::try_parse_from(&["vite-plus", "doc", "--", "--port", "3000", "--host"]).unwrap();
assert_eq!(args.task, None);
assert!(args.task_args.is_empty());
if let Some(Commands::Doc { args }) = &args.commands {
assert_eq!(args, &vec!["--port".to_string(), "3000".to_string(), "--host".to_string()]);
} else {
panic!("Expected Doc command");
}
}

#[test]
fn test_args_debug_flag() {
let args = Args::try_parse_from(&["vite-plus", "--debug", "build"]).unwrap();
Expand Down
22 changes: 21 additions & 1 deletion packages/cli/binding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub struct CliOptions {
pub vite: Arc<ThreadsafeFunction<(), Promise<JsCommandResolvedResult>>>,
/// Resolver function for the test tool (vitest)
pub test: Arc<ThreadsafeFunction<(), Promise<JsCommandResolvedResult>>>,
/// Resolver function for the doc tool (vitepress)
pub doc: Arc<ThreadsafeFunction<(), Promise<JsCommandResolvedResult>>>,
/// Optional working directory override
pub cwd: Option<String>,
}
Expand All @@ -70,7 +72,7 @@ impl From<JsCommandResolvedResult> for ResolveCommandResult {
}
}

static BUILTIN_COMMANDS: &[&str] = &["lint", "fmt", "build", "test"];
static BUILTIN_COMMANDS: &[&str] = &["lint", "fmt", "build", "test", "doc"];

/// Main entry point for the CLI, called from JavaScript.
///
Expand Down Expand Up @@ -104,6 +106,7 @@ pub async fn run(options: CliOptions) -> Result<()> {
let fmt = options.fmt;
let vite = options.vite;
let test = options.test;
let doc = options.doc;
// Call the Rust core with wrapped resolver functions
if let Err(e) = vite_task::main(
cwd,
Expand Down Expand Up @@ -154,6 +157,17 @@ pub async fn run(options: CliOptions) -> Result<()> {

Ok(resolved.into())
},
// Wrap the doc resolver to be callable from Rust
doc: || async {
let resolved = doc
.call_async(Ok(()))
.await
.map_err(js_error_to_doc_error)?
.await
.map_err(js_error_to_doc_error)?;

Ok(resolved.into())
},
}),
)
.await
Expand Down Expand Up @@ -184,6 +198,11 @@ fn js_error_to_test_error(err: napi::Error) -> Error {
Error::TestFailed { status: err.status.to_string().into(), reason: err.to_string().into() }
}

/// Convert JavaScript errors to Rust doc errors
fn js_error_to_doc_error(err: napi::Error) -> Error {
Error::DocFailed { status: err.status.to_string().into(), reason: err.to_string().into() }
}

fn parse_args() -> Args {
// ArgsOs [node, vite-plus, ...]
let mut raw_args = std::env::args_os().skip(2);
Expand All @@ -204,6 +223,7 @@ fn parse_args() -> Args {
"fmt" => Commands::Fmt { args: forwarded_args },
"build" => Commands::Build { args: forwarded_args },
"test" => Commands::Test { args: forwarded_args },
"doc" => Commands::Doc { args: forwarded_args },
_ => unreachable!(),
}),
debug: false,
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*/

import { run } from '../binding/index.js';
import { doc } from './doc.ts';
import { fmt } from './fmt.ts';
import { lint } from './lint.ts';
import { test } from './test.ts';
Expand All @@ -22,4 +23,5 @@ run({
fmt, // Resolves oxfmt binary for formatting
vite, // Resolves vite binary for build/dev commands
test, // Resolves vitest binary for test commands
doc, // Resolves vitepress binary for documentation
});
45 changes: 45 additions & 0 deletions packages/cli/src/doc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* VitePress tool resolver for the vite-plus CLI.
*
* This module exports a function that resolves the VitePress binary path
* using Node.js module resolution. The resolved path is passed back
* to the Rust core, which then executes VitePress for documentation.
*
* Used for: `vite-plus doc` command
*/

import { createRequire } from 'node:module';
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

const require = createRequire(import.meta.url);

/**
* Resolves the VitePress binary path and environment variables.
*
* @returns Promise containing:
* - binPath: Absolute path to the VitePress CLI entry point
* - envs: Environment variables to set when executing VitePress
*
* VitePress is a Vite & Vue powered static site generator for
* building documentation websites with excellent performance.
*/
export async function doc(): Promise<{
binPath: string;
envs: Record<string, string>;
}> {
// Resolve the VitePress CLI module directly
const binPath = require.resolve('vitepress/bin/vitepress.js', {
paths: [process.cwd(), dirname(fileURLToPath(import.meta.url))],
});

return {
binPath,
// Pass through source map debugging environment variable if set
envs: process.env.DEBUG_DISABLE_SOURCE_MAP
? {
DEBUG_DISABLE_SOURCE_MAP: process.env.DEBUG_DISABLE_SOURCE_MAP,
}
: {},
};
}
Loading