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

Issue:
🚀 Feature: Add --exclude-templated-by option
### 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

Right now, a notice is appended to the bottom of generated README.md files:

https://github.com/JoshuaKGoldberg/create-typescript-app/blob/e65eae3e00d797ce0d88b5a1eb9707c857c66944/src/steps/updateReadme.ts#L7

In general it's added when the README.md doesn't yet have one:

https://github.com/JoshuaKGoldberg/create-typescript-app/blob/e65eae3e00d797ce0d88b5a1eb9707c857c66944/src/steps/updateReadme.ts#L25-L27

The `create` way would be to move that behind an option. Which I think is also just generally cleaner in CTA already.

### Additional Info

Split out of #1670.

💖 

Pull request description:
feat: add --exclude-templated-by option
## PR Checklist

- [x] Addresses an existing open issue: fixes #1714
- [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

Adds the option as described, with a unit test.

💖

Patch:
diff --git a/docs/Options.md b/docs/Options.md
index d548a04..9b29b51 100644
--- a/docs/Options.md
+++ b/docs/Options.md
@@ -101,6 +101,7 @@ Alternately, you can bypass that prompt by providing any number of the following
 - `--exclude-lint-yml`: Don't apply linting and sorting to `*.yaml` and `*.yml` files.
 - `--exclude-releases`: Don't add release-it to generate changelogs, package bumps, and publishes based on conventional commits.
 - `--exclude-renovate`: Don't add a Renovate config to dependencies up-to-date with PRs.
+- `--exclude-templated-by`: Don't add a _"This package was templated with create-typescript-app"_ notice at the end of the README.md.
 - `--exclude-tests`: Don't add Vitest tooling for fast unit tests, configured with coverage tracking.
 
 For example, initializing with all tooling except for `package.json` checks and Renovate:
diff --git a/src/shared/options/args.ts b/src/shared/options/args.ts
index 3c78474..a4325fe 100644
--- a/src/shared/options/args.ts
+++ b/src/shared/options/args.ts
@@ -174,6 +174,11 @@ export const allArgOptions = {
 		docsSection: "opt-out",
 		type: "boolean",
 	},
+	"exclude-templated-by": {
+		description: `Don't add a _"This package was templated with create-typescript-app"_ notice at the end of the README.md.`,
+		docsSection: "opt-out",
+		type: "boolean",
+	},
 	"exclude-tests": {
 		description: `Don't add Vitest tooling for fast unit tests, configured 
   with coverage tracking.`,
diff --git a/src/shared/options/exclusionKeys.ts b/src/shared/options/exclusionKeys.ts
index a15acf2..013d117 100644
--- a/src/shared/options/exclusionKeys.ts
+++ b/src/shared/options/exclusionKeys.ts
@@ -107,6 +107,12 @@ export const exclusionDescriptions: Record<ExclusionKey, ExclusionDescription> =
 			hint: "--exclude-renovate",
 			label: "Add a Renovate config to keep dependencies up-to-date with PRs.",
 		},
+		excludeTemplatedBy: {
+			hint: "--exclude-templated-by",
+			label:
+				'Add a _"This package was templated with create-typescript-app"_ notice at the end of the README.md.',
+			level: "minimal",
+		},
 		excludeTests: {
 			hint: "--exclude-tests",
 			label:
diff --git a/src/shared/options/optionsSchema.ts b/src/shared/options/optionsSchema.ts
index e6a097a..36c3abc 100644
--- a/src/shared/options/optionsSchema.ts
+++ b/src/shared/options/optionsSchema.ts
@@ -39,6 +39,7 @@ export const optionsSchemaShape = {
 	excludeLintYml: z.boolean().optional(),
 	excludeReleases: z.boolean().optional(),
 	excludeRenovate: z.boolean().optional(),
+	excludeTemplatedBy: z.boolean().optional(),
 	excludeTests: z.boolean().optional(),
 	funding: z.string().optional(),
 	guide: z.string().url().optional(),
diff --git a/src/shared/options/readOptions.ts b/src/shared/options/readOptions.ts
index f637451..6fa0f4b 100644
--- a/src/shared/options/readOptions.ts
+++ b/src/shared/options/readOptions.ts
@@ -81,6 +81,7 @@ export async function readOptions(
 		excludeLintYml: values["exclude-lint-yml"],
 		excludeReleases: values["exclude-releases"],
 		excludeRenovate: values["exclude-renovate"],
+		excludeTemplatedBy: values["exclude-templated-by"],
 		excludeTests: values["unit-tests"],
 		funding: values.funding,
 		guide: values.guide,
diff --git a/src/shared/types.ts b/src/shared/types.ts
index a22f944..af6f3f1 100644
--- a/src/shared/types.ts
+++ b/src/shared/types.ts
@@ -71,6 +71,7 @@ export interface Options {
 	excludeLintYml?: boolean;
 	excludeReleases?: boolean;
 	excludeRenovate?: boolean;
+	excludeTemplatedBy?: boolean;
 	excludeTests?: boolean;
 	funding?: string;
 	guide?: OptionsGuide;
diff --git a/src/steps/updateReadme.ts b/src/steps/updateReadme.ts
index 6032020..7eb3dab 100644
--- a/src/steps/updateReadme.ts
+++ b/src/steps/updateReadme.ts
@@ -17,12 +17,14 @@ export const endOfReadmeNotice = [
 export const endOfReadmeMatcher =
 	/💙.+(?:based|built|templated).+(?:from|using|on|with).+create-typescript-app/;
 
-export async function updateReadme(options: Pick<Options, "owner">) {
+export async function updateReadme(
+	options: Pick<Options, "excludeTemplatedBy" | "owner">,
+) {
 	let contents = await readFileSafe("./README.md", "");
 
 	contents = contents.replaceAll("JoshuaKGoldberg", options.owner);
 
-	if (!endOfReadmeMatcher.test(contents)) {
+	if (!options.excludeTemplatedBy && !endOfReadmeMatcher.test(contents)) {
 		contents += endOfReadmeNotice;
 	}
 


Test patch:
diff --git a/src/bin/help.test.ts b/src/bin/help.test.ts
index d0956d3..0ce57c9 100644
--- a/src/bin/help.test.ts
+++ b/src/bin/help.test.ts
@@ -271,6 +271,10 @@ describe("logHelpText", () => {
 			  --exclude-renovate: Don't add a Renovate config to dependencies up-to-date with 
 			  PRs.",
 			  ],
+			  [
+			    "
+			  --exclude-templated-by: Don't add a _"This package was templated with create-typescript-app"_ notice at the end of the README.md.",
+			  ],
 			  [
 			    "
 			  --exclude-tests: Don't add Vitest tooling for fast unit tests, configured 
diff --git a/src/create/createRerunSuggestion.test.ts b/src/create/createRerunSuggestion.test.ts
index 7dfbe20..9854ca3 100644
--- a/src/create/createRerunSuggestion.test.ts
+++ b/src/create/createRerunSuggestion.test.ts
@@ -27,6 +27,7 @@ const options = {
 	excludeLintYml: false,
 	excludeReleases: false,
 	excludeRenovate: undefined,
+	excludeTemplatedBy: undefined,
 	excludeTests: undefined,
 	funding: undefined,
 	keywords: ["abc", "def ghi", "jkl mno pqr"],
diff --git a/src/shared/options/augmentOptionsWithExcludes.test.ts b/src/shared/options/augmentOptionsWithExcludes.test.ts
index 63f2a7e..75dd924 100644
--- a/src/shared/options/augmentOptionsWithExcludes.test.ts
+++ b/src/shared/options/augmentOptionsWithExcludes.test.ts
@@ -44,6 +44,7 @@ const optionsBase = {
 	excludeLintYml: undefined,
 	excludeReleases: undefined,
 	excludeRenovate: undefined,
+	excludeTemplatedBy: undefined,
 	excludeTests: undefined,
 	funding: undefined,
 	logo: undefined,
@@ -102,6 +103,7 @@ describe("augmentOptionsWithExcludes", () => {
 			excludeLintYml: true,
 			excludeReleases: true,
 			excludeRenovate: true,
+			excludeTemplatedBy: true,
 			excludeTests: true,
 		});
 	});
diff --git a/src/shared/options/readOptions.test.ts b/src/shared/options/readOptions.test.ts
index 8df87a8..2c6c26d 100644
--- a/src/shared/options/readOptions.test.ts
+++ b/src/shared/options/readOptions.test.ts
@@ -31,6 +31,7 @@ const emptyOptions = {
 	excludeLintYml: undefined,
 	excludeReleases: undefined,
 	excludeRenovate: undefined,
+	excludeTemplatedBy: undefined,
 	excludeTests: undefined,
 	funding: undefined,
 	guide: undefined,
@@ -751,6 +752,7 @@ describe("readOptions", () => {
 			      "excludeLintYml": undefined,
 			      "excludeReleases": undefined,
 			      "excludeRenovate": undefined,
+			      "excludeTemplatedBy": undefined,
 			      "excludeTests": undefined,
 			      "funding": undefined,
 			      "github": "mock.git",
diff --git a/src/steps/updateReadme.test.ts b/src/steps/updateReadme.test.ts
index 135c456..5247063 100644
--- a/src/steps/updateReadme.test.ts
+++ b/src/steps/updateReadme.test.ts
@@ -23,7 +23,7 @@ const options = {
 };
 
 describe("updateReadme", () => {
-	it("adds a notice when the file does not contain it already", async () => {
+	it("adds a notice when the file does not contain it already and excludeTemplatedBy is not enabled", async () => {
 		mockReadFileSafe.mockResolvedValue(
 			"Existing JoshuaKGoldberg/create-typescript-app content.",
 		);
@@ -44,6 +44,23 @@ describe("updateReadme", () => {
 		`);
 	});
 
+	it("doesn't add a notice when excludeTemplatedBy is enabled", async () => {
+		mockReadFileSafe.mockResolvedValue(
+			"Existing JoshuaKGoldberg/create-typescript-app content.",
+		);
+
+		await updateReadme({ ...options, excludeTemplatedBy: true });
+
+		expect(mockWriteFile.mock.calls).toMatchInlineSnapshot(`
+			[
+			  [
+			    "./README.md",
+			    "Existing NewOwner/create-typescript-app content.",
+			  ],
+			]
+		`);
+	});
+
 	it("doesn't add a notice when the file contains it already", async () => {
 		mockReadFileSafe.mockResolvedValue(`
 			Existing JoshuaKGoldberg/create-typescript-app content.
diff --git a/src/steps/writing/creation/dotGitHub/createDotGitHubFiles.test.ts b/src/steps/writing/creation/dotGitHub/createDotGitHubFiles.test.ts
index 66a0962..acb1471 100644
--- a/src/steps/writing/creation/dotGitHub/createDotGitHubFiles.test.ts
+++ b/src/steps/writing/creation/dotGitHub/createDotGitHubFiles.test.ts
@@ -30,6 +30,7 @@ const createOptions = (exclude: boolean) =>
 		excludeLintYml: exclude,
 		excludeReleases: exclude,
 		excludeRenovate: exclude,
+		excludeTemplatedBy: exclude,
 		excludeTests: exclude,
 		mode: "create",
 		owner: "StubOwner",
diff --git a/src/steps/writing/creation/dotGitHub/createWorkflows.test.ts b/src/steps/writing/creation/dotGitHub/createWorkflows.test.ts
index 6eaa1c0..d348abd 100644
--- a/src/steps/writing/creation/dotGitHub/createWorkflows.test.ts
+++ b/src/steps/writing/creation/dotGitHub/createWorkflows.test.ts
@@ -26,6 +26,7 @@ const createOptions = (exclude: boolean) =>
 		excludeLintYml: exclude,
 		excludeReleases: exclude,
 		excludeRenovate: exclude,
+		excludeTemplatedBy: exclude,
 		excludeTests: exclude,
 		mode: "create",
 		owner: "StubOwner",