diff --git a/README.md b/README.md index b9d2767..724aa0f 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 oxlint prettier publint eslint +pnpm i -D typescript oxlint prettier publint eslint markdownlint-cli ``` ## Contributing @@ -30,10 +30,14 @@ bun run build # Run checks bun check --help bun check + +# Debug commands used +DEBUG=1 bun check ``` ### Adding Tools -I've added everything I use, so if you want to add support for another tool, feel free. - -Just copy `src/tools/prettier.ts` and `src/tools/prettier.test.ts`, update the implementations (yes, tests are required), and add your new tool to `src/tools/index.ts`'s `ALL_TOOLS` export. +1. Copy and rename `src/tools/prettier.ts` and `src/tools/prettier.test.ts` accordingly +2. Implement and update tests for your new tool +3. Add your tool to the `ALL_TOOLS` array in `src/tools/index.ts` +4. Add the tool's NPM package to the first section of this README diff --git a/src/tools/index.ts b/src/tools/index.ts index 469d738..143ff3f 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,7 +1,15 @@ import { eslint } from "./eslint"; +import { markdownlint } from "./markdownlint"; import { oxlint } from "./oxlint"; import { prettier } from "./prettier"; import { publint } from "./publint"; import { typescript } from "./typescript"; -export const ALL_TOOLS = [publint, prettier, typescript, oxlint, eslint]; +export const ALL_TOOLS = [ + eslint, + markdownlint, + oxlint, + prettier, + publint, + typescript, +]; diff --git a/src/tools/markdownlint.test.ts b/src/tools/markdownlint.test.ts new file mode 100644 index 0000000..7eb2e6a --- /dev/null +++ b/src/tools/markdownlint.test.ts @@ -0,0 +1,74 @@ +import { describe, it, expect } from "bun:test"; +import { parseOutput } from "./markdownlint"; + +describe("Markdownlint", () => { + it("should properly parse output", async () => { + const stdout = ""; + const stderr = ` +[ + { + "fileName": "docs/guide/resources/upgrading.md", + "lineNumber": 59, + "ruleNames": [ + "MD031", + "blanks-around-fences" + ], + "ruleDescription": "Fenced code blocks should be surrounded by blank lines", + "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md031.md", + "errorDetail": null, + "errorContext": "\`\`\`ts", + "errorRange": null, + "fixInfo": { + "lineNumber": 59, + "insertText": "\\n" + } + }, + { + "fileName": "CODE_OF_CONDUCT.md", + "lineNumber": 63, + "ruleNames": [ + "MD034", + "no-bare-urls" + ], + "ruleDescription": "Bare URL used", + "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md034.md", + "errorDetail": null, + "errorContext": "example@gmail.com", + "errorRange": [ + 5, + 23 + ], + "fixInfo": { + "editColumn": 1, + "deleteCount": 23, + "insertText": "" + } + } +] +`; + const code = 1; + + expect(parseOutput({ code, stdout, stderr })).toEqual([ + { + file: "docs/guide/resources/upgrading.md", + message: "Fenced code blocks should be surrounded by blank lines", + location: { + line: 59, + column: 0, + }, + rule: "MD031", + kind: "warning", + }, + { + file: "CODE_OF_CONDUCT.md", + message: "Bare URL used", + location: { + line: 63, + column: 5, + }, + rule: "MD034", + kind: "warning", + }, + ]); + }); +}); diff --git a/src/tools/markdownlint.ts b/src/tools/markdownlint.ts new file mode 100644 index 0000000..0741577 --- /dev/null +++ b/src/tools/markdownlint.ts @@ -0,0 +1,40 @@ +import type { OutputParser, ToolDefinition } from "../types"; +import { execAndParse } from "../utils"; + +export const markdownlint: ToolDefinition = ({ root }) => { + const bin = "markdownlint"; + const checkArgs = [ + ".", + "--json", + "--ignore='**/dist/**'", + "--ignore='**/node_modules/**'", + "--ignore='**/.output/**'", + "--ignore='**/coverage/**'", + ]; + const fixArgs = [...checkArgs, "--fix"]; + + return { + name: "Markdownlint", + packageName: "markdownlint-cli", + check: () => execAndParse(bin, checkArgs, root, parseOutput), + fix: () => execAndParse(bin, fixArgs, root, parseOutput), + }; +}; + +export const parseOutput: OutputParser = ({ stderr: _stderr }) => { + const stderr = _stderr.trim(); + + // When there are no errors, stderr is blank + if (!stderr) return []; + + return JSON.parse(stderr).map((warning: any) => ({ + location: { + line: warning.lineNumber, + column: warning.errorRange?.[0] ?? 0, + }, + message: warning.ruleDescription, + file: warning.fileName, + kind: "warning", + rule: warning.ruleNames[0], + })); +};