import { spawnSync } from 'node:child_process' import { resolve } from 'node:path' import { fileURLToPath } from 'node:url' const oxlintCli = fileURLToPath(new URL('../node_modules/oxlint/bin/oxlint', import.meta.url)) const MAX_CAPTURED_OUTPUT_BYTES = 64 * 1024 * 1024 const FIX_FLAGS = new Set(['--fix', '--fix-dangerously', '--fix-suggestions']) function isFixInvocation(args: readonly string[]): boolean { return args.some(arg => FIX_FLAGS.has(arg)) } /** Complete Oxlint child-process arguments and environment. */ export interface OxlintInvocation { readonly args: readonly string[] readonly env: NodeJS.ProcessEnv } /** * Apply the repository worker bound to both Oxlint backends. * @param args - Oxlint CLI arguments requested by the caller. * @param env - Environment inherited by the Oxlint process. * @returns the complete CLI arguments and child environment. */ export function resolveOxlintInvocation(args: readonly string[], env: NodeJS.ProcessEnv): OxlintInvocation { const raw = env.DSH_OXLINT_THREADS if (raw === undefined || raw === '') return { args: [...args], env: { ...env } } const parsed = Number.parseInt(raw, 10) if (!Number.isSafeInteger(parsed) || parsed < 1 || String(parsed) !== raw) { throw new Error(`run-oxlint: DSH_OXLINT_THREADS must be a positive integer, got ${JSON.stringify(raw)}.`) } if (args.some(arg => arg === '--threads' || arg.startsWith('--threads='))) { throw new Error('run-oxlint: use DSH_OXLINT_THREADS instead of passing --threads directly.') } return { args: [...args, `--threads=${raw}`], env: { ...env, GOMAXPROCS: raw }, } } function completeFrom(result: { readonly signal: NodeJS.Signals | null; readonly status: number | null }): void { if (result.signal !== null) { process.kill(process.pid, result.signal) return } process.exitCode = result.status ?? 1 } function main(): void { const invocation = resolveOxlintInvocation(process.argv.slice(2), process.env) if (!isFixInvocation(invocation.args)) { const result = spawnSync(process.execPath, [oxlintCli, ...invocation.args], { env: invocation.env, stdio: 'inherit', }) if (result.error !== undefined) throw result.error completeFrom(result) return } const first = spawnSync(process.execPath, [oxlintCli, ...invocation.args], { encoding: 'utf8', env: invocation.env, maxBuffer: MAX_CAPTURED_OUTPUT_BYTES, }) if (first.error !== undefined) throw first.error if (first.signal !== null) { completeFrom(first) return } if (first.status === 0) { process.stdout.write(first.stdout) process.stderr.write(first.stderr) process.exitCode = 0 return } // Overlapping JS-plugin fixes can expose one more fixable diagnostic after the first pass. const second = spawnSync(process.execPath, [oxlintCli, ...invocation.args], { env: invocation.env, stdio: 'inherit', }) if (second.error !== undefined) throw second.error completeFrom(second) } const entrypoint = process.argv[1] if (entrypoint !== undefined && resolve(entrypoint) === fileURLToPath(import.meta.url)) main()