Repository: JoshuaKGoldberg/create-typescript-app (ts)

Issue:
🚀 Feature: Add an addon for comments on top of the ESLint config
### Bug Report Checklist

- [X] I have pulled the latest `main` branch of the repository.
- [X] I have [searched for related issues](https://github.com/JoshuaKGoldberg/create-typescript-app/issues?q=is%3Aissue) and found none that matched my issue.

### Overview

I've intentionally kept this warning on top of the `eslint.config.js` file for a while:

https://github.com/JoshuaKGoldberg/create-typescript-app/blob/e6e1575333ba3cc59a1e7e919e02f81035c19280/eslint.config.js#L1-L9

I think it's good for this template to have it because I use this repo as a reference for other people. I don't want everyone to read the file and think all ESLint configs must be as thorough as it.

But, I don't want that comment to exist in generated repos. I would assume most people are using the _Common_ base/preset - which isn't quite so intense. And if someone has opted into the _Everything_ base/preset, then they probably don't need it.

Resolution: I'll add an _addon_ for the ESLint block so that it can be added if a `create.config.js` wants it.

### Additional Info

I don't plan on adding this to the legacy engine, just `create` blocks.

Prototyped in #1839.

💖

Pull request:
feat: add addons.explanations to blockESLint
## PR Checklist

- [x] Addresses an existing open issue: fixes #1849
- [x] That issue was marked as [`status: accepting prs`](https://github.com/JoshuaKGoldberg/create-typescript-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22)
- [x] Steps in [CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/create-typescript-app/blob/main/.github/CONTRIBUTING.md) were taken

## Overview

💖 

Patch:
diff --git a/src/next/blocks/blockESLint.ts b/src/next/blocks/blockESLint.ts
index cbbf38f..65990ab 100644
--- a/src/next/blocks/blockESLint.ts
+++ b/src/next/blocks/blockESLint.ts
@@ -57,6 +57,7 @@ export const blockESLint = base.createBlock({
 	},
 	addons: {
 		beforeLint: z.string().optional(),
+		explanations: z.array(z.string()).default([]),
 		extensions: z.array(z.union([z.string(), zExtension])).default([]),
 		ignores: z.array(z.string()).default([]),
 		imports: z.array(zPackageImport).default([]),
@@ -74,7 +75,8 @@ export const blockESLint = base.createBlock({
 		};
 	},
 	produce({ addons, options }) {
-		const { extensions, ignores, imports, rules, settings } = addons;
+		const { explanations, extensions, ignores, imports, rules, settings } =
+			addons;
 
 		const importLines = [
 			'import eslint from "@eslint/js";',
@@ -197,7 +199,7 @@ Each should be shown in VS Code, and can be run manually on the command-line:
 				}),
 			],
 			files: {
-				"eslint.config.js": `${importLines.join("\n")}
+				"eslint.config.js": `${explanations.map((explanation) => `/*\n${explanation}\n*/\n`).join("")}${importLines.join("\n")}
 
 export default tseslint.config(
 	{ ignores: [${ignoreLines.join(", ")}] },
diff --git a/src/next/blocks/blockKnip.ts b/src/next/blocks/blockKnip.ts
index d727d5e..1d96260 100644
--- a/src/next/blocks/blockKnip.ts
+++ b/src/next/blocks/blockKnip.ts
@@ -1,3 +1,5 @@
+import { z } from "zod";
+
 import { base } from "../base.js";
 import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js";
 import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js";
@@ -9,6 +11,9 @@ export const blockKnip = base.createBlock({
 	about: {
 		name: "Knip",
 	},
+	addons: {
+		ignoreDependencies: z.array(z.string()).optional(),
+	},
 	migrate() {
 		return {
 			scripts: [
@@ -19,7 +24,8 @@ export const blockKnip = base.createBlock({
 			],
 		};
 	},
-	produce() {
+	produce({ addons }) {
+		const { ignoreDependencies } = addons;
 		return {
 			addons: [
 				blockDevelopmentDocs({
@@ -54,6 +60,7 @@ export const blockKnip = base.createBlock({
 				"knip.json": JSON.stringify({
 					$schema: `https://unpkg.com/knip@${getPackageDependency("knip")}/schema.json`,
 					entry: ["src/index.ts"],
+					ignoreDependencies,
 					ignoreExportsUsedInFile: {
 						interface: true,
 						type: true,


Test patch:
diff --git a/src/next/blocks/blockESLint.test.ts b/src/next/blocks/blockESLint.test.ts
index 4c24fa1..de6ba66 100644
--- a/src/next/blocks/blockESLint.test.ts
+++ b/src/next/blocks/blockESLint.test.ts
@@ -272,6 +272,7 @@ describe("blockESLint", () => {
 		const creation = testBlock(blockESLint, {
 			addons: {
 				beforeLint: "Before lint.",
+				explanations: ["This is a great config!", "You should use it!"],
 				extensions: [
 					"a.configs.recommended",
 					{
@@ -406,7 +407,13 @@ describe("blockESLint", () => {
 			    },
 			  ],
 			  "files": {
-			    "eslint.config.js": "import eslint from "@eslint/js";
+			    "eslint.config.js": "/*
+			This is a great config!
+			*/
+			/*
+			You should use it!
+			*/
+			import eslint from "@eslint/js";
 			import a from "eslint-plugin-markdown"
 			import b from "eslint-plugin-regexp"
 			import tseslint from "typescript-eslint";
diff --git a/src/next/blocks/blockKnip.test.ts b/src/next/blocks/blockKnip.test.ts
new file mode 100644
index 0000000..1b73639
--- /dev/null
+++ b/src/next/blocks/blockKnip.test.ts
@@ -0,0 +1,130 @@
+import { testBlock } from "create-testers";
+import { describe, expect, test, vi } from "vitest";
+
+import { blockKnip } from "./blockKnip.js";
+import { optionsBase } from "./options.fakes.js";
+
+vi.mock("../utils/resolveBin.js", () => ({
+	resolveBin: (bin: string) => `path/to/${bin}`,
+}));
+
+describe("blockKnip", () => {
+	test("without addons", () => {
+		const creation = testBlock(blockKnip, {
+			options: optionsBase,
+		});
+
+		expect(creation).toMatchInlineSnapshot(`
+			{
+			  "addons": [
+			    {
+			      "addons": {
+			        "sections": {
+			          "Linting": {
+			            "contents": {
+			              "items": [
+			                "- \`pnpm lint:knip\` ([knip](https://github.com/webpro/knip)): Detects unused files, dependencies, and code exports",
+			              ],
+			            },
+			          },
+			        },
+			      },
+			      "block": [Function],
+			    },
+			    {
+			      "addons": {
+			        "jobs": [
+			          {
+			            "name": "Lint Knip",
+			            "steps": [
+			              {
+			                "run": "pnpm lint:knip",
+			              },
+			            ],
+			          },
+			        ],
+			      },
+			      "block": [Function],
+			    },
+			    {
+			      "addons": {
+			        "properties": {
+			          "devDependencies": {
+			            "knip": "5.41.1",
+			          },
+			          "scripts": {
+			            "lint:knip": "knip",
+			          },
+			        },
+			      },
+			      "block": [Function],
+			    },
+			  ],
+			  "files": {
+			    "knip.json": "{"$schema":"https://unpkg.com/knip@5.41.1/schema.json","entry":["src/index.ts"],"ignoreExportsUsedInFile":{"interface":true,"type":true},"project":["src/**/*.ts"]}",
+			  },
+			}
+		`);
+	});
+
+	test("with addons", () => {
+		const creation = testBlock(blockKnip, {
+			addons: {
+				ignoreDependencies: ["abc", "def"],
+			},
+			options: optionsBase,
+		});
+
+		expect(creation).toMatchInlineSnapshot(`
+			{
+			  "addons": [
+			    {
+			      "addons": {
+			        "sections": {
+			          "Linting": {
+			            "contents": {
+			              "items": [
+			                "- \`pnpm lint:knip\` ([knip](https://github.com/webpro/knip)): Detects unused files, dependencies, and code exports",
+			              ],
+			            },
+			          },
+			        },
+			      },
+			      "block": [Function],
+			    },
+			    {
+			      "addons": {
+			        "jobs": [
+			          {
+			            "name": "Lint Knip",
+			            "steps": [
+			              {
+			                "run": "pnpm lint:knip",
+			              },
+			            ],
+			          },
+			        ],
+			      },
+			      "block": [Function],
+			    },
+			    {
+			      "addons": {
+			        "properties": {
+			          "devDependencies": {
+			            "knip": "5.41.1",
+			          },
+			          "scripts": {
+			            "lint:knip": "knip",
+			          },
+			        },
+			      },
+			      "block": [Function],
+			    },
+			  ],
+			  "files": {
+			    "knip.json": "{"$schema":"https://unpkg.com/knip@5.41.1/schema.json","entry":["src/index.ts"],"ignoreDependencies":["abc","def"],"ignoreExportsUsedInFile":{"interface":true,"type":true},"project":["src/**/*.ts"]}",
+			  },
+			}
+		`);
+	});
+});