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

Issue:
🚀 Feature: Allow customizing ncc build entry point
### Feature Request 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

Coming over from https://github.com/JoshuaKGoldberg/release-it-action/pull/479/files#r2029276151: not every ncc-built project has the same `src/index.ts` entry point. They'll need to be customizable.

### Additional Info

🎁 

Pull request:
feat: add entry addon for blockNcc
## PR Checklist

- [x] Addresses an existing open issue: fixes #2154
- [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

Includes bumps of `bingo-stratum` and `bingo-stratum-testers` so its `intake` can include `options` per https://github.com/JoshuaKGoldberg/bingo/issues/345.

🎁 

Patch:
diff --git a/package.json b/package.json
index ff91469..5ec3f72 100644
--- a/package.json
+++ b/package.json
@@ -39,7 +39,7 @@
 	"dependencies": {
 		"bingo": "^0.5.14",
 		"bingo-fs": "^0.5.5",
-		"bingo-stratum": "^0.5.10",
+		"bingo-stratum": "^0.5.11",
 		"cached-factory": "^0.1.0",
 		"cspell-populate-words": "^0.3.0",
 		"execa": "^9.5.2",
@@ -88,7 +88,7 @@
 		"@vitest/eslint-plugin": "1.1.38",
 		"all-contributors-cli": "6.26.1",
 		"bingo-requests": "0.5.5",
-		"bingo-stratum-testers": "0.5.8",
+		"bingo-stratum-testers": "0.5.9",
 		"bingo-testers": "0.5.7",
 		"console-fail-test": "0.5.0",
 		"cspell": "8.17.5",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9b15295..ca31207 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -15,8 +15,8 @@ importers:
         specifier: ^0.5.5
         version: 0.5.5
       bingo-stratum:
-        specifier: ^0.5.10
-        version: 0.5.10(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14)(zod@3.24.2)
+        specifier: ^0.5.11
+        version: 0.5.11(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14)(zod@3.24.2)
       cached-factory:
         specifier: ^0.1.0
         version: 0.1.0
@@ -157,8 +157,8 @@ importers:
         specifier: 0.5.5
         version: 0.5.5
       bingo-stratum-testers:
-        specifier: 0.5.8
-        version: 0.5.8(bingo-fs@0.5.5)(bingo-stratum@0.5.10(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14)(zod@3.24.2))(bingo-systems@0.5.4)(bingo-testers@0.5.7(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14))(bingo@0.5.14)
+        specifier: 0.5.9
+        version: 0.5.9(bingo-fs@0.5.5)(bingo-stratum@0.5.11(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14)(zod@3.24.2))(bingo-systems@0.5.4)(bingo-testers@0.5.7(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14))(bingo@0.5.14)
       bingo-testers:
         specifier: 0.5.7
         version: 0.5.7(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14)
@@ -1655,21 +1655,21 @@ packages:
     resolution: {integrity: sha512-NQcnlAtnR72Cfn6Y9EWPS3RXAGM+4ndE3BEueLnWOQgj+/Y6kQyhGEiB7cPmi4VeJL+Kcqo/EGyzuF23aIT3Cg==}
     engines: {node: '>=18'}
 
-  bingo-stratum-testers@0.5.8:
-    resolution: {integrity: sha512-QrAvq03mKTDG0jJtJ6AQ7cKVxA/iFdz6co1SHsu2NkYp/XKnFwchX0jhFeb8VWWoN68cJxv+Abjp6Utmb0qlYA==}
+  bingo-stratum-testers@0.5.9:
+    resolution: {integrity: sha512-G7V5j4++dnHv3PqAbnFEVADQWmFLyQJtM1aIHOTMW41xOl9wa+XLmQ70uFvuvZQvhb59Q/sgpO5ORSltImeDQA==}
     engines: {node: '>=18'}
     peerDependencies:
-      bingo: ^0.5.11
-      bingo-fs: ^0.5.4
-      bingo-stratum: ^0.5.8
+      bingo: ^0.5.14
+      bingo-fs: ^0.5.5
+      bingo-stratum: ^0.5.11
       bingo-systems: ^0.5.4
       bingo-testers: ^0.5.7
 
-  bingo-stratum@0.5.10:
-    resolution: {integrity: sha512-Nzuh93WLHp0ngAf/oJq5iWjFdaPRgS6sUq29M4GgRdmSO3ApCeH1nWZTVuGQL5nPsYDFW8+MgWCNpQYbHmCVlQ==}
+  bingo-stratum@0.5.11:
+    resolution: {integrity: sha512-5tmx6zmqDjHyMklhnYsUNyv9aJVxu92kYdUlSs3Jq42w9WVIwCACJatCMUJYDRo6035MXjB6sbUSORT55l/qBg==}
     engines: {node: '>=18'}
     peerDependencies:
-      bingo: ^0.5.13
+      bingo: ^0.5.14
       bingo-fs: ^0.5.5
       bingo-systems: ^0.5.4
       zod: ^3.24.2
@@ -5776,15 +5776,15 @@ snapshots:
     dependencies:
       '@octokit/types': 13.10.0
 
-  bingo-stratum-testers@0.5.8(bingo-fs@0.5.5)(bingo-stratum@0.5.10(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14)(zod@3.24.2))(bingo-systems@0.5.4)(bingo-testers@0.5.7(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14))(bingo@0.5.14):
+  bingo-stratum-testers@0.5.9(bingo-fs@0.5.5)(bingo-stratum@0.5.11(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14)(zod@3.24.2))(bingo-systems@0.5.4)(bingo-testers@0.5.7(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14))(bingo@0.5.14):
     dependencies:
       bingo: 0.5.14
       bingo-fs: 0.5.5
-      bingo-stratum: 0.5.10(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14)(zod@3.24.2)
+      bingo-stratum: 0.5.11(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14)(zod@3.24.2)
       bingo-systems: 0.5.4
       bingo-testers: 0.5.7(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14)
 
-  bingo-stratum@0.5.10(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14)(zod@3.24.2):
+  bingo-stratum@0.5.11(bingo-fs@0.5.5)(bingo-systems@0.5.4)(bingo@0.5.14)(zod@3.24.2):
     dependencies:
       all-properties-lazy: 0.1.0
       bingo: 0.5.14
diff --git a/src/blocks/blockNcc.ts b/src/blocks/blockNcc.ts
index 39a965e..0e5beaa 100644
--- a/src/blocks/blockNcc.ts
+++ b/src/blocks/blockNcc.ts
@@ -1,3 +1,5 @@
+import { z } from "zod";
+
 import { base } from "../base.js";
 import { blockCSpell } from "./blockCSpell.js";
 import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js";
@@ -10,7 +12,19 @@ export const blockNcc = base.createBlock({
 	about: {
 		name: "ncc",
 	},
-	produce() {
+	addons: {
+		entry: z.string().optional(),
+	},
+	intake({ options }) {
+		return {
+			entry: options.packageData?.scripts?.["build:release"]?.match(
+				/ncc build (.+) -o dist/,
+			)?.[1],
+		};
+	},
+	produce({ addons }) {
+		const { entry = "src/index.ts" } = addons;
+
 		return {
 			addons: [
 				blockCSpell({
@@ -69,12 +83,12 @@ pnpm build:release
 						},
 						scripts: {
 							build: "tsc",
-							"build:release": "ncc build src/index.ts -o dist",
+							"build:release": `ncc build ${entry} -o dist`,
 						},
 					},
 				}),
 				blockPrettier({
-					ignores: ["dist"],
+					ignores: ["/dist"],
 				}),
 			],
 		};


Test patch:
diff --git a/src/blocks/blockNcc.test.ts b/src/blocks/blockNcc.test.ts
index c7c1927..c9b15b4 100644
--- a/src/blocks/blockNcc.test.ts
+++ b/src/blocks/blockNcc.test.ts
@@ -1,11 +1,11 @@
-import { testBlock } from "bingo-stratum-testers";
-import { describe, expect, test } from "vitest";
+import { testBlock, testIntake } from "bingo-stratum-testers";
+import { describe, expect, it, test } from "vitest";
 
 import { blockNcc } from "./blockNcc.js";
 import { optionsBase } from "./options.fakes.js";
 
 describe("blockNcc", () => {
-	test("production", () => {
+	test("without addons", () => {
 		const creation = testBlock(blockNcc, {
 			options: optionsBase,
 		});
@@ -100,6 +100,72 @@ describe("blockNcc", () => {
 			      },
 			      "block": [Function],
 			    },
+			    {
+			      "addons": {
+			        "ignores": [
+			          "/dist",
+			        ],
+			      },
+			      "block": [Function],
+			    },
+			  ],
+			}
+		`);
+	});
+
+	test("with addons", () => {
+		const creation = testBlock(blockNcc, {
+			addons: {
+				entry: "src/action/index.ts",
+			},
+			options: optionsBase,
+		});
+
+		expect(creation).toMatchInlineSnapshot(`
+			{
+			  "addons": [
+			    {
+			      "addons": {
+			        "ignores": [
+			          "dist",
+			        ],
+			      },
+			      "block": [Function],
+			    },
+			    {
+			      "addons": {
+			        "sections": {
+			          "Building": {
+			            "contents": "
+			Run [TypeScript](https://typescriptlang.org) locally to type check and build source files from \`src/\` into output files in \`lib/\`:
+
+			\`\`\`shell
+			pnpm build
+			\`\`\`
+
+			Add \`--watch\` to run the builder in a watch mode that continuously cleans and recreates \`lib/\` as you save files:
+
+			\`\`\`shell
+			pnpm build --watch
+			\`\`\`
+			",
+			            "innerSections": [
+			              {
+			                "contents": "
+			Run [\`@vercel/ncc\`](https://github.com/vercel/ncc) to create an output \`dist/\` to be used in production.
+
+			\`\`\`shell
+			pnpm build:release
+			\`\`\`
+					",
+			                "heading": "Building for Release",
+			              },
+			            ],
+			          },
+			        },
+			      },
+			      "block": [Function],
+			    },
 			    {
 			      "addons": {
 			        "ignores": [
@@ -108,8 +174,125 @@ describe("blockNcc", () => {
 			      },
 			      "block": [Function],
 			    },
+			    {
+			      "addons": {
+			        "jobs": [
+			          {
+			            "name": "Build",
+			            "steps": [
+			              {
+			                "run": "pnpm build",
+			              },
+			            ],
+			          },
+			          {
+			            "name": "Build (Release)",
+			            "steps": [
+			              {
+			                "run": "pnpm build:release",
+			              },
+			            ],
+			          },
+			        ],
+			      },
+			      "block": [Function],
+			    },
+			    {
+			      "addons": {
+			        "properties": {
+			          "devDependencies": {
+			            "@vercel/ncc": "^0.38.3",
+			          },
+			          "scripts": {
+			            "build": "tsc",
+			            "build:release": "ncc build src/action/index.ts -o dist",
+			          },
+			        },
+			      },
+			      "block": [Function],
+			    },
+			    {
+			      "addons": {
+			        "ignores": [
+			          "/dist",
+			        ],
+			      },
+			      "block": [Function],
+			    },
 			  ],
 			}
 		`);
 	});
+
+	describe("intake", () => {
+		it("returns an undefined entry when options.packageData does not exist", () => {
+			const actual = testIntake(blockNcc, {
+				files: {},
+				options: {
+					...optionsBase,
+					packageData: undefined,
+				},
+			});
+
+			expect(actual).toEqual({ entry: undefined });
+		});
+
+		it("returns an undefined entry when options.packageData does not contain scripts", () => {
+			const actual = testIntake(blockNcc, {
+				files: {},
+				options: {
+					...optionsBase,
+					packageData: {},
+				},
+			});
+
+			expect(actual).toEqual({ entry: undefined });
+		});
+
+		it("returns an undefined entry when options.packageData does not contain a build:release script", () => {
+			const actual = testIntake(blockNcc, {
+				files: {},
+				options: {
+					...optionsBase,
+					packageData: {
+						scripts: {},
+					},
+				},
+			});
+
+			expect(actual).toEqual({ entry: undefined });
+		});
+
+		it("returns an undefined entry when options.packageData contains an unrelated build:release script", () => {
+			const actual = testIntake(blockNcc, {
+				files: {},
+				options: {
+					...optionsBase,
+					packageData: {
+						scripts: {
+							"build:release": "tsup",
+						},
+					},
+				},
+			});
+
+			expect(actual).toEqual({ entry: undefined });
+		});
+
+		it("returns a parsed entry when options.packageData contains a matching build:release script", () => {
+			const actual = testIntake(blockNcc, {
+				files: {},
+				options: {
+					...optionsBase,
+					packageData: {
+						scripts: {
+							"build:release": "ncc build src/action/index.ts -o dist",
+						},
+					},
+				},
+			});
+
+			expect(actual).toEqual({ entry: "src/action/index.ts" });
+		});
+	});
 });