@aklinker1/check 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,9 @@
1
1
  # Check
2
2
 
3
- An opinionated CLI tool to run all your checks all at once. The command will only exit with code 0 when no warnings exist.
3
+ An opinionated CLI tool to run all your checks all at once. The command will only exit with code 0 when no problems exist.
4
4
 
5
5
  https://github.com/aklinker1/check/assets/10101283/c8089e5c-e25f-4f59-8897-d2a6f97a3139
6
6
 
7
- > [!WARNING]
8
- > I have not actually published this to NPM yet.
9
-
10
7
  ```sh
11
8
  pnpm i @aklinker1/check
12
9
  pnpm check
package/dist/index.mjs CHANGED
@@ -3,8 +3,9 @@ import { p } from "@antfu/utils";
3
3
  import { bold, cyan, debug, dim, humanMs, isDebug, red, yellow } from "./utils.mjs";
4
4
  import { createTaskList } from "./tasklist/index.mjs";
5
5
  import { relative, resolve, sep } from "node:path";
6
+ import { isCI } from "ci-info";
6
7
  export async function check(options = {}) {
7
- const { debug: debug2, fix, root } = options;
8
+ const { debug: debug2, fix = !isCI, root } = options;
8
9
  if (debug2) {
9
10
  process.env.DEBUG = "true";
10
11
  }
@@ -20,24 +21,23 @@ export async function check(options = {}) {
20
21
  async ({ input: tool, fail, succeed, warn }) => {
21
22
  const startTime = performance.now();
22
23
  const fn = fix ? tool.fix ?? tool.check : tool.check;
23
- const output = await fn(root);
24
- if ("problems" in output) {
25
- output.problems.forEach((problem) => {
26
- problem.file = resolve(root ?? process.cwd(), problem.file);
27
- });
28
- }
24
+ const problems2 = await fn(root);
25
+ problems2.forEach((problem) => {
26
+ problem.file = resolve(root ?? process.cwd(), problem.file);
27
+ });
29
28
  const duration = humanMs(performance.now() - startTime);
30
29
  const title = `${tool.name} ${dim(`(${duration})`)}`;
31
- if (output.type === "error")
30
+ const errorCount = problems2.filter((p2) => p2.kind === "error").length;
31
+ if (errorCount > 0)
32
32
  fail(title);
33
- else if (output.type === "warning")
33
+ else if (problems2.length > 0)
34
34
  warn(title);
35
35
  else
36
36
  succeed(title);
37
- return output;
37
+ return problems2;
38
38
  }
39
39
  );
40
- const problems = results.flatMap((result) => result.type === "success" ? [] : result.problems).sort((l, r) => {
40
+ const problems = results.flat().sort((l, r) => {
41
41
  const nameCompare = l.file.localeCompare(r.file);
42
42
  if (nameCompare !== 0)
43
43
  return nameCompare;
@@ -72,8 +72,7 @@ export async function check(options = {}) {
72
72
  console.log(`${cyan(file.padEnd(maxLength, " "))} ${count}`);
73
73
  });
74
74
  console.log();
75
- const failedChecks = results.filter((res) => res.type === "error").length;
76
- process.exit(failedChecks);
75
+ process.exit(problems.length);
77
76
  }
78
77
  async function findInstalledTools(root) {
79
78
  const status = await p(ALL_TOOLS).map(async (tool) => ({
@@ -8,21 +8,23 @@ export async function createTaskList(inputs, run) {
8
8
  const isTty = process.stderr.isTTY;
9
9
  let tick = 0;
10
10
  const render = (opts) => {
11
- if (!opts?.firstRender) {
11
+ if (isTty && !opts?.firstRender) {
12
12
  readline.moveCursor(process.stderr, 0, -1 * states.length);
13
13
  }
14
- states.forEach(({ state, title }) => {
15
- readline.clearLine(process.stderr, 0);
16
- const frames = SPINNER_FRAMES[state];
17
- process.stderr.write(`${frames[tick % frames.length]} ${title}
14
+ if (isTty || opts?.firstRender || opts?.lastRender) {
15
+ states.forEach(({ state, title }) => {
16
+ readline.clearLine(process.stderr, 0);
17
+ const frames = SPINNER_FRAMES[state];
18
+ process.stderr.write(`${frames[tick % frames.length]} ${title}
18
19
  `);
19
- });
20
+ });
21
+ }
20
22
  tick++;
21
23
  };
22
24
  render({ firstRender: true });
23
25
  const renderInterval = setInterval(render, SPINNER_INTERVAL_MS);
24
26
  try {
25
- const result = Promise.all(
27
+ const result = await Promise.all(
26
28
  inputs.map(async (input, i) => {
27
29
  const succeed = (title) => {
28
30
  if (title != null)
@@ -46,22 +48,22 @@ export async function createTaskList(inputs, run) {
46
48
  states[i].state = "in-progress";
47
49
  render();
48
50
  const res = await run({ input, succeed, warn, fail });
49
- if (states[i].state === "in-progress") {
51
+ if (states[i].state === "in-progress")
50
52
  states[i].state = "success";
51
- render();
52
- }
53
+ render();
53
54
  return res;
54
55
  } catch (err) {
55
56
  if (err instanceof Error)
56
57
  fail(err.message);
57
58
  else
58
59
  fail(String(err));
60
+ render();
59
61
  throw err;
60
62
  }
61
63
  })
62
64
  );
63
65
  render({ lastRender: true });
64
- return await result;
66
+ return result;
65
67
  } finally {
66
68
  clearInterval(renderInterval);
67
69
  }
@@ -15,10 +15,8 @@ export const eslint = {
15
15
  check: (root) => execAndParse(root, bin, args, parseOuptut),
16
16
  fix: (root) => execAndParse(root, bin, [...args, "--fix"], parseOuptut)
17
17
  };
18
- export const parseOuptut = ({ code, stdout, stderr }) => {
19
- if (code === 0)
20
- return { type: "success" };
21
- const problems = `${stdout}
18
+ export const parseOuptut = ({ stdout, stderr }) => {
19
+ return `${stdout}
22
20
  ${stderr}`.split(/\r?\n/).reduce((acc, line) => {
23
21
  const match = /^(.*?): line ([0-9]+), col ([0-9]+), (\S+) - (.*?) \((\S*?)\)$/.exec(
24
22
  line
@@ -37,8 +35,4 @@ ${stderr}`.split(/\r?\n/).reduce((acc, line) => {
37
35
  }
38
36
  return acc;
39
37
  }, []);
40
- return {
41
- type: problems.some((problem) => problem.kind === "error") ? "error" : "warning",
42
- problems
43
- };
44
38
  };
@@ -9,30 +9,27 @@ describe("ESLint", () => {
9
9
  `;
10
10
  const stderr = "";
11
11
  const code = 1;
12
- expect(parseOuptut({ code, stdout, stderr })).toEqual({
13
- type: "error",
14
- problems: [
15
- {
16
- file: "/path/to/check/demo/test.ts",
17
- message: "'test' is assigned a value but never used.",
18
- kind: "warning",
19
- location: {
20
- line: 1,
21
- column: 7
22
- },
23
- rule: "@typescript-eslint/no-unused-vars"
12
+ expect(parseOuptut({ code, stdout, stderr })).toEqual([
13
+ {
14
+ file: "/path/to/check/demo/test.ts",
15
+ message: "'test' is assigned a value but never used.",
16
+ kind: "warning",
17
+ location: {
18
+ line: 1,
19
+ column: 7
24
20
  },
25
- {
26
- file: "/path/to/check/demo/test.ts",
27
- message: "'variable' is assigned a value but never used.",
28
- kind: "error",
29
- location: {
30
- line: 5,
31
- column: 7
32
- },
33
- rule: "@typescript-eslint/no-unused-vars"
34
- }
35
- ]
36
- });
21
+ rule: "@typescript-eslint/no-unused-vars"
22
+ },
23
+ {
24
+ file: "/path/to/check/demo/test.ts",
25
+ message: "'variable' is assigned a value but never used.",
26
+ kind: "error",
27
+ location: {
28
+ line: 5,
29
+ column: 7
30
+ },
31
+ rule: "@typescript-eslint/no-unused-vars"
32
+ }
33
+ ]);
37
34
  });
38
35
  });
@@ -2,4 +2,4 @@ import { eslint } from "./eslint.mjs";
2
2
  import { prettier } from "./prettier.mjs";
3
3
  import { typescript } from "./typescript.mjs";
4
4
  import { publint } from "./publint.mjs";
5
- export const ALL_TOOLS = [prettier, typescript, eslint, publint];
5
+ export const ALL_TOOLS = [publint, prettier, typescript, eslint];
@@ -1,25 +1,19 @@
1
1
  import { execAndParse, isBinInstalled } from "../utils.mjs";
2
2
  const bin = "node_modules/.bin/prettier";
3
3
  const checkArgs = [".", "--list-different"];
4
- const fixArgs = [".", "--fix"];
4
+ const fixArgs = [".", "-w"];
5
5
  export const prettier = {
6
6
  name: "Prettier",
7
7
  isInstalled: (root) => isBinInstalled(bin, root),
8
8
  check: (root) => execAndParse(root, bin, checkArgs, parseOuptut),
9
9
  fix: (root) => execAndParse(root, bin, fixArgs, parseOuptut)
10
10
  };
11
- export const parseOuptut = ({ code, stdout }) => {
12
- if (code === 0)
13
- return { type: "success" };
14
- const problems = stdout.trim().split(/\r?\n/).map(
11
+ export const parseOuptut = ({ stdout }) => {
12
+ return stdout.trim().split(/\r?\n/).filter((line) => !!line).map(
15
13
  (line) => ({
16
14
  file: line.trim(),
17
15
  kind: "warning",
18
16
  message: "Not formatted."
19
17
  })
20
18
  );
21
- return {
22
- type: "warning",
23
- problems
24
- };
25
19
  };
@@ -3,24 +3,27 @@ import { parseOuptut } from "./prettier.mjs";
3
3
  describe("Prettier", () => {
4
4
  it("should properly parse output", async () => {
5
5
  const stdout = `target/.rustc_info.json
6
- test.ts
7
- `;
6
+ test.ts
7
+ `;
8
8
  const stderr = "";
9
9
  const code = 1;
10
- expect(parseOuptut({ code, stdout, stderr })).toEqual({
11
- type: "warning",
12
- problems: [
13
- {
14
- file: "target/.rustc_info.json",
15
- message: "Not formatted.",
16
- kind: "warning"
17
- },
18
- {
19
- file: "test.ts",
20
- message: "Not formatted.",
21
- kind: "warning"
22
- }
23
- ]
24
- });
10
+ expect(parseOuptut({ code, stdout, stderr })).toEqual([
11
+ {
12
+ file: "target/.rustc_info.json",
13
+ message: "Not formatted.",
14
+ kind: "warning"
15
+ },
16
+ {
17
+ file: "test.ts",
18
+ message: "Not formatted.",
19
+ kind: "warning"
20
+ }
21
+ ]);
22
+ });
23
+ it("return no problems when there isn't any output", async () => {
24
+ const stdout = "";
25
+ const stderr = "";
26
+ const code = 1;
27
+ expect(parseOuptut({ code, stdout, stderr })).toEqual([]);
25
28
  });
26
29
  });
@@ -6,11 +6,9 @@ export const publint = {
6
6
  isInstalled: (root) => isBinInstalled(bin, root),
7
7
  check: (root) => execAndParse(root, bin, args, parseOuptut)
8
8
  };
9
- export const parseOuptut = ({ code, stdout, stderr }) => {
10
- if (code === 0)
11
- return { type: "success" };
9
+ export const parseOuptut = ({ stdout }) => {
12
10
  let kind = "warning";
13
- const problems = stdout.split(/\r?\n/).reduce((acc, line) => {
11
+ return stdout.split(/\r?\n/).reduce((acc, line) => {
14
12
  if (line.includes("Errors:")) {
15
13
  kind = "error";
16
14
  return acc;
@@ -25,8 +23,4 @@ export const parseOuptut = ({ code, stdout, stderr }) => {
25
23
  });
26
24
  return acc;
27
25
  }, []);
28
- return {
29
- type: kind,
30
- problems
31
- };
32
26
  };
@@ -12,25 +12,22 @@ Errors:
12
12
  `;
13
13
  const stderr = "";
14
14
  const code = 1;
15
- expect(parseOuptut({ code, stdout, stderr })).toEqual({
16
- type: "error",
17
- problems: [
18
- {
19
- file: "package.json",
20
- message: "Consider being better lolz.",
21
- kind: "warning"
22
- },
23
- {
24
- file: "package.json",
25
- 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.`,
26
- kind: "warning"
27
- },
28
- {
29
- file: "package.json",
30
- message: "pkg.module is ./dist/index.cjs but the file does not exist.",
31
- kind: "error"
32
- }
33
- ]
34
- });
15
+ expect(parseOuptut({ code, stdout, stderr })).toEqual([
16
+ {
17
+ file: "package.json",
18
+ message: "Consider being better lolz.",
19
+ kind: "warning"
20
+ },
21
+ {
22
+ file: "package.json",
23
+ 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.`,
24
+ kind: "warning"
25
+ },
26
+ {
27
+ file: "package.json",
28
+ message: "pkg.module is ./dist/index.cjs but the file does not exist.",
29
+ kind: "error"
30
+ }
31
+ ]);
35
32
  });
36
33
  });
@@ -6,10 +6,8 @@ export const typescript = {
6
6
  isInstalled: (root) => isBinInstalled(bin, root),
7
7
  check: (root) => execAndParse(root, bin, checkArgs, parseOuptut)
8
8
  };
9
- export const parseOuptut = ({ code, stdout }) => {
10
- if (code === 0)
11
- return { type: "success" };
12
- const problems = stdout.split(/\r?\n/).reduce((acc, line) => {
9
+ export const parseOuptut = ({ stdout }) => {
10
+ return stdout.split(/\r?\n/).reduce((acc, line) => {
13
11
  const match = /^(\S+?)\(([0-9]+),([0-9]+)\): \w+? (TS[0-9]+): (.*)$/.exec(
14
12
  line
15
13
  );
@@ -27,8 +25,4 @@ export const parseOuptut = ({ code, stdout }) => {
27
25
  }
28
26
  return acc;
29
27
  }, []);
30
- return {
31
- type: "error",
32
- problems
33
- };
34
28
  };
@@ -7,30 +7,27 @@ test.ts(5,24): error TS7006: Parameter 'a' implicitly has an 'any' type.
7
7
  `;
8
8
  const stderr = "";
9
9
  const code = 1;
10
- expect(parseOuptut({ code, stdout, stderr })).toEqual({
11
- type: "error",
12
- problems: [
13
- {
14
- file: "test.ts",
15
- message: "A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.",
16
- kind: "error",
17
- location: {
18
- line: 1,
19
- column: 19
20
- },
21
- rule: "TS2355"
10
+ expect(parseOuptut({ code, stdout, stderr })).toEqual([
11
+ {
12
+ file: "test.ts",
13
+ message: "A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.",
14
+ kind: "error",
15
+ location: {
16
+ line: 1,
17
+ column: 19
22
18
  },
23
- {
24
- file: "test.ts",
25
- message: "Parameter 'a' implicitly has an 'any' type.",
26
- kind: "error",
27
- location: {
28
- line: 5,
29
- column: 24
30
- },
31
- rule: "TS7006"
32
- }
33
- ]
34
- });
19
+ rule: "TS2355"
20
+ },
21
+ {
22
+ file: "test.ts",
23
+ message: "Parameter 'a' implicitly has an 'any' type.",
24
+ kind: "error",
25
+ location: {
26
+ line: 5,
27
+ column: 24
28
+ },
29
+ rule: "TS7006"
30
+ }
31
+ ]);
35
32
  });
36
33
  });
package/dist/types.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export interface CheckOptions {
2
2
  /**
3
3
  * Set to true to fix problems that can be automatically fixed.
4
+ *
5
+ * Defaults to `true` outside CI, and `false` inside CI.
4
6
  */
5
7
  fix?: boolean;
6
8
  /**
@@ -24,23 +26,11 @@ export interface Tool {
24
26
  /**
25
27
  * Run the tool, only checking for problems.
26
28
  */
27
- check: (root: string | undefined) => Promise<Output>;
29
+ check: (root: string | undefined) => Promise<Problem[]>;
28
30
  /**
29
31
  * Run the tool, but fix problems if possible. If the tool doesn't support fixing problems, `check` will be called instead.
30
32
  */
31
- fix?: (root: string | undefined) => Promise<Output>;
32
- }
33
- export type Output = OutputSuccess | OutputWarning | OutputError;
34
- export interface OutputSuccess {
35
- type: "success";
36
- }
37
- export interface OutputWarning {
38
- type: "warning";
39
- problems: Problem[];
40
- }
41
- export interface OutputError {
42
- type: "error";
43
- problems: Problem[];
33
+ fix?: (root: string | undefined) => Promise<Problem[]>;
44
34
  }
45
35
  export interface Problem {
46
36
  location?: CodeLocation;
@@ -58,4 +48,4 @@ export type OutputParser = (data: {
58
48
  code: number;
59
49
  stdout: string;
60
50
  stderr: string;
61
- }) => Output;
51
+ }) => Problem[];
package/dist/utils.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { OutputParser, Output } from "./types";
1
+ import type { OutputParser, Problem } from "./types";
2
2
  export declare function isBinInstalled(bin: string, root?: string): Promise<boolean>;
3
- export declare function execAndParse(root: string | undefined, bin: string, args: string[], parser: OutputParser): Promise<Output>;
3
+ export declare function execAndParse(root: string | undefined, bin: string, args: string[], parser: OutputParser): Promise<Problem[]>;
4
4
  export declare function isDebug(): boolean;
5
5
  export declare const bold: (str: string) => string;
6
6
  export declare const dim: (str: string) => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aklinker1/check",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -10,18 +10,16 @@
10
10
  },
11
11
  "module": "./dist/index.mjs",
12
12
  "types": "./dist/index.d.ts",
13
- "files": ["dist"],
13
+ "files": [
14
+ "dist"
15
+ ],
14
16
  "bin": {
15
17
  "check": "bin/check.mjs"
16
18
  },
17
- "scripts": {
18
- "build": "bunx unbuild",
19
- "check": "bun src/cli.ts",
20
- "prepublish": "bun build"
21
- },
22
19
  "dependencies": {
23
20
  "@antfu/utils": "^0.7.7",
24
- "citty": "^0.1.6"
21
+ "citty": "^0.1.6",
22
+ "ci-info": "^4.0.0"
25
23
  },
26
24
  "devDependencies": {
27
25
  "@types/bun": "latest",
@@ -40,5 +38,10 @@
40
38
  }
41
39
  ],
42
40
  "declaration": true
41
+ },
42
+ "scripts": {
43
+ "build": "bunx --bun unbuild",
44
+ "check": "bun src/cli.ts",
45
+ "prepublish": "bun run build"
43
46
  }
44
- }
47
+ }