Skip to content

Commit

Permalink
fix: Exit code based on problem count
Browse files Browse the repository at this point in the history
  • Loading branch information
aklinker1 committed Mar 8, 2024
1 parent 03e0678 commit 5cfeb9d
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 167 deletions.
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Check

> [!WARNING]
> I have not actually published this to NPM yet.
An opinionated CLI tool to run all your checks all at once. The command will only exit with code 0 when no warnings exist.

https://github.com/aklinker1/check/assets/10101283/c8089e5c-e25f-4f59-8897-d2a6f97a3139

> [!WARNING]
> I have not actually published this to NPM yet.
```sh
pnpm i @aklinker1/check
pnpm check
Expand Down Expand Up @@ -36,3 +38,9 @@ bun check --help
bun check
bun check demo
```

### 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.
38 changes: 17 additions & 21 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,30 @@ export async function check(options: CheckOptions = {}) {
const startTime = performance.now();
// Run checks
const fn = fix ? tool.fix ?? tool.check : tool.check;
const output = await fn(root);
if ("problems" in output) {
// Ensure problems are absolute paths relative to the root dir
output.problems.forEach((problem) => {
problem.file = resolve(root ?? process.cwd(), problem.file);
});
}
const problems = await fn(root);
// Ensure problems are absolute paths relative to the root dir
problems.forEach((problem) => {
problem.file = resolve(root ?? process.cwd(), problem.file);
});
const duration = humanMs(performance.now() - startTime);

const title = `${tool.name} ${dim(`(${duration})`)}`;
if (output.type === "error") fail(title);
else if (output.type === "warning") warn(title);
const errorCount = problems.filter((p) => p.kind === "error").length;
if (errorCount > 0) fail(title);
else if (problems.length > 0) warn(title);
else succeed(title);

return output;
return problems;
},
);

const problems = results
.flatMap((result) => (result.type === "success" ? [] : result.problems))
.sort((l, r) => {
const nameCompare = l.file.localeCompare(r.file);
if (nameCompare !== 0) return nameCompare;
const lineCompare = (l.location?.line ?? 0) - (r.location?.line ?? 0);
if (lineCompare !== 0) return lineCompare;
return (l.location?.column ?? 0) - (r.location?.column ?? 0);
});
const problems = results.flat().sort((l, r) => {
const nameCompare = l.file.localeCompare(r.file);
if (nameCompare !== 0) return nameCompare;
const lineCompare = (l.location?.line ?? 0) - (r.location?.line ?? 0);
if (lineCompare !== 0) return lineCompare;
return (l.location?.column ?? 0) - (r.location?.column ?? 0);
});

console.log();
if (problems.length === 0) {
Expand Down Expand Up @@ -86,8 +83,7 @@ export async function check(options: CheckOptions = {}) {

// Exit based on number of commands that exited with code >= 1
console.log();
const failedChecks = results.filter((res) => res.type === "error").length;
process.exit(failedChecks);
process.exit(problems.length);
}

async function findInstalledTools(root: string | undefined): Promise<Tool[]> {
Expand Down
43 changes: 20 additions & 23 deletions src/tools/eslint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,27 @@ describe("ESLint", () => {
const stderr = "";
const code = 1;

expect(parseOuptut({ code, stdout, stderr })).toEqual({
type: "error",
problems: [
{
file: "/path/to/check/demo/test.ts",
message: "'test' is assigned a value but never used.",
kind: "warning",
location: {
line: 1,
column: 7,
},
rule: "@typescript-eslint/no-unused-vars",
expect(parseOuptut({ code, stdout, stderr })).toEqual([
{
file: "/path/to/check/demo/test.ts",
message: "'test' is assigned a value but never used.",
kind: "warning",
location: {
line: 1,
column: 7,
},
{
file: "/path/to/check/demo/test.ts",
message: "'variable' is assigned a value but never used.",
kind: "error",
location: {
line: 5,
column: 7,
},
rule: "@typescript-eslint/no-unused-vars",
rule: "@typescript-eslint/no-unused-vars",
},
{
file: "/path/to/check/demo/test.ts",
message: "'variable' is assigned a value but never used.",
kind: "error",
location: {
line: 5,
column: 7,
},
],
});
rule: "@typescript-eslint/no-unused-vars",
},
]);
});
});
13 changes: 2 additions & 11 deletions src/tools/eslint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,8 @@ export const eslint: Tool = {
fix: (root) => execAndParse(root, bin, [...args, "--fix"], parseOuptut),
};

export const parseOuptut: OutputParser = ({ code, stdout, stderr }) => {
if (code === 0) return { type: "success" };

const problems = `${stdout}\n${stderr}`
export const parseOuptut: OutputParser = ({ stdout, stderr }) => {
return `${stdout}\n${stderr}`
.split(/\r?\n/)
.reduce<Problem[]>((acc, line) => {
const match =
Expand All @@ -43,11 +41,4 @@ export const parseOuptut: OutputParser = ({ code, stdout, stderr }) => {
}
return acc;
}, []);

return {
type: problems.some((problem) => problem.kind === "error")
? "error"
: "warning",
problems,
};
};
39 changes: 22 additions & 17 deletions src/tools/prettier.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,30 @@ import { parseOuptut } from "./prettier";
describe("Prettier", () => {
it("should properly parse output", async () => {
const stdout = `target/.rustc_info.json
test.ts
`;
test.ts
`;
const stderr = "";
const code = 1;

expect(parseOuptut({ code, stdout, stderr })).toEqual({
type: "warning",
problems: [
{
file: "target/.rustc_info.json",
message: "Not formatted.",
kind: "warning",
},
{
file: "test.ts",
message: "Not formatted.",
kind: "warning",
},
],
});
expect(parseOuptut({ code, stdout, stderr })).toEqual([
{
file: "target/.rustc_info.json",
message: "Not formatted.",
kind: "warning",
},
{
file: "test.ts",
message: "Not formatted.",
kind: "warning",
},
]);
});

it("return no problems when there isn't any output", async () => {
const stdout = "";
const stderr = "";
const code = 1;

expect(parseOuptut({ code, stdout, stderr })).toEqual([]);
});
});
13 changes: 4 additions & 9 deletions src/tools/prettier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { execAndParse, isBinInstalled } from "../utils";

const bin = "node_modules/.bin/prettier";
const checkArgs = [".", "--list-different"];
const fixArgs = [".", "--fix"];
const fixArgs = [".", "-w"];

export const prettier: Tool = {
name: "Prettier",
Expand All @@ -12,21 +12,16 @@ export const prettier: Tool = {
fix: (root) => execAndParse(root, bin, fixArgs, parseOuptut),
};

export const parseOuptut: OutputParser = ({ code, stdout }) => {
if (code === 0) return { type: "success" };

const problems = stdout
export const parseOuptut: OutputParser = ({ stdout }) => {
return stdout
.trim()
.split(/\r?\n/)
.filter((line) => !!line)
.map(
(line): Problem => ({
file: line.trim(),
kind: "warning",
message: "Not formatted.",
}),
);
return {
type: "warning",
problems,
};
};
40 changes: 18 additions & 22 deletions src/tools/publint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,23 @@ Errors:
const stderr = "";
const code = 1;

expect(parseOuptut({ code, stdout, stderr })).toEqual({
type: "error",
problems: [
{
file: "package.json",
message: "Consider being better lolz.",
kind: "warning",
},
{
file: "package.json",
message:
'pkg.exports["."].import types is not exported. Consider adding pkg.exports["."].import.types: "./dist/index.d.ts" to be compatible with TypeScript\'s "moduleResolution": "bundler" compiler option.',
kind: "warning",
},
{
file: "package.json",
message:
"pkg.module is ./dist/index.cjs but the file does not exist.",
kind: "error",
},
],
});
expect(parseOuptut({ code, stdout, stderr })).toEqual([
{
file: "package.json",
message: "Consider being better lolz.",
kind: "warning",
},
{
file: "package.json",
message:
'pkg.exports["."].import types is not exported. Consider adding pkg.exports["."].import.types: "./dist/index.d.ts" to be compatible with TypeScript\'s "moduleResolution": "bundler" compiler option.',
kind: "warning",
},
{
file: "package.json",
message: "pkg.module is ./dist/index.cjs but the file does not exist.",
kind: "error",
},
]);
});
});
11 changes: 2 additions & 9 deletions src/tools/publint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@ export const publint: Tool = {
check: (root) => execAndParse(root, bin, args, parseOuptut),
};

export const parseOuptut: OutputParser = ({ code, stdout, stderr }) => {
if (code === 0) return { type: "success" };

export const parseOuptut: OutputParser = ({ stdout }) => {
let kind: ProblemKind = "warning";
const problems = stdout.split(/\r?\n/).reduce<Problem[]>((acc, line) => {
return stdout.split(/\r?\n/).reduce<Problem[]>((acc, line) => {
if (line.includes("Errors:")) {
kind = "error";
return acc;
Expand All @@ -30,9 +28,4 @@ export const parseOuptut: OutputParser = ({ code, stdout, stderr }) => {

return acc;
}, []);

return {
type: kind,
problems,
};
};
45 changes: 21 additions & 24 deletions src/tools/typescript.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,28 @@ test.ts(5,24): error TS7006: Parameter 'a' implicitly has an 'any' type.
const stderr = "";
const code = 1;

expect(parseOuptut({ code, stdout, stderr })).toEqual({
type: "error",
problems: [
{
file: "test.ts",
message:
"A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.",
kind: "error",
location: {
line: 1,
column: 19,
},
rule: "TS2355",
expect(parseOuptut({ code, stdout, stderr })).toEqual([
{
file: "test.ts",
message:
"A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.",
kind: "error",
location: {
line: 1,
column: 19,
},
{
file: "test.ts",
message: "Parameter 'a' implicitly has an 'any' type.",
kind: "error",
location: {
line: 5,
column: 24,
},
rule: "TS7006",
rule: "TS2355",
},
{
file: "test.ts",
message: "Parameter 'a' implicitly has an 'any' type.",
kind: "error",
location: {
line: 5,
column: 24,
},
],
});
rule: "TS7006",
},
]);
});
});
10 changes: 2 additions & 8 deletions src/tools/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ export const typescript: Tool = {
check: (root) => execAndParse(root, bin, checkArgs, parseOuptut),
};

export const parseOuptut: OutputParser = ({ code, stdout }) => {
if (code === 0) return { type: "success" };

const problems = stdout.split(/\r?\n/).reduce<Problem[]>((acc, line) => {
export const parseOuptut: OutputParser = ({ stdout }) => {
return stdout.split(/\r?\n/).reduce<Problem[]>((acc, line) => {
const match = /^(\S+?)\(([0-9]+),([0-9]+)\): \w+? (TS[0-9]+): (.*)$/.exec(
line,
);
Expand All @@ -31,8 +29,4 @@ export const parseOuptut: OutputParser = ({ code, stdout }) => {
}
return acc;
}, []);
return {
type: "error",
problems,
};
};
Loading

0 comments on commit 5cfeb9d

Please sign in to comment.