Skip to content
Merged
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
6 changes: 4 additions & 2 deletions apps/oxfmt/src-js/bindings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* 3. `format_embedded_cb`: Callback to format embedded code in templates
* 4. `format_file_cb`: Callback to format files
*
* Returns `true` if formatting succeeded without errors, `false` otherwise.
* Returns a tuple of `[mode, exitCode]`:
* - `mode`: "cli" | "lsp" | "init"
* - `exitCode`: exit code (0, 1, 2) for "cli" mode, `undefined` for other modes
*/
export declare function format(args: Array<string>, setupConfigCb: (configJSON: string, numThreads: number) => Promise<string[]>, formatEmbeddedCb: (tagName: string, code: string) => Promise<string>, formatFileCb: (parserName: string, fileName: string, code: string) => Promise<string>): Promise<boolean>
export declare function runCli(args: Array<string>, setupConfigCb: (configJSON: string, numThreads: number) => Promise<string[]>, formatEmbeddedCb: (tagName: string, code: string) => Promise<string>, formatFileCb: (parserName: string, fileName: string, code: string) => Promise<string>): Promise<[string, number | undefined | null]>
4 changes: 2 additions & 2 deletions apps/oxfmt/src-js/bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -575,5 +575,5 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

const { format } = nativeBinding
export { format }
const { runCli } = nativeBinding
export { runCli }
31 changes: 19 additions & 12 deletions apps/oxfmt/src-js/cli.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { format } from "./bindings.js";
import { runCli } from "./bindings.js";
import { setupConfig, formatEmbeddedCode, formatFile } from "./prettier-proxy.js";
import { runInit } from "./migration/init.js";

void (async () => {
const args = process.argv.slice(2);

// Handle `--init` command in JS
if (args.includes("--init")) {
return await runInit();
}

// Call the Rust formatter with our JS callback
const success = await format(args, setupConfig, formatEmbeddedCode, formatFile);
// Call the Rust CLI to parse args and determine mode
const [mode, exitCode] = await runCli(args, setupConfig, formatEmbeddedCode, formatFile);

// NOTE: It's recommended to set `process.exitCode` instead of calling `process.exit()`.
// `process.exit()` kills the process immediately and `stdout` may not be flushed before process dies.
// https://nodejs.org/api/process.html#processexitcode
if (!success) process.exitCode = 1;
switch (mode) {
// Handle `--init` command in JS
case "init":
await runInit();
break;
// LSP mode is handled by Rust, nothing to do here
case "lsp":
break;
// CLI mode also handled by Rust, just need to set exit code
case "cli":
// NOTE: It's recommended to set `process.exitCode` instead of calling `process.exit()`.
// `process.exit()` kills the process immediately and `stdout` may not be flushed before process dies.
// https://nodejs.org/api/process.html#processexitcode
if (exitCode) process.exitCode = exitCode;
break;
}
})();
16 changes: 11 additions & 5 deletions apps/oxfmt/src/cli/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,18 @@ pub enum CliRunResult {
FormatFailed,
}

impl Termination for CliRunResult {
fn report(self) -> ExitCode {
impl CliRunResult {
pub fn exit_code(&self) -> u8 {
match self {
Self::None | Self::FormatSucceeded => ExitCode::from(0),
Self::InvalidOptionConfig | Self::FormatMismatch => ExitCode::from(1),
Self::NoFilesFound | Self::FormatFailed => ExitCode::from(2),
Self::None | Self::FormatSucceeded => 0,
Self::InvalidOptionConfig | Self::FormatMismatch => 1,
Self::NoFilesFound | Self::FormatFailed => 2,
}
}
}

impl Termination for CliRunResult {
fn report(self) -> ExitCode {
ExitCode::from(self.exit_code())
}
}
2 changes: 1 addition & 1 deletion apps/oxfmt/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use oxfmt::cli::{

// Pure Rust CLI entry point.
// This CLI only supports the basic `Cli` mode.
// For full featured JS CLI entry point, see `format()` exported by `main_napi.rs`.
// For full featured JS CLI entry point, see `run_cli()` exported by `main_napi.rs`.

#[tokio::main]
async fn main() -> CliRunResult {
Expand Down
45 changes: 16 additions & 29 deletions apps/oxfmt/src/main_napi.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
use std::{
ffi::OsString,
process::{ExitCode, Termination},
};
use std::ffi::OsString;

use napi_derive::napi;

use crate::{
cli::{
CliRunResult, FormatRunner, Mode, format_command, init_miette, init_rayon, init_tracing,
},
cli::{FormatRunner, Mode, format_command, init_miette, init_rayon, init_tracing},
core::{ExternalFormatter, JsFormatEmbeddedCb, JsFormatFileCb, JsSetupConfigCb},
lsp::run_lsp,
};
Expand All @@ -24,11 +19,13 @@ use crate::{
/// 3. `format_embedded_cb`: Callback to format embedded code in templates
/// 4. `format_file_cb`: Callback to format files
///
/// Returns `true` if formatting succeeded without errors, `false` otherwise.
/// Returns a tuple of `[mode, exitCode]`:
/// - `mode`: "cli" | "lsp" | "init"
/// - `exitCode`: exit code (0, 1, 2) for "cli" mode, `undefined` for other modes
#[expect(clippy::allow_attributes)]
#[allow(clippy::trailing_empty_array, clippy::unused_async)] // https://github.com/napi-rs/napi-rs/issues/2758
#[napi]
pub async fn format(
pub async fn run_cli(
args: Vec<String>,
#[napi(ts_arg_type = "(configJSON: string, numThreads: number) => Promise<string[]>")]
setup_config_cb: JsSetupConfigCb,
Expand All @@ -38,18 +35,7 @@ pub async fn format(
ts_arg_type = "(parserName: string, fileName: string, code: string) => Promise<string>"
)]
format_file_cb: JsFormatFileCb,
) -> bool {
format_impl(args, setup_config_cb, format_embedded_cb, format_file_cb).await.report()
== ExitCode::SUCCESS
}

/// Run the formatter.
async fn format_impl(
args: Vec<String>,
setup_config_cb: JsSetupConfigCb,
format_embedded_cb: JsFormatEmbeddedCb,
format_file_cb: JsFormatFileCb,
) -> CliRunResult {
) -> (String, Option<u8>) {
// Convert String args to OsString for compatibility with bpaf
let args: Vec<OsString> = args.into_iter().map(OsString::from).collect();

Expand All @@ -58,19 +44,17 @@ async fn format_impl(
Ok(cmd) => cmd,
Err(e) => {
e.print_message(100);
return if e.exit_code() == 0 {
CliRunResult::None
} else {
CliRunResult::InvalidOptionConfig
};
// `bpaf` returns exit_code 0 for --help/--version, non-0 for parse errors
let exit_code = u8::from(e.exit_code() != 0);
return ("cli".to_string(), Some(exit_code));
}
};

match command.mode {
Mode::Init => unreachable!("`--init` should be handled by JS side"),
Mode::Init => ("init".to_string(), None),
Mode::Lsp => {
run_lsp().await;
CliRunResult::None
("lsp".to_string(), None)
}
Mode::Cli(_) => {
init_tracing();
Expand All @@ -81,7 +65,10 @@ async fn format_impl(
let external_formatter =
ExternalFormatter::new(setup_config_cb, format_embedded_cb, format_file_cb);

FormatRunner::new(command).with_external_formatter(Some(external_formatter)).run()
let result =
FormatRunner::new(command).with_external_formatter(Some(external_formatter)).run();

("cli".to_string(), Some(result.exit_code()))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Finished in <variable>ms on 1 files using 1 threads.
--------------------
arguments: --check --ignore-path ignore1
working directory: fixtures/ignore_and_override
exit code: 1
exit code: 2
--- STDOUT ---------
Checking formatting...

Expand All @@ -28,7 +28,7 @@ Expected at least one target file
--------------------
arguments: --check --ignore-path ignore1 should_format/ok.js
working directory: fixtures/ignore_and_override
exit code: 1
exit code: 2
--- STDOUT ---------

--- STDERR ---------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ No files found matching the given patterns.
--------------------
arguments: --check __non__existent__file.js
working directory: fixtures
exit code: 1
exit code: 2
--- STDOUT ---------
Checking formatting...

Expand Down
Loading