From 1fb1abe376d5944db1e0bdcd7e83e8af4ba93c78 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 1 Apr 2025 16:02:00 -0400 Subject: [PATCH] feat: infer Knip Addons from disk --- src/blocks/blockKnip.test.ts | 36 ++++++++++++++++++++-- src/blocks/blockKnip.ts | 17 +++++++++- src/blocks/intake/intakeFileAsJson.test.ts | 35 +++++++++++++++++++++ src/blocks/intake/intakeFileAsJson.ts | 14 +++++++++ 4 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 src/blocks/intake/intakeFileAsJson.test.ts create mode 100644 src/blocks/intake/intakeFileAsJson.ts diff --git a/src/blocks/blockKnip.test.ts b/src/blocks/blockKnip.test.ts index 1403c4d58..830e80499 100644 --- a/src/blocks/blockKnip.test.ts +++ b/src/blocks/blockKnip.test.ts @@ -1,5 +1,5 @@ -import { testBlock } from "bingo-stratum-testers"; -import { describe, expect, test, vi } from "vitest"; +import { testBlock, testIntake } from "bingo-stratum-testers"; +import { describe, expect, it, test, vi } from "vitest"; import { blockKnip } from "./blockKnip.js"; import { optionsBase } from "./options.fakes.js"; @@ -206,4 +206,36 @@ describe("blockKnip", () => { } `); }); + + describe("intake", () => { + it("returns undefined when knip.json does not exist", () => { + const actual = testIntake(blockKnip, { + files: {}, + }); + + expect(actual).toBeUndefined(); + }); + + it("returns undefined when knip.json does not contain ignoreDependencies", () => { + const actual = testIntake(blockKnip, { + files: { + "knip.json": [JSON.stringify({ other: true })], + }, + }); + + expect(actual).toBeUndefined(); + }); + + it("returns ignoreDependencies when knip.json contains ignoreDependencies", () => { + const ignoreDependencies = ["a", "b", "c"]; + + const actual = testIntake(blockKnip, { + files: { + "knip.json": [JSON.stringify({ ignoreDependencies })], + }, + }); + + expect(actual).toEqual({ ignoreDependencies }); + }); + }); }); diff --git a/src/blocks/blockKnip.ts b/src/blocks/blockKnip.ts index 87680d47b..2fbda8c26 100644 --- a/src/blocks/blockKnip.ts +++ b/src/blocks/blockKnip.ts @@ -10,13 +10,28 @@ import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js"; import { blockPackageJson } from "./blockPackageJson.js"; import { blockRemoveFiles } from "./blockRemoveFiles.js"; import { blockRemoveWorkflows } from "./blockRemoveWorkflows.js"; +import { intakeFileAsJson } from "./intake/intakeFileAsJson.js"; + +const zIgnoreDependencies = z.array(z.string()); export const blockKnip = base.createBlock({ about: { name: "Knip", }, addons: { - ignoreDependencies: z.array(z.string()).optional(), + ignoreDependencies: zIgnoreDependencies.optional(), + }, + intake({ files }) { + const knipJson = intakeFileAsJson(files, ["knip.json"]); + if (!knipJson?.ignoreDependencies) { + return undefined; + } + + return { + ignoreDependencies: zIgnoreDependencies.safeParse( + knipJson.ignoreDependencies, + ).data, + }; }, produce({ addons }) { const { ignoreDependencies } = addons; diff --git a/src/blocks/intake/intakeFileAsJson.test.ts b/src/blocks/intake/intakeFileAsJson.test.ts new file mode 100644 index 000000000..95107819e --- /dev/null +++ b/src/blocks/intake/intakeFileAsJson.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from "vitest"; + +import { intakeFileAsJson } from "./intakeFileAsJson.js"; + +describe(intakeFileAsJson, () => { + it("returns undefined when the file does not exist", () => { + const actual = intakeFileAsJson({}, []); + + expect(actual).toBeUndefined(); + }); + + it("returns undefined when the file does not have valid JSON", () => { + const actual = intakeFileAsJson( + { + "file.json": ["{"], + }, + ["file.json"], + ); + + expect(actual).toBeUndefined(); + }); + + it("returns loaded file contents when the file has valid JSON", () => { + const value = { key: "value" }; + + const actual = intakeFileAsJson( + { + "file.json": [JSON.stringify(value)], + }, + ["file.json"], + ); + + expect(actual).toEqual(value); + }); +}); diff --git a/src/blocks/intake/intakeFileAsJson.ts b/src/blocks/intake/intakeFileAsJson.ts new file mode 100644 index 000000000..0173a578e --- /dev/null +++ b/src/blocks/intake/intakeFileAsJson.ts @@ -0,0 +1,14 @@ +import { IntakeDirectory } from "bingo-fs"; +import JSON5 from "json5"; + +import { intakeFile } from "./intakeFile.js"; + +export function intakeFileAsJson(files: IntakeDirectory, filePath: string[]) { + const file = intakeFile(files, filePath); + + try { + return file && JSON5.parse | undefined>(file[0]); + } catch { + return undefined; + } +}