From e0d2bdb33215b2269e66c66cb2059757b07d9889 Mon Sep 17 00:00:00 2001 From: sushichan044 Date: Thu, 6 Mar 2025 23:00:58 +0900 Subject: [PATCH 1/5] chore: add --list flag --- src/cli/context.ts | 15 +++++++++++++-- src/cli/index.ts | 8 ++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/cli/context.ts b/src/cli/context.ts index 65ce96d..1052e6a 100644 --- a/src/cli/context.ts +++ b/src/cli/context.ts @@ -7,7 +7,7 @@ import type { OperationLimit } from "../operation/types"; import { safeTryNumber } from "../utils/number"; type CLIContext = { - mode: "correct" | "generate"; + mode: "correct" | "generate" | "list"; operation: CLIOperation; /** * Path to the todo file relative to the current working directory @@ -23,6 +23,7 @@ type Input = { mode: { correct: boolean; + list: boolean; }; operation: CLIOperationInput; todoFileAbsolutePath: string; @@ -31,8 +32,18 @@ type Input = { export const resolveCLIContext = (input: Input): CLIContext => { const todoFilePath = relative(input.cwd, input.todoFileAbsolutePath); + const mode = (() => { + if (input.mode.correct) { + return "correct"; + } + if (input.mode.list) { + return "list"; + } + return "generate"; + })(); + return { - mode: input.mode.correct ? "correct" : "generate", + mode, operation: resolveCLIOperation(input.operation), todoFilePath: todoFilePath, }; diff --git a/src/cli/index.ts b/src/cli/index.ts index c54c8e4..616e9b7 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -47,6 +47,13 @@ const cli = defineCommand({ required: false, type: "boolean", }, + "list": { + alias: "l", + default: false, + description: "See violations in the todo file with table format", + required: false, + type: "boolean", + }, // operation options "allow-partial-selection": { @@ -115,6 +122,7 @@ const cli = defineCommand({ cwd: cliCwd, mode: { correct: args.correct, + list: args.list, }, operation: { allowPartialSelection: args["allow-partial-selection"], From fc78ce0ecff77a64fbb6f4d4cabf992bc999f405 Mon Sep 17 00:00:00 2001 From: sushichan044 Date: Thu, 6 Mar 2025 23:01:11 +0900 Subject: [PATCH 2/5] chore(deps): add table --- package.json | 1 + pnpm-lock.yaml | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/package.json b/package.json index f5f3c72..244ae06 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "klona": "2.0.6", "magicast": "0.3.5", "pathe": "2.0.3", + "table": "6.9.0", "valibot": "1.0.0-rc.3" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ab72e01..fd17453 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: pathe: specifier: 2.0.3 version: 2.0.3 + table: + specifier: 6.9.0 + version: 6.9.0 valibot: specifier: 1.0.0-rc.3 version: 1.0.0-rc.3(typescript@5.7.3) @@ -1108,6 +1111,9 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} @@ -1198,6 +1204,10 @@ packages: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + astro-eslint-parser@1.2.1: resolution: {integrity: sha512-3oqANMjrvJ+IE5pwlUWsH/4UztmYf/GTL0HPUkWnYBNAHiGVGrOh2EbegxS5niAwlO0w9dRYk0CkCPlJcu8c3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1962,6 +1972,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.0.6: + resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + fastq@1.19.0: resolution: {integrity: sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==} @@ -2545,6 +2558,9 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -2628,6 +2644,9 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} @@ -3434,6 +3453,10 @@ packages: engines: {node: ^20.9.0 || >=22.0.0} hasBin: true + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -3587,6 +3610,10 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -3738,6 +3765,10 @@ packages: resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} engines: {node: ^14.18.0 || >=16.0.0} + table@6.9.0: + resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} + engines: {node: '>=10.0.0'} + tailwindcss@3.4.17: resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} engines: {node: '>=14.0.0'} @@ -5176,6 +5207,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.6 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-align@3.0.1: dependencies: string-width: 4.2.3 @@ -5276,6 +5314,8 @@ snapshots: dependencies: tslib: 2.8.1 + astral-regex@2.0.0: {} + astro-eslint-parser@1.2.1: dependencies: '@astrojs/compiler': 2.10.3 @@ -6306,6 +6346,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.0.6: {} + fastq@1.19.0: dependencies: reusify: 1.0.4 @@ -6889,6 +6931,8 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} jsonc-parser@3.3.1: {} @@ -6955,6 +6999,8 @@ snapshots: lodash.merge@4.6.2: {} + lodash.truncate@4.4.2: {} + lodash.uniq@4.5.0: {} lodash.uniqby@4.7.0: {} @@ -7789,6 +7835,8 @@ snapshots: - supports-color - typescript + require-from-string@2.0.2: {} + resolve-from@4.0.0: {} resolve@1.22.10: @@ -7966,6 +8014,12 @@ snapshots: slash@5.1.0: {} + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + smart-buffer@4.2.0: {} socks-proxy-agent@8.0.5: @@ -8141,6 +8195,14 @@ snapshots: '@pkgr/core': 0.1.1 tslib: 2.8.1 + table@6.9.0: + dependencies: + ajv: 8.17.1 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + tailwindcss@3.4.17: dependencies: '@alloc/quick-lru': 5.2.0 From 5e7093d98a6ac138ba9996cf5a8d61df9f04b17a Mon Sep 17 00:00:00 2001 From: sushichan044 Date: Thu, 6 Mar 2025 23:01:25 +0900 Subject: [PATCH 3/5] feat: implement list view --- src/cli/action/list.ts | 66 ++++++++++++++++++++++++++++++++++++++++++ src/cli/index.ts | 6 ++++ 2 files changed, 72 insertions(+) create mode 100644 src/cli/action/list.ts diff --git a/src/cli/action/list.ts b/src/cli/action/list.ts new file mode 100644 index 0000000..3180494 --- /dev/null +++ b/src/cli/action/list.ts @@ -0,0 +1,66 @@ +import { table } from "table"; + +import type { TodoModuleLike } from "../../todofile/types"; +import type { TodoModuleV2 } from "../../todofile/v2"; + +import { TodoModuleV2Handler } from "../../todofile/v2"; +import { defineAction } from "./index"; + +export const listAction = defineAction(async ({ core, logger }) => { + const todoModule = getTodoModuleV2(await core.readTodoModule()); + + if (todoModule === false) { + throw new Error("Unsupported todo file version"); + } + + const todoTable = generateTodoTable(todoModule); + + if (todoTable.length === 0) { + logger.success("No rules found in the todo file! Good job!"); + return; + } + + const tableData = [ + ["Rule", "Files", "Violations", "AutoFixable"], + ...todoTable, + ]; + + logger.log.raw(table(tableData)); + + return; +}); + +const getTodoModuleV2 = (module: TodoModuleLike): false | TodoModuleV2 => { + if (TodoModuleV2Handler.isVersion(module)) { + return module; + } + + return false; +}; + +type TableFormat = [ + ruleId: string, + violatedFiles: number, + totalViolations: number, + autoFixableEmoji: string, +]; + +const generateTodoTable = (todoModule: TodoModuleV2): TableFormat[] => { + const tableData: TableFormat[] = []; + + for (const [ruleId, { autoFix, violations }] of Object.entries( + todoModule.todo, + )) { + const violatedFiles = Object.keys(violations).length; + const totalViolations = Object.values(violations).reduce( + (sum, count) => sum + count, + 0, + ); + + const autoFixEmoji = autoFix ? "✓" : ""; + + tableData.push([ruleId, violatedFiles, totalViolations, autoFixEmoji]); + } + + return tableData; +}; diff --git a/src/cli/index.ts b/src/cli/index.ts index 616e9b7..df1516f 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -9,6 +9,7 @@ import { ESLintTodoCore } from "../index"; import { runAction } from "./action"; import { deleteRuleAction } from "./action/delete-rule"; import { genAction } from "./action/gen"; +import { listAction } from "./action/list"; import { selectRulesToFixAction } from "./action/select-rule"; import { updateAction } from "./action/update"; import { resolveCLIContext } from "./context"; @@ -135,6 +136,11 @@ const cli = defineCommand({ await runAction(updateAction, { consola, options }); + if (context.mode === "list") { + await runAction(listAction, { consola, options }); + return; + } + if (context.mode === "generate") { await runAction(genAction, { consola, options }); consola.success(`ESLint todo file generated at ${context.todoFilePath}!`); From 2aec0cd8bf6690bd71af31a66c81ddb2d76eb7e2 Mon Sep 17 00:00:00 2001 From: sushichan044 Date: Thu, 6 Mar 2025 23:09:44 +0900 Subject: [PATCH 4/5] chore: rename AutoFixable to AutoFix --- src/cli/action/list.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/cli/action/list.ts b/src/cli/action/list.ts index 3180494..d16977e 100644 --- a/src/cli/action/list.ts +++ b/src/cli/action/list.ts @@ -20,10 +20,7 @@ export const listAction = defineAction(async ({ core, logger }) => { return; } - const tableData = [ - ["Rule", "Files", "Violations", "AutoFixable"], - ...todoTable, - ]; + const tableData = [["Rule", "Files", "Violations", "AutoFix"], ...todoTable]; logger.log.raw(table(tableData)); From 8bc8fc5be6bc068e89d8850634a9dc61a682c9f0 Mon Sep 17 00:00:00 2001 From: sushichan044 Date: Thu, 6 Mar 2025 23:48:18 +0900 Subject: [PATCH 5/5] refactor: use json like object internally --- src/cli/action/list.ts | 54 +++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/cli/action/list.ts b/src/cli/action/list.ts index d16977e..058a9ad 100644 --- a/src/cli/action/list.ts +++ b/src/cli/action/list.ts @@ -7,13 +7,21 @@ import { TodoModuleV2Handler } from "../../todofile/v2"; import { defineAction } from "./index"; export const listAction = defineAction(async ({ core, logger }) => { + // v1 module should be upgraded to v2 at the entry point of CLI const todoModule = getTodoModuleV2(await core.readTodoModule()); - if (todoModule === false) { + if (!todoModule) { throw new Error("Unsupported todo file version"); } - const todoTable = generateTodoTable(todoModule); + const todoTable = generateTodoInfo(todoModule).map((row) => { + return [ + row.rule, + row.violations.files, + row.violations.total, + row.autoFix ? "✔" : "", + ]; + }); if (todoTable.length === 0) { logger.success("No rules found in the todo file! Good job!"); @@ -23,29 +31,26 @@ export const listAction = defineAction(async ({ core, logger }) => { const tableData = [["Rule", "Files", "Violations", "AutoFix"], ...todoTable]; logger.log.raw(table(tableData)); - return; }); -const getTodoModuleV2 = (module: TodoModuleLike): false | TodoModuleV2 => { - if (TodoModuleV2Handler.isVersion(module)) { - return module; - } - - return false; +const getTodoModuleV2 = (module: TodoModuleLike): TodoModuleV2 | null => { + return TodoModuleV2Handler.isVersion(module) ? module : null; }; -type TableFormat = [ - ruleId: string, - violatedFiles: number, - totalViolations: number, - autoFixableEmoji: string, -]; +type TodoInfo = { + autoFix: boolean; + rule: string; + violations: { + files: number; + total: number; + }; +}; -const generateTodoTable = (todoModule: TodoModuleV2): TableFormat[] => { - const tableData: TableFormat[] = []; +const generateTodoInfo = (todoModule: TodoModuleV2): TodoInfo[] => { + const todoInfo: TodoInfo[] = []; - for (const [ruleId, { autoFix, violations }] of Object.entries( + for (const [rule, { autoFix, violations }] of Object.entries( todoModule.todo, )) { const violatedFiles = Object.keys(violations).length; @@ -54,10 +59,15 @@ const generateTodoTable = (todoModule: TodoModuleV2): TableFormat[] => { 0, ); - const autoFixEmoji = autoFix ? "✓" : ""; - - tableData.push([ruleId, violatedFiles, totalViolations, autoFixEmoji]); + todoInfo.push({ + autoFix, + rule, + violations: { + files: violatedFiles, + total: totalViolations, + }, + }); } - return tableData; + return todoInfo; };