mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
a26f856402
`ExternalFormatter` is merely a transport that calls the JS side via napi; it is not referred to as a formatter.
84 lines
3.5 KiB
TypeScript
84 lines
3.5 KiB
TypeScript
import { runCli } from "./bindings";
|
|
import {
|
|
initExternalServices,
|
|
disposeExternalServices,
|
|
formatFile,
|
|
formatEmbeddedCode,
|
|
formatEmbeddedDoc,
|
|
sortTailwindClasses,
|
|
} from "./cli/worker-proxy";
|
|
import { loadJsConfig, loadVitePlusConfig } from "./cli/js_config";
|
|
|
|
// napi-JS `oxfmt` CLI entry point
|
|
// See also `run_cli()` function in `./src/main_napi.rs`
|
|
|
|
void (async () => {
|
|
const args = process.argv.slice(2);
|
|
|
|
// Node.js sets non-TTY `stdio` to non-blocking mode,
|
|
// which causes `WouldBlock` errors in Rust.
|
|
// As a workaround, if used with pipe, set blocking mode before calling NAPI bindings.
|
|
// See: https://github.com/napi-rs/napi-rs/issues/1630
|
|
//
|
|
// stdout: Writing large formatted output via `--stdin-filepath` can overflow the pipe buffer.
|
|
// https://github.com/oxc-project/oxc/issues/17939 (observed on macOS)
|
|
// @ts-expect-error: `_handle` is an internal API
|
|
if (!process.stdout.isTTY) process.stdout._handle?.setBlocking?.(true);
|
|
// stdin: In LSP mode (`--lsp`), VSCode communicates via stdin/stdout pipes.
|
|
// Rust reads stdin expecting blocking I/O, but non-blocking mode returns `EAGAIN` (os error 11).
|
|
// https://github.com/oxc-project/oxc/issues/20285
|
|
// @ts-expect-error: `_handle` is an internal API
|
|
if (!process.stdin.isTTY) process.stdin._handle?.setBlocking?.(true);
|
|
|
|
// LSP uses stdout for communication, so write logs to stderr to avoid breaking the protocol.
|
|
// Since LSP is handled on the Rust side, we have to check the flag here. (`runCli()` starts the server and waits)
|
|
// Also, for Oxfmt, this only actually affects loading JS/TS config files.
|
|
// The call to Prettier is currently done via `child_process`, so it won't break LSP.
|
|
if (args.includes("--lsp")) process.stdout.write = process.stderr.write.bind(process.stderr);
|
|
|
|
// Call the Rust CLI first, to parse args and determine mode
|
|
// NOTE: If the mode is formatter CLI, it will also perform formatting and return an exit code
|
|
const [mode, exitCode] = await runCli(
|
|
args,
|
|
process.env.VP_VERSION ? loadVitePlusConfig : loadJsConfig,
|
|
initExternalServices,
|
|
formatFile,
|
|
formatEmbeddedCode,
|
|
formatEmbeddedDoc,
|
|
sortTailwindClasses,
|
|
);
|
|
|
|
// Migration modes are handled by JS
|
|
if (mode === "init") {
|
|
await import("./cli/migration/init").then((m) => m.runInit());
|
|
return;
|
|
}
|
|
if (mode === "migrate:prettier") {
|
|
await import("./cli/migration/migrate-prettier").then((m) => m.runMigratePrettier());
|
|
return;
|
|
}
|
|
if (mode === "migrate:biome") {
|
|
await import("./cli/migration/migrate-biome").then((m) => m.runMigrateBiome());
|
|
return;
|
|
}
|
|
|
|
// Other modes are handled by Rust, just need to set `exitCode`
|
|
|
|
// Clean up worker pool to not V8 crashes on process exit
|
|
await disposeExternalServices();
|
|
|
|
// 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
|
|
process.exitCode = exitCode!;
|
|
|
|
// Node.js < 25.4.0 has a race condition with ThreadsafeFunction cleanup that causes
|
|
// crashes on large codebases. Add a small delay to allow pending NAPI operations
|
|
// to complete before exit. Fixed in Node.js 25.4.0+.
|
|
// See: https://github.com/nodejs/node/issues/55706
|
|
const [major, minor] = process.versions.node.split(".").map(Number);
|
|
if (major < 25 || (major === 25 && minor < 4)) {
|
|
setTimeout(() => process.exit(), 50);
|
|
}
|
|
})();
|