vite-plugin-oxlint
Version:
Oxlint plugin for vite.
142 lines (141 loc) • 4.6 kB
JavaScript
import { spawn } from "cross-spawn";
import nodePath from "node:path";
import { existsSync } from "node:fs";
import { detect } from "package-manager-detector/detect";
import { resolveCommand } from "package-manager-detector/commands";
//#region src/index.ts
const DEBOUNCE_MS = 300;
const resolveAbsolutePath = (p) => nodePath.isAbsolute(p) ? p : nodePath.join(process.cwd(), p);
const buildArgs = (options) => {
const { ignorePattern, configFile = "oxlintrc.json", deny = [], allow = [], warn = [], params = "", format = "", quiet = false, fix = false, failOnWarning = false } = options;
const args = [];
if (quiet) args.push("--quiet");
if (fix) args.push("--fix");
if (format) args.push("--format", format);
if (failOnWarning) args.push("--deny-warnings");
(Array.isArray(ignorePattern) ? ignorePattern : ignorePattern ? [ignorePattern] : []).forEach((pattern) => args.push(`--ignore-pattern=${pattern}`));
deny.forEach((d) => args.push("-D", d));
allow.forEach((a) => args.push("-A", a));
warn.forEach((w) => args.push("-W", w));
const configFilePath = resolveAbsolutePath(configFile);
if (existsSync(configFilePath)) args.push("-c", configFilePath);
if (params) args.push(...params.split(" ").filter(Boolean));
return args;
};
const runChild = ({ cmd, args, cwd, logger, shouldFail, buffered }) => new Promise((resolve, reject) => {
const bufferedOutput = [];
const child = spawn(cmd, args, {
cwd,
env: {
...process.env,
FORCE_COLOR: "1"
},
shell: false,
stdio: "pipe"
});
const emit = (data, log) => {
const trimmed = data.toString().trimEnd();
if (!trimmed) return;
if (buffered) bufferedOutput.push(trimmed);
else log(trimmed);
};
child.stdout?.on("data", (d) => emit(d, (s) => logger?.info(s)));
child.stderr?.on("data", (d) => emit(d, (s) => logger?.error(s)));
child.on("error", (error) => {
if (buffered) {
resolve("fallback");
return;
}
logger?.error(`oxlint Error: ${error.message}`);
reject(error);
});
child.on("exit", (code) => {
const flush = () => bufferedOutput.forEach((line) => logger?.info(line));
if (code === 0) {
if (buffered) flush();
logger?.info("Oxlint successfully finished.");
resolve("ok");
} else if (code === 1) {
if (buffered) flush();
if (shouldFail) reject(/* @__PURE__ */ new Error("Oxlint found lint errors."));
else {
logger?.warn("Oxlint found lint errors.");
resolve("lint-errors");
}
} else if (buffered) resolve("fallback");
else {
logger?.error(`Oxlint exited with unexpected code: ${code}`);
resolve("ok");
}
});
});
const runOxlintOnce = async (options, logger, pmPromise) => {
const { path = "", oxlintPath = "", failOnError = false, failOnWarning = false } = options;
const shouldFail = failOnError || failOnWarning;
const args = buildArgs(options);
const cwd = resolveAbsolutePath(path);
const pm = await pmPromise;
if (!pm) throw new Error("Could not detect package manager");
const tryRun = async (useExecuteLocal) => {
const resolved = oxlintPath ? {
args,
command: resolveAbsolutePath(oxlintPath)
} : resolveCommand(pm.agent, useExecuteLocal ? "execute-local" : "execute", ["oxlint", ...args]);
if (!resolved) {
if (useExecuteLocal && !oxlintPath) return tryRun(false);
throw new Error(`Could not resolve oxlint command for ${pm.agent}`);
}
if (await runChild({
args: resolved.args,
buffered: useExecuteLocal && !oxlintPath,
cmd: resolved.command,
cwd,
logger,
shouldFail
}) === "fallback") return tryRun(false);
};
return tryRun(true);
};
const oxlintPlugin = (options = {}) => {
let timeoutId = void 0;
let pmPromise = void 0;
let logger = void 0;
const getPm = () => {
if (!pmPromise) pmPromise = detect();
return pmPromise;
};
const runOxlint = async () => {
try {
await runOxlintOnce(options, logger, getPm());
} catch (error) {
logger?.error(`Error executing command: ${error}`);
throw error;
}
};
const debouncedRun = () => {
if (timeoutId) clearTimeout(timeoutId);
timeoutId = setTimeout(async () => {
try {
await runOxlintOnce(options, logger, getPm());
} catch (error) {
logger?.error(`Error executing command: ${error}`);
}
}, DEBOUNCE_MS);
};
return {
async buildStart() {
const { lintOnStart = true } = options;
if (lintOnStart) await runOxlint();
},
configResolved(config) {
logger = config.logger;
},
handleHotUpdate() {
const { lintOnHotUpdate = true } = options;
if (lintOnHotUpdate) debouncedRun();
},
name: "vite-plugin-oxlint"
};
};
//#endregion
export { oxlintPlugin as default };