From 8e54fc2c19f8daac00d98bbfdb7c308b62c1e4ff Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 29 Apr 2026 14:59:28 +0100 Subject: [PATCH 1/4] fix: update `runKnip` --- src/commands/lint.ts | 46 +++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/commands/lint.ts b/src/commands/lint.ts index 44db5d0..9009766 100644 --- a/src/commands/lint.ts +++ b/src/commands/lint.ts @@ -2,6 +2,7 @@ import { fileURLToPath } from "node:url"; import { parse } from "@bomb.sh/args"; import { publint } from "publint"; import { x } from "tinyexec"; +import type { ReporterOptions } from "knip"; import type { CommandContext } from "../context.ts"; import { local } from "../utils.ts"; @@ -58,22 +59,30 @@ async function runPublint(): Promise { })); } -interface KnipIssue { - file: string; - dependencies: Array<{ name: string; line: number; col: number }>; - devDependencies: Array<{ name: string; line: number; col: number }>; - exports: Array<{ name: string; line: number; col: number }>; - types: Array<{ name: string; line: number; col: number }>; +type KnipCategory = keyof ReporterOptions["issues"]; + +interface KnipJsonIssueItem { + name: string; + line?: number; + col?: number; + pos?: number; +} + +type KnipJsonIssue = { file: string } & Record; + +interface KnipJsonReport { + issues: KnipJsonIssue[]; } async function runKnip(): Promise { const args = ["--no-progress", "--reporter", "json"]; const result = await x(local("knip"), args, { throwOnError: false }); if (!result.stdout.trim()) return []; - const json = JSON.parse(result.stdout); + + const json: KnipJsonReport = JSON.parse(result.stdout); const violations: Violation[] = []; - for (const issue of json.issues as KnipIssue[]) { + for (const issue of json.issues) { for (const dep of issue.dependencies) { violations.push({ tool: "knip", @@ -118,16 +127,17 @@ async function runKnip(): Promise { column: t.col, }); } - } - - for (const file of json.files as string[]) { - violations.push({ - tool: "knip", - level: "warning", - code: "unused-file", - message: `Unused file`, - file, - }); + for (const file of issue.files) { + violations.push({ + tool: "knip", + level: "warning", + code: "unused-file", + message: `Unused file`, + file: issue.file, + line: file.line, + column: file.col, + }); + } } return violations; From 42dc014fe832fc31e5c3a0583874bdc9247c898a Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 29 Apr 2026 16:26:45 +0100 Subject: [PATCH 2/4] add tests --- knip.jsonc | 10 +++ src/commands/__snapshots__/lint.test.ts.snap | 47 +++++++++++ src/commands/lint.test.ts | 82 ++++++++++++++++++++ src/commands/lint.ts | 4 +- 4 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 knip.jsonc create mode 100644 src/commands/__snapshots__/lint.test.ts.snap create mode 100644 src/commands/lint.test.ts diff --git a/knip.jsonc b/knip.jsonc new file mode 100644 index 0000000..fe3a134 --- /dev/null +++ b/knip.jsonc @@ -0,0 +1,10 @@ +{ + "$schema": "./node_modules/knip/schema-jsonc.json", + "entry": [ + "src/index.ts", + "src/test-utils/*.ts" + ], + "ignoreDependencies": [ + "@tanstack/intent" + ] +} diff --git a/src/commands/__snapshots__/lint.test.ts.snap b/src/commands/__snapshots__/lint.test.ts.snap new file mode 100644 index 0000000..2bd16b3 --- /dev/null +++ b/src/commands/__snapshots__/lint.test.ts.snap @@ -0,0 +1,47 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`lint command > runKnip > detects unused exports and unused files 1`] = ` +[ + { + "code": "unused-file", + "column": undefined, + "file": "src/unused-file.ts", + "level": "warning", + "line": undefined, + "message": "Unused file", + "tool": "knip", + }, + { + "code": "unused-export", + "column": 20, + "file": "src/used.ts", + "level": "warning", + "line": 3, + "message": "Unused export 'unusedExport'", + "tool": "knip", + }, +] +`; + +exports[`lint command > runOxlint > detects violations in bad code 1`] = ` +[ + { + "code": "eslint(no-unused-vars)", + "column": 5, + "file": "src/index.ts", + "level": "error", + "line": 1, + "message": "Variable 'x' is declared but never used. Unused variables should start with a '_'.", + "tool": "oxlint", + }, + { + "code": "eslint(no-var)", + "column": 1, + "file": "src/index.ts", + "level": "error", + "line": 1, + "message": "Unexpected var, use let or const instead.", + "tool": "oxlint", + }, +] +`; diff --git a/src/commands/lint.test.ts b/src/commands/lint.test.ts new file mode 100644 index 0000000..7b6b9f0 --- /dev/null +++ b/src/commands/lint.test.ts @@ -0,0 +1,82 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { createFixture } from "../test-utils/index.ts"; +import { runOxlint, runKnip } from "./lint.ts"; +import { fileURLToPath } from "node:url"; + +describe("lint command", () => { + let originalCwd: string; + let fixture: Awaited>; + + beforeEach(() => { + originalCwd = process.cwd(); + }); + + afterEach(async () => { + process.chdir(originalCwd); + if (fixture) await fixture.cleanup(); + }); + + describe("runOxlint", () => { + it("detects violations in bad code", async () => { + fixture = await createFixture({ + src: { + "index.ts": "var x = 1;", + }, + }); + process.chdir(fileURLToPath(fixture.root)); + + const violations = await runOxlint(["./src"]); + + expect(violations).not.toEqual([]); + expect(violations).toMatchSnapshot(); + }); + + it("returns empty array for clean code", async () => { + fixture = await createFixture({ + src: { + "index.ts": ` + export const x = 1; + `, + }, + }); + process.chdir(fileURLToPath(fixture.root)); + + const violations = await runOxlint(["./src"]); + + expect(violations).toEqual([]); + }); + }); + + describe("runKnip", () => { + it("detects unused exports and unused files", async () => { + fixture = await createFixture({ + "package.json": { + name: "test-pkg", + version: "1.0.0", + type: "module", + exports: "./src/index.ts", + }, + src: { + "index.ts": ` + import { used } from "./used"; + console.log(used); + export const value = 42; + `, + "used.ts": ` + export const used = "used"; + export const unusedExport = "unusedExport"; + `, + "unused-file.ts": ` + export const unused = "unused"; + `, + }, + }); + process.chdir(fileURLToPath(fixture.root)); + + const violations = await runKnip(); + + expect(violations).not.toEqual([]); + expect(violations).toMatchSnapshot(); + }); + }); +}); diff --git a/src/commands/lint.ts b/src/commands/lint.ts index 9009766..4f28c5d 100644 --- a/src/commands/lint.ts +++ b/src/commands/lint.ts @@ -22,7 +22,7 @@ interface Violation { // -- Tool Runners -- -async function runOxlint(targets: string[], fix?: boolean): Promise { +export async function runOxlint(targets: string[], fix?: boolean): Promise { const args = ["-c", oxlintConfig, "--format=json", ...targets]; if (fix) args.push("--fix"); const result = await x(local("oxlint"), args, { throwOnError: false }); @@ -74,7 +74,7 @@ interface KnipJsonReport { issues: KnipJsonIssue[]; } -async function runKnip(): Promise { +export async function runKnip(): Promise { const args = ["--no-progress", "--reporter", "json"]; const result = await x(local("knip"), args, { throwOnError: false }); if (!result.stdout.trim()) return []; From c7287a7ea4f12b2d3ac31e30909e17c5d24fd851 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 29 Apr 2026 16:29:51 +0100 Subject: [PATCH 3/4] update knip --- package.json | 2 +- pnpm-lock.yaml | 215 ++++++++++++++++++++----------------------- src/commands/lint.ts | 29 ++---- 3 files changed, 106 insertions(+), 140 deletions(-) diff --git a/package.json b/package.json index 4bf6af5..5289ee9 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@humanfs/node": "^0.16.7", "@humanfs/types": "^0.15.0", "@typescript/native-preview": "7.0.0-dev.20260427.1", - "knip": "^6.7.0", + "knip": "^6.9.0", "oxfmt": "^0.47.0", "oxlint": "^1.62.0", "publint": "^0.3.18", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 18078c9..14b780c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,8 +21,8 @@ importers: specifier: 7.0.0-dev.20260427.1 version: 7.0.0-dev.20260427.1 knip: - specifier: ^6.7.0 - version: 6.7.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + specifier: ^6.9.0 + version: 6.9.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) oxfmt: specifier: ^0.47.0 version: 0.47.0 @@ -143,15 +143,9 @@ packages: '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} - '@emnapi/core@1.9.2': - resolution: {integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==} - '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - '@emnapi/runtime@1.9.2': - resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} - '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} @@ -373,121 +367,121 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@oxc-parser/binding-android-arm-eabi@0.127.0': - resolution: {integrity: sha512-0LC7ye4hvqbIKxAzThzvswgHLFu2AURKzYLeSVvLdu2TBOYWQDmHnTqPLeA597BcUCxiLqLsS4CJ5uoI5WYWCQ==} + '@oxc-parser/binding-android-arm-eabi@0.128.0': + resolution: {integrity: sha512-aca6ZvzmCBUGOANQRiRQRZuRKYI3ENhcit6GisnknOOmcezfQc7xJ4dxlPU7MV7mOvrC7RNR1u3LAD7xyaiCxA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm64@0.127.0': - resolution: {integrity: sha512-b5jtVTH6AU5CJXHNdj7Jj9IEiR9yVjjnwHzPJhGyHGPdcsZSzBCkS9GBbV33niRMvKthDwQRFRJfI4a+k4PvYg==} + '@oxc-parser/binding-android-arm64@0.128.0': + resolution: {integrity: sha512-BbeDmuohoJ7Rz/it5wnkj69i/OsCPS3Z51nLEzwO/Y6YshtC4JU+15oNwhY8v4LRKRYclRc7ggOikwrsJ/eOEQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-parser/binding-darwin-arm64@0.127.0': - resolution: {integrity: sha512-obCE8B7ISKkJidjlhv9xRGJPOSDG2Yu6PRga9Ruaz35uintHxbp1Ki/Yc71wx4rj3Edrm0a1kzG1TAwit0wFpg==} + '@oxc-parser/binding-darwin-arm64@0.128.0': + resolution: {integrity: sha512-tRUHPt80417QmvNpoSslJT1VY8NUbWdrWR+L14Zn+RbOTcaqB8E6PYE/ZGN8jjWBzqporiA/H4MfO50ew/NCNA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.127.0': - resolution: {integrity: sha512-JL6Xb5IwPQT8rUzlpsX7E+AgfcdNklXNPFp8pjCQQ5MQOQo5rtEB2ui+3Hgg9Sn7Y9Egj6YOLLiHhLpdAe12Aw==} + '@oxc-parser/binding-darwin-x64@0.128.0': + resolution: {integrity: sha512-rWI2Hb1Nt3U/vKsjyNvZzDC8i/l144U20DKjhzaTmwIhIiSRGeroPWWiImwypmKLqrw8GuIixbWJkpGWLbkzrQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-parser/binding-freebsd-x64@0.127.0': - resolution: {integrity: sha512-SDQ/3MQFw58fqQz3Z1PhSKFF3JoCF4gmlNjziDm8X02tTahCw0qJbd7FGPDKw1i4VTBZene9JPyC3mHtSvi+wA==} + '@oxc-parser/binding-freebsd-x64@0.128.0': + resolution: {integrity: sha512-hhpdVMaNCLgQxjgNPeeFzSeJMmZPc5lKfv0NGSI3egZq9EdnEGqeC8JsYsQjK7PoQgbvZ17xlj0SO5ziH5Obkg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-parser/binding-linux-arm-gnueabihf@0.127.0': - resolution: {integrity: sha512-Av+D1MIqzV0YMGPT9we2SIZaMKD7Cxs4CvXSx/yxaWHewZjYEjScpOf5igc8IILASViw4WTnjlwUdI1KzVtDHQ==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.128.0': + resolution: {integrity: sha512-093zNw0zZ/e/obML+rhlSdmnzR0mVZluPcAkxunEc5E3F0yBVsFn24Y1ILfsEte11Ud041qn/gp2OJ1jxNqUng==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.127.0': - resolution: {integrity: sha512-Cs2fdJ8cPpFdeebj6p4dag8A4+56hPvZ0AhQQzlaLswGz1tz7bXt1nETLeorrM9+AMcWFFkqxcXwDGfTVidY8g==} + '@oxc-parser/binding-linux-arm-musleabihf@0.128.0': + resolution: {integrity: sha512-fq7DmKmfC+dvD97IXrgbph6Jzwe0EDu+PYMofmzZ6fv5X1k9vtaqLpDGMuICO9MmUnyKAQmVl+wIv2RNy4Dz8g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm64-gnu@0.127.0': - resolution: {integrity: sha512-qdOfTcT6SY8gsJrrV92uyEUyjqMGPpIB5JZUG6QN5dukYd+7/j0kX6MwK1DgQj39jtUYixxPiaRUiEN1+0CXgQ==} + '@oxc-parser/binding-linux-arm64-gnu@0.128.0': + resolution: {integrity: sha512-Xvm48jJah8TlIrURIjNOP/gNiGe6aKvCB+r06VliflFo8Kq7VOLE8PxtgShJzZIqubrgdMdYfvuPPozn7F6MbQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@oxc-parser/binding-linux-arm64-musl@0.127.0': - resolution: {integrity: sha512-EoTCZneNFU/P2qrpEM+RHmQwt+CvDkyGESG6qhr7KaegXLZwePfbrkCDfAk8/rhxbDUVGsZILX+2tqPzFtoFWA==} + '@oxc-parser/binding-linux-arm64-musl@0.128.0': + resolution: {integrity: sha512-M7iwBGmYJTx+pKOYFjI0buop4gJvlmcVzFGaXPt21DKpQkbQZG1f63Yg7LloIYT/t9yLxCw0Lhfx/RFlAlMSjA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@oxc-parser/binding-linux-ppc64-gnu@0.127.0': - resolution: {integrity: sha512-zALjmZYgxFLHjXeudcDF0xFGNydTAtkAeXAr2EuC17ywCyFxcmQra4w0BMde0Yi/re4Bi4iwEoEXtYN7l6eBLQ==} + '@oxc-parser/binding-linux-ppc64-gnu@0.128.0': + resolution: {integrity: sha512-21LGNIZb1Pcfk5/EGsqabrxv4yqQOWis1407JJrClS7XpFCrbvr74YAB1V+m54cYbwvO6UWwQqS4WecxiyfCRg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - '@oxc-parser/binding-linux-riscv64-gnu@0.127.0': - resolution: {integrity: sha512-fPP8M6zQLS7Jz7o9d5ArUSuAuSK3e+WCYVrCpdzeCOejidtZExJ9tjhDrAd3HEPqARBCPmdpqxESPFqy44vkBQ==} + '@oxc-parser/binding-linux-riscv64-gnu@0.128.0': + resolution: {integrity: sha512-gyHjOTFpg9bTTYjxPmQirvufb89+VdZwVfcMtAUyPr6F5H8ZswvCQshK4qOW+Q+2Xyb33hduRgY/eFHJQjU/vQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] - '@oxc-parser/binding-linux-riscv64-musl@0.127.0': - resolution: {integrity: sha512-7IcC4Ao02oGpfnjt+X/oF4U2mllo2qoSkw5xxiXNKL9MCTsTiAC6616beOuehdxGcnz1bRoPC1RQ2f1GQDdN+g==} + '@oxc-parser/binding-linux-riscv64-musl@0.128.0': + resolution: {integrity: sha512-X6Q2oKUrP5GyDd2xniuEBLk6aFQCZ97W2+aVXGgJXdjx5t4/oFuA9ri0wLOUrBIX+qdSuK581snMBio4z910eA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] - '@oxc-parser/binding-linux-s390x-gnu@0.127.0': - resolution: {integrity: sha512-pbXIhiNFHoqWeqDNLiJ9JkpHz1IM9k4DXa66x+1GTWMG7iLxtkXgE53iiuKSXwmk3zIYmaPVfBvgcAhS583K4Q==} + '@oxc-parser/binding-linux-s390x-gnu@0.128.0': + resolution: {integrity: sha512-BdzTmqxfxoYkpgokoLaSnOX6T+R3/goL42klre2tnG+kHbG2TXS0VN+P5BPofH1axdKOHy5ei4ENZrjmCOt2lA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - '@oxc-parser/binding-linux-x64-gnu@0.127.0': - resolution: {integrity: sha512-MYCguB9RvBvlSd6gbuNI7QwiLoCCAlGnlRJFPrzLI6U1/9wkC/WK6LtBAUln55H1Ctqw45PWmqrobKoMhsYQzQ==} + '@oxc-parser/binding-linux-x64-gnu@0.128.0': + resolution: {integrity: sha512-OO1nW2Q7sSYYvJZpDHdvyFSdRaVcQqRijZSSmWVMqFxPYy8cEF45zJ9fcdIYuzIT3jYq6YRhEFm/VMWNWhE22Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@oxc-parser/binding-linux-x64-musl@0.127.0': - resolution: {integrity: sha512-5eY0B/bxf1xIUxb4NOTvOI3KWtBQfPWYyKAzgcrCt0mDibSZygVpO1Pz8bkeiSZ5Jj9+M09dkggG3H8I5d0Uyg==} + '@oxc-parser/binding-linux-x64-musl@0.128.0': + resolution: {integrity: sha512-4NehAe404MRdoZVS9DW8C5XbJwbXIc/KfVlYdpi5vE4081zc9Y0YzKVqyOYj/Puye7/Do+ohaONBFWlEHYl9hw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@oxc-parser/binding-openharmony-arm64@0.127.0': - resolution: {integrity: sha512-Gld0ajrFTUXNtdw20fVBuTQx66FA75nIVg+//pPfR3sXkuABB4mTBhl3r9JNzrJpgW//qiwxf0nWXUWGJSL3UQ==} + '@oxc-parser/binding-openharmony-arm64@0.128.0': + resolution: {integrity: sha512-kVbqgW9xLL8bh8oc7aYOJilRKXE5G33+tE0jan+duo/9OriaFRpijcCwT2waWs2oqYROYq0GlE7/p3ywoshVeg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxc-parser/binding-wasm32-wasi@0.127.0': - resolution: {integrity: sha512-T6KVD7rhLzFlwGRXMnxUFfkCZD8FHnb968wVXW1mXzgRFc5RNXOBY2mPPDZ77x5Ln76ltLMgtPg0cOkU1NSrEQ==} + '@oxc-parser/binding-wasm32-wasi@0.128.0': + resolution: {integrity: sha512-L38ojghJYHmgiz6fJd7jwLB/ESDBpB02NdFxh+smqVM6P2anCEvHn0jhaSrt5eVNR1Ak8+moOeftUlofeyvniA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@oxc-parser/binding-win32-arm64-msvc@0.127.0': - resolution: {integrity: sha512-Ujvw4X+LD1CCGULcsQcvb4YNVoBGqt+JHgNNzGGaCImELiZLk477ifUH53gIbE7EKd933NdTi25JWEr9K2HwXw==} + '@oxc-parser/binding-win32-arm64-msvc@0.128.0': + resolution: {integrity: sha512-xgvO35GyHBtjlQ5AEpaYr7Rll1rvY7zqIhT6ty8E3ezBW2J1SFLjIDEvI/tcgDg6oaseDAqVcM+jU1HuCekgZw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.127.0': - resolution: {integrity: sha512-0cwxKO7KHQQQfo4Uf4B2SQrhgm+cJaP9OvFFhx52Tkg4bezsacu83GB2/In5bC415Ueeym+kXdnge/57rbSfTw==} + '@oxc-parser/binding-win32-ia32-msvc@0.128.0': + resolution: {integrity: sha512-OY+3eM2SN72prHKRB22mPz8o5A/7dJ+f5DFLBVvggyZhEaNDAH9IB+ElMjmOkOIwf5MDCUAowCK7pAncNxzpBA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.127.0': - resolution: {integrity: sha512-rOrnSQSCbhI2kowr9XxE7m9a8oQXnBHjnS6j95LxxAnEZ0+Fz20WlRXG4ondQb+ejjt2KOsa65sE6++L6kUd+w==} + '@oxc-parser/binding-win32-x64-msvc@0.128.0': + resolution: {integrity: sha512-NE9ny+cPUCCObXa0IKLfj0tCdPd7pe/dz9ZpkxpUOymB3miNeMPybdlYYTBSGJUalMWeBM85/4JcCErCNTqOXw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -495,6 +489,9 @@ packages: '@oxc-project/types@0.127.0': resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} + '@oxc-project/types@0.128.0': + resolution: {integrity: sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==} + '@oxc-resolver/binding-android-arm-eabi@11.19.1': resolution: {integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==} cpu: [arm] @@ -1386,8 +1383,8 @@ packages: jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - knip@6.7.0: - resolution: {integrity: sha512-ckL51NDH1YJxnv1kNB0iUdDngB4f/e9Igz8uIqYfmNDoyOFmmk1V0WFv3LQ7/hzC63b2Z9X41gGUE9eOWrZpaA==} + knip@6.9.0: + resolution: {integrity: sha512-2GLjxteBwmsSA3Z5sJZpPDaNPBIMnlm4/9Nx4CZadEK7YccJZ2/4kwKgPWhVYEqxhwhD0WO4txWXNGTO/Odkkg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -1427,8 +1424,8 @@ packages: outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - oxc-parser@0.127.0: - resolution: {integrity: sha512-bkgD4qHlN7WxLdX8bLXdaU54TtQtAIg/ZBAfm0aje/mo3MRDo3P0hZSgr4U7O3xfX+fQmR5AP04JS/TGcZLcFA==} + oxc-parser@0.128.0: + resolution: {integrity: sha512-XkOw3eiIxAgQ19WRew/Bq9wc5Ga/guaWIzDBzq80z1PyuDNGvWBpPby9k6YGwV8A8uMw+Nlq3xqlzuDYmUFYUw==} engines: {node: ^20.19.0 || >=22.12.0} oxc-resolver@11.19.1: @@ -2008,22 +2005,11 @@ snapshots: tslib: 2.8.1 optional: true - '@emnapi/core@1.9.2': - dependencies: - '@emnapi/wasi-threads': 1.2.1 - tslib: 2.8.1 - optional: true - '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 optional: true - '@emnapi/runtime@1.9.2': - dependencies: - tslib: 2.8.1 - optional: true - '@emnapi/wasi-threads@1.2.1': dependencies: tslib: 2.8.1 @@ -2162,13 +2148,6 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)': - dependencies: - '@emnapi/core': 1.9.2 - '@emnapi/runtime': 1.9.2 - '@tybys/wasm-util': 0.10.1 - optional: true - '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -2181,72 +2160,74 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - '@oxc-parser/binding-android-arm-eabi@0.127.0': + '@oxc-parser/binding-android-arm-eabi@0.128.0': optional: true - '@oxc-parser/binding-android-arm64@0.127.0': + '@oxc-parser/binding-android-arm64@0.128.0': optional: true - '@oxc-parser/binding-darwin-arm64@0.127.0': + '@oxc-parser/binding-darwin-arm64@0.128.0': optional: true - '@oxc-parser/binding-darwin-x64@0.127.0': + '@oxc-parser/binding-darwin-x64@0.128.0': optional: true - '@oxc-parser/binding-freebsd-x64@0.127.0': + '@oxc-parser/binding-freebsd-x64@0.128.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.127.0': + '@oxc-parser/binding-linux-arm-gnueabihf@0.128.0': optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.127.0': + '@oxc-parser/binding-linux-arm-musleabihf@0.128.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.127.0': + '@oxc-parser/binding-linux-arm64-gnu@0.128.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.127.0': + '@oxc-parser/binding-linux-arm64-musl@0.128.0': optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.127.0': + '@oxc-parser/binding-linux-ppc64-gnu@0.128.0': optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.127.0': + '@oxc-parser/binding-linux-riscv64-gnu@0.128.0': optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.127.0': + '@oxc-parser/binding-linux-riscv64-musl@0.128.0': optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.127.0': + '@oxc-parser/binding-linux-s390x-gnu@0.128.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.127.0': + '@oxc-parser/binding-linux-x64-gnu@0.128.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.127.0': + '@oxc-parser/binding-linux-x64-musl@0.128.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.127.0': + '@oxc-parser/binding-openharmony-arm64@0.128.0': optional: true - '@oxc-parser/binding-wasm32-wasi@0.127.0': + '@oxc-parser/binding-wasm32-wasi@0.128.0': dependencies: - '@emnapi/core': 1.9.2 - '@emnapi/runtime': 1.9.2 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2) + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.127.0': + '@oxc-parser/binding-win32-arm64-msvc@0.128.0': optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.127.0': + '@oxc-parser/binding-win32-ia32-msvc@0.128.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.127.0': + '@oxc-parser/binding-win32-x64-msvc@0.128.0': optional: true '@oxc-project/types@0.127.0': {} + '@oxc-project/types@0.128.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.19.1': optional: true @@ -2879,14 +2860,14 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 - knip@6.7.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): + knip@6.9.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): dependencies: fdir: 6.5.0(picomatch@4.0.4) formatly: 0.3.0 get-tsconfig: 4.14.0 jiti: 2.6.1 minimist: 1.2.8 - oxc-parser: 0.127.0 + oxc-parser: 0.128.0 oxc-resolver: 11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) picomatch: 4.0.4 smol-toml: 1.6.1 @@ -2926,30 +2907,30 @@ snapshots: outdent@0.5.0: {} - oxc-parser@0.127.0: + oxc-parser@0.128.0: dependencies: - '@oxc-project/types': 0.127.0 + '@oxc-project/types': 0.128.0 optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.127.0 - '@oxc-parser/binding-android-arm64': 0.127.0 - '@oxc-parser/binding-darwin-arm64': 0.127.0 - '@oxc-parser/binding-darwin-x64': 0.127.0 - '@oxc-parser/binding-freebsd-x64': 0.127.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.127.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.127.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.127.0 - '@oxc-parser/binding-linux-arm64-musl': 0.127.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.127.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.127.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.127.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.127.0 - '@oxc-parser/binding-linux-x64-gnu': 0.127.0 - '@oxc-parser/binding-linux-x64-musl': 0.127.0 - '@oxc-parser/binding-openharmony-arm64': 0.127.0 - '@oxc-parser/binding-wasm32-wasi': 0.127.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.127.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.127.0 - '@oxc-parser/binding-win32-x64-msvc': 0.127.0 + '@oxc-parser/binding-android-arm-eabi': 0.128.0 + '@oxc-parser/binding-android-arm64': 0.128.0 + '@oxc-parser/binding-darwin-arm64': 0.128.0 + '@oxc-parser/binding-darwin-x64': 0.128.0 + '@oxc-parser/binding-freebsd-x64': 0.128.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.128.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.128.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.128.0 + '@oxc-parser/binding-linux-arm64-musl': 0.128.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.128.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.128.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.128.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.128.0 + '@oxc-parser/binding-linux-x64-gnu': 0.128.0 + '@oxc-parser/binding-linux-x64-musl': 0.128.0 + '@oxc-parser/binding-openharmony-arm64': 0.128.0 + '@oxc-parser/binding-wasm32-wasi': 0.128.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.128.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.128.0 + '@oxc-parser/binding-win32-x64-msvc': 0.128.0 oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): optionalDependencies: diff --git a/src/commands/lint.ts b/src/commands/lint.ts index 4f28c5d..9232a39 100644 --- a/src/commands/lint.ts +++ b/src/commands/lint.ts @@ -2,7 +2,7 @@ import { fileURLToPath } from "node:url"; import { parse } from "@bomb.sh/args"; import { publint } from "publint"; import { x } from "tinyexec"; -import type { ReporterOptions } from "knip"; +import type { JSONReport as KnipJSONReport } from "knip"; import type { CommandContext } from "../context.ts"; import { local } from "../utils.ts"; @@ -59,31 +59,16 @@ async function runPublint(): Promise { })); } -type KnipCategory = keyof ReporterOptions["issues"]; - -interface KnipJsonIssueItem { - name: string; - line?: number; - col?: number; - pos?: number; -} - -type KnipJsonIssue = { file: string } & Record; - -interface KnipJsonReport { - issues: KnipJsonIssue[]; -} - export async function runKnip(): Promise { const args = ["--no-progress", "--reporter", "json"]; const result = await x(local("knip"), args, { throwOnError: false }); if (!result.stdout.trim()) return []; - const json: KnipJsonReport = JSON.parse(result.stdout); + const json: KnipJSONReport = JSON.parse(result.stdout); const violations: Violation[] = []; for (const issue of json.issues) { - for (const dep of issue.dependencies) { + for (const dep of issue.dependencies ?? []) { violations.push({ tool: "knip", level: "warning", @@ -94,7 +79,7 @@ export async function runKnip(): Promise { column: dep.col, }); } - for (const dep of issue.devDependencies) { + for (const dep of issue.devDependencies ?? []) { violations.push({ tool: "knip", level: "warning", @@ -105,7 +90,7 @@ export async function runKnip(): Promise { column: dep.col, }); } - for (const exp of issue.exports) { + for (const exp of issue.exports ?? []) { violations.push({ tool: "knip", level: "warning", @@ -116,7 +101,7 @@ export async function runKnip(): Promise { column: exp.col, }); } - for (const t of issue.types) { + for (const t of issue.types ?? []) { violations.push({ tool: "knip", level: "warning", @@ -127,7 +112,7 @@ export async function runKnip(): Promise { column: t.col, }); } - for (const file of issue.files) { + for (const file of issue.files ?? []) { violations.push({ tool: "knip", level: "warning", From d52f2bf197de770028284a1b8a1c61c5defa4c2b Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 29 Apr 2026 19:00:38 +0100 Subject: [PATCH 4/4] add changeset --- .changeset/large-actors-remain.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/large-actors-remain.md diff --git a/.changeset/large-actors-remain.md b/.changeset/large-actors-remain.md new file mode 100644 index 0000000..d9aa292 --- /dev/null +++ b/.changeset/large-actors-remain.md @@ -0,0 +1,6 @@ +--- +"@bomb.sh/tools": patch +--- + +- Fixes `runKnip` to correctly handle reported violations +- Updates `knip` dependency to `6.9.0`