diff --git a/README.md b/README.md index 57d2325..7cbaaac 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ pnpm check --fix To enable checks for any of the following modules, just install them: ```sh -pnpm i -D typescript eslint prettier publint +pnpm i -D typescript oxlint prettier publint eslint ``` ## Contributing diff --git a/demo/package.json b/demo/package.json index ba1d28e..5591b29 100644 --- a/demo/package.json +++ b/demo/package.json @@ -12,6 +12,7 @@ "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-only-warn": "^1.1.0", + "oxlint": "^0.9.1", "vue-tsc": "^2.0.7" } } diff --git a/demo/pnpm-lock.yaml b/demo/pnpm-lock.yaml index 0a7432d..65026f2 100644 --- a/demo/pnpm-lock.yaml +++ b/demo/pnpm-lock.yaml @@ -28,6 +28,9 @@ devDependencies: eslint-plugin-only-warn: specifier: ^1.1.0 version: 1.1.0 + oxlint: + specifier: ^0.9.1 + version: 0.9.1 vue-tsc: specifier: ^2.0.7 version: 2.0.7(typescript@5.3.3) @@ -144,6 +147,70 @@ packages: fastq: 1.17.1 dev: true + /@oxlint/darwin-arm64@0.9.1: + resolution: {integrity: sha512-SzYTggt2eJqMhemijEv9AszSdc8Vv1OBQbmAZpDNPHmwlDzPKEopSrDURwt/Jq56FYsJC/jhzBzrJLXSK55i1A==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@oxlint/darwin-x64@0.9.1: + resolution: {integrity: sha512-IDgjpNdtt2XkEcWDIhFyZCkIIonqlBQf3jQ2L1voGQxQFwjl+cP9+hdpFp/m89qGO708MULeegCH+MGq28xWbg==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@oxlint/linux-arm64-gnu@0.9.1: + resolution: {integrity: sha512-kEGN/CNdUB11So4c3OpTIJi0YvPgu5i9gvUcWX+CxEkeACRPj8dq7/Rm1FgfIqky6oYY5QjXDioZ+yKt+Xw1rQ==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@oxlint/linux-arm64-musl@0.9.1: + resolution: {integrity: sha512-US5UVB7JDwNuQa2+RZyWzEtgjEOA9wAvwUy8oUmgqomo02cpCrZo3wOQAvAckE5KxJCskr7T6pGW9eA8y3IhsQ==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@oxlint/linux-x64-gnu@0.9.1: + resolution: {integrity: sha512-w2Sz/WevEa6FxDjwwg9xbmpebDmLXO9YCvUUs5K4emKhzh05xEwuXrD2kJuQUdFapiHHQUMTQ5/iBF0NOPb9Uw==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@oxlint/linux-x64-musl@0.9.1: + resolution: {integrity: sha512-hSukTLTn+wKyJUbR3934bOmWNoDaJv4yJwumE6QqBQZWoFNyd3seiYJLPWD+7aMJx6nOnot8uqjnvYO1QZsccA==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@oxlint/win32-arm64@0.9.1: + resolution: {integrity: sha512-9Y7RuvnulhZyxLdsDrGRaojxytzcBTGHpl7AxED41G/IXGPOs+dE1q2NFyHWB6Gfzi/MJZ6plXYIOKx/e/3t/g==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@oxlint/win32-x64@0.9.1: + resolution: {integrity: sha512-51wrzMDGFyCYZbhSczU9VS7EYe1N0tNlDfdne6VCKgpPPJX0+XLZHybgUFZNYVyf7/W5t1/gpyOTFErVYRP0pw==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true @@ -898,6 +965,21 @@ packages: type-check: 0.4.0 dev: true + /oxlint@0.9.1: + resolution: {integrity: sha512-77v9YVkaVOWguxUttiJ31yfWkG/Yo7kIzY0CrKHxM6p0xiUqN0LVF4IVgjcE6vevyd9bBVphtOgJOJUq4To/+w==} + engines: {node: '>=14.*'} + hasBin: true + optionalDependencies: + '@oxlint/darwin-arm64': 0.9.1 + '@oxlint/darwin-x64': 0.9.1 + '@oxlint/linux-arm64-gnu': 0.9.1 + '@oxlint/linux-arm64-musl': 0.9.1 + '@oxlint/linux-x64-gnu': 0.9.1 + '@oxlint/linux-x64-musl': 0.9.1 + '@oxlint/win32-arm64': 0.9.1 + '@oxlint/win32-x64': 0.9.1 + dev: true + /p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} diff --git a/src/tools/index.ts b/src/tools/index.ts index e39fa1f..469d738 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,6 +1,7 @@ import { eslint } from "./eslint"; +import { oxlint } from "./oxlint"; import { prettier } from "./prettier"; -import { typescript } from "./typescript"; import { publint } from "./publint"; +import { typescript } from "./typescript"; -export const ALL_TOOLS = [publint, prettier, typescript, eslint]; +export const ALL_TOOLS = [publint, prettier, typescript, oxlint, eslint]; diff --git a/src/tools/oxlint.test.ts b/src/tools/oxlint.test.ts new file mode 100644 index 0000000..4a3cd51 --- /dev/null +++ b/src/tools/oxlint.test.ts @@ -0,0 +1,59 @@ +import { describe, it, expect } from "bun:test"; +import { parseOuptut } from "./oxlint"; + +describe("Oxlint", () => { + it("should properly parse output", async () => { + const stdout = ` +test.ts:1:7: Variable 'test' is declared but never used. [Warning/eslint(no-unused-vars)] +test.ts:3:7: Variable 'variable' is declared but never used. [Warning/eslint(no-unused-vars)] +test.ts:8:7: Variable 'two' is declared but never used. [Warning/eslint(no-unused-vars)] +test.ts:8:7: Missing initializer in const declaration [Error] + +3 problems +`; + const stderr = ""; + const code = 1; + + expect(parseOuptut({ code, stdout, stderr })).toEqual([ + { + file: "test.ts", + message: "Variable 'test' is declared but never used.", + location: { + line: 1, + column: 7, + }, + rule: "eslint(no-unused-vars)", + kind: "warning", + }, + { + file: "test.ts", + message: "Variable 'variable' is declared but never used.", + location: { + line: 3, + column: 7, + }, + rule: "eslint(no-unused-vars)", + kind: "warning", + }, + { + file: "test.ts", + message: "Variable 'two' is declared but never used.", + location: { + line: 8, + column: 7, + }, + rule: "eslint(no-unused-vars)", + kind: "warning", + }, + { + file: "test.ts", + message: "Missing initializer in const declaration", + location: { + line: 8, + column: 7, + }, + kind: "error", + }, + ]); + }); +}); diff --git a/src/tools/oxlint.ts b/src/tools/oxlint.ts new file mode 100644 index 0000000..fe33923 --- /dev/null +++ b/src/tools/oxlint.ts @@ -0,0 +1,53 @@ +import type { OutputParser, Problem, ToolDefinition } from "../types"; +import { execAndParse, isBinInstalled } from "../utils"; +import { resolve } from "node:path"; + +export const oxlint: ToolDefinition = ({ binDir, root }) => { + const bin = resolve(root, binDir, "oxlint"); + const checkArgs = ["--format=linux"]; + const fixArgs = ["--format=linux", "--fix"]; + + return { + name: "Oxlint", + isInstalled: () => isBinInstalled(bin), + check: () => execAndParse(bin, checkArgs, root, parseOuptut), + fix: () => execAndParse(bin, fixArgs, root, parseOuptut), + }; +}; + +export const parseOuptut: OutputParser = ({ stdout, stderr }) => { + if (stdout.trim()) { + return stdout.split(/\r?\n/).reduce((acc, line) => { + const groups = + /^(?.+?):(?[0-9]+):(?[0-9]):\s?(?.*?)\s?\[(?Warning|Error)\/?(?.*?)\]\s?$/.exec( + line, + )?.groups; + if (groups) { + acc.push({ + file: groups.file, + kind: groups.kind === "Error" ? "error" : "warning", + message: groups.message, + rule: groups.rule || undefined, + location: { + line: parseInt(groups.line, 10), + column: parseInt(groups.column, 10), + }, + }); + } + return acc; + }, []); + } + + return stdout + .trim() + .split(/\r?\n/) + .map((line) => line.trim()) + .filter((line) => !!line && !line.includes(" ")) + .map( + (line): Problem => ({ + file: line.trim(), + kind: "warning", + message: "Not formatted.", + }), + ); +};