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.
106 lines
3.3 KiB
TypeScript
106 lines
3.3 KiB
TypeScript
import Tinypool from "tinypool";
|
|
import { toFormatFileResult, toNullable } from "../libs/napi-callbacks";
|
|
import type { FormatFileResult } from "../libs/napi-callbacks";
|
|
import type {
|
|
FormatFileParam,
|
|
FormatEmbeddedCodeParam,
|
|
FormatEmbeddedDocParam,
|
|
SortTailwindClassesArgs,
|
|
} from "../libs/apis";
|
|
|
|
// Worker pool for parallel Prettier formatting
|
|
let pool: Tinypool | null = null;
|
|
let poolSize: number | null = null;
|
|
|
|
export async function initExternalServices(numThreads: number): Promise<void> {
|
|
// In LSP mode, this can be called repeatedly for the lifetime of the process.
|
|
// e.g. on every workspace folder build, config-triggered rebuild, etc
|
|
// The process-wide pool must never be recreated or destroyed on re-init:
|
|
// that leaks the previous `child_process` workers, and other workspace folders may have formats in-flight.
|
|
// (https://github.com/oxc-project/oxc/issues/24147)
|
|
// NOTE: `numThreads` never changes within a single session, so the first value wins.
|
|
poolSize ??= numThreads;
|
|
}
|
|
|
|
// Create the pool lazily on first use,
|
|
// so runs that never delegate to Prettier spawn no `child_process` workers at all.
|
|
// (e.g. Rust-tier files only)
|
|
async function getPool(): Promise<Tinypool> {
|
|
// Rust always calls `initExternalServices` before formatting, so this is defensive.
|
|
if (poolSize === null) throw new Error("External services are not initialized");
|
|
|
|
pool ??= new Tinypool({
|
|
filename: new URL("./cli-worker.js", import.meta.url).href,
|
|
minThreads: poolSize,
|
|
maxThreads: poolSize,
|
|
// XXX: Use `child_process` instead of `worker_threads`.
|
|
// Not sure why, but when using `worker_threads`,
|
|
// calls from NAPI (CLI) -> worker threads -> NAPI (prettier-plugin-oxfmt) causes a hang...
|
|
runtime: "child_process",
|
|
// When setting the `runtime: child_process`,
|
|
// `process.env` is not inherited (likely a bug), so it needs to be explicitly specified.
|
|
env: process.env as Record<string, string>,
|
|
});
|
|
return pool;
|
|
}
|
|
|
|
export async function disposeExternalServices(): Promise<void> {
|
|
await pool?.destroy();
|
|
pool = null;
|
|
poolSize = null;
|
|
}
|
|
|
|
// ---
|
|
|
|
export function formatFile(
|
|
options: FormatFileParam["options"],
|
|
code: string,
|
|
): Promise<FormatFileResult> {
|
|
return toFormatFileResult(
|
|
getPool().then((pool) =>
|
|
pool.run({ options, code } satisfies FormatFileParam, { name: "formatFile" }),
|
|
),
|
|
);
|
|
}
|
|
|
|
// ---
|
|
|
|
export function formatEmbeddedCode(
|
|
options: FormatEmbeddedCodeParam["options"],
|
|
code: string,
|
|
): Promise<string | null> {
|
|
return toNullable(
|
|
getPool().then((pool) =>
|
|
pool.run({ options, code } satisfies FormatEmbeddedCodeParam, {
|
|
name: "formatEmbeddedCode",
|
|
}),
|
|
),
|
|
);
|
|
}
|
|
|
|
export function formatEmbeddedDoc(
|
|
options: FormatEmbeddedDocParam["options"],
|
|
code: string,
|
|
): Promise<string | null> {
|
|
return toNullable(
|
|
getPool().then((pool) =>
|
|
pool.run({ options, code } satisfies FormatEmbeddedDocParam, {
|
|
name: "formatEmbeddedDoc",
|
|
}),
|
|
),
|
|
);
|
|
}
|
|
|
|
export function sortTailwindClasses(
|
|
options: SortTailwindClassesArgs["options"],
|
|
classes: string[],
|
|
): Promise<string[] | null> {
|
|
return toNullable(
|
|
getPool().then((pool) =>
|
|
pool.run({ classes, options } satisfies SortTailwindClassesArgs, {
|
|
name: "sortTailwindClasses",
|
|
}),
|
|
),
|
|
);
|
|
}
|