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

Issue:
🚀 Feature: Allow not putting a main in package.json
### 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

Turns out not all of my repositories have a `main`! https://github.com/JoshuaKGoldberg/emoji-platform-data doesn't. It just has `exports`.

I'll need to split out a `blockMain` or something like that.

### Additional Info

🎁 

Pull request:
feat: separate out blockMain
## PR Checklist

- [x] Addresses an existing open issue: fixes #2048
- [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

Separates out the concept of a "main" file to into its own `blockMain`. `blockTSup` now takes in `runInCI` as a general list of steps after `pnpm build`.

🎁 

Patch:
diff --git a/docs/Blocks.md b/docs/Blocks.md
index 544da1d..fe0e7a7 100644
--- a/docs/Blocks.md
+++ b/docs/Blocks.md
@@ -31,6 +31,7 @@ This table summarizes each block and which base levels they're included in:
 | GitHub PR Template           | `--exclude-github-pr-template`           | ✔️      | ✅     | 💯         |
 | Gitignore                    | `--exclude-gitignore`                    | ✔️      | ✅     | 💯         |
 | Knip                         | `--exclude-knip`                         |         |        | 💯         |
+| Main                         | `--exclude-main`                         | ✔️      | ✅     | 💯         |
 | Markdownlint                 | `--exclude-markdownlint`                 |         |        | 💯         |
 | MIT License                  | `--exclude-mit-license`                  | ✔️      | ✅     | 💯         |
 | ncc                          | `--exclude-ncc`                          |         |        |            |
diff --git a/src/blocks/blockMain.ts b/src/blocks/blockMain.ts
new file mode 100644
index 0000000..d0ee68f
--- /dev/null
+++ b/src/blocks/blockMain.ts
@@ -0,0 +1,33 @@
+import { z } from "zod";
+
+import { base } from "../base.js";
+import { blockPackageJson } from "./blockPackageJson.js";
+import { blockTSup } from "./blockTSup.js";
+
+export const blockMain = base.createBlock({
+	about: {
+		name: "Main",
+	},
+	addons: {
+		filePath: z.string().optional(),
+		runArgs: z.array(z.string()).default([]),
+	},
+	produce({ addons }) {
+		const { filePath = "lib/index.js", runArgs } = addons;
+
+		return {
+			addons: [
+				blockPackageJson({
+					properties: {
+						main: filePath,
+					},
+				}),
+				blockTSup({
+					runInCI: [
+						`node ${filePath}${runArgs.map((arg) => ` ${arg}`).join("")}`,
+					],
+				}),
+			],
+		};
+	},
+});
diff --git a/src/blocks/blockPackageJson.ts b/src/blocks/blockPackageJson.ts
index 11d5f5a..bc61ea6 100644
--- a/src/blocks/blockPackageJson.ts
+++ b/src/blocks/blockPackageJson.ts
@@ -72,7 +72,6 @@ export const blockPackageJson = base.createBlock({
 								keyword.split(/ /),
 							),
 							license: "MIT",
-							main: "lib/index.js",
 							name: options.repository,
 							repository: {
 								type: "git",
diff --git a/src/blocks/blockTSup.ts b/src/blocks/blockTSup.ts
index 8c06255..7d605fd 100644
--- a/src/blocks/blockTSup.ts
+++ b/src/blocks/blockTSup.ts
@@ -17,7 +17,7 @@ export const blockTSup = base.createBlock({
 	},
 	addons: {
 		entry: z.array(z.string()).default([]),
-		runArgs: z.array(z.string()).default([]),
+		runInCI: z.array(z.string()).default([]),
 	},
 	produce({ addons, options }) {
 		const { entry } = addons;
@@ -52,9 +52,7 @@ pnpm build --watch
 							name: "Build",
 							steps: [
 								{ run: "pnpm build" },
-								{
-									run: `node lib/index.js${addons.runArgs.map((arg) => ` ${arg}`).join("")}`,
-								},
+								...addons.runInCI.map((run) => ({ run })),
 							],
 						},
 					],
diff --git a/src/blocks/blockTypeScript.ts b/src/blocks/blockTypeScript.ts
index 52a84da..17a4447 100644
--- a/src/blocks/blockTypeScript.ts
+++ b/src/blocks/blockTypeScript.ts
@@ -81,7 +81,6 @@ export * from "./types.js";
 					properties: {
 						devDependencies: getPackageDependencies("typescript"),
 						files: ["lib/"],
-						main: "lib/index.js",
 						scripts: {
 							tsc: "tsc",
 						},
diff --git a/src/blocks/index.ts b/src/blocks/index.ts
index e062537..b2b8db0 100644
--- a/src/blocks/index.ts
+++ b/src/blocks/index.ts
@@ -22,6 +22,7 @@ import { blockGitHubIssueTemplates } from "./blockGitHubIssueTemplates.js";
 import { blockGitHubPRTemplate } from "./blockGitHubPRTemplate.js";
 import { blockGitignore } from "./blockGitignore.js";
 import { blockKnip } from "./blockKnip.js";
+import { blockMain } from "./blockMain.js";
 import { blockMarkdownlint } from "./blockMarkdownlint.js";
 import { blockMITLicense } from "./blockMITLicense.js";
 import { blockNcc } from "./blockNcc.js";
@@ -68,6 +69,7 @@ export const blocks = {
 	blockGitHubPRTemplate,
 	blockGitignore,
 	blockKnip,
+	blockMain,
 	blockMarkdownlint,
 	blockMITLicense,
 	blockNcc,
@@ -115,6 +117,7 @@ export { blockGitHubIssueTemplates } from "./blockGitHubIssueTemplates.js";
 export { blockGitHubPRTemplate } from "./blockGitHubPRTemplate.js";
 export { blockGitignore } from "./blockGitignore.js";
 export { blockKnip } from "./blockKnip.js";
+export { blockMain } from "./blockMain.js";
 export { blockMarkdownlint } from "./blockMarkdownlint.js";
 export { blockMITLicense } from "./blockMITLicense.js";
 export { blockNcc } from "./blockNcc.js";
diff --git a/src/presets/minimal.ts b/src/presets/minimal.ts
index 233bf08..4431ae4 100644
--- a/src/presets/minimal.ts
+++ b/src/presets/minimal.ts
@@ -9,6 +9,7 @@ import { blockGitHubApps } from "../blocks/blockGitHubApps.js";
 import { blockGitHubIssueTemplates } from "../blocks/blockGitHubIssueTemplates.js";
 import { blockGitHubPRTemplate } from "../blocks/blockGitHubPRTemplate.js";
 import { blockGitignore } from "../blocks/blockGitignore.js";
+import { blockMain } from "../blocks/blockMain.js";
 import { blockMITLicense } from "../blocks/blockMITLicense.js";
 import { blockPackageJson } from "../blocks/blockPackageJson.js";
 import { blockPrettier } from "../blocks/blockPrettier.js";
@@ -42,6 +43,7 @@ export const presetMinimal = base.createPreset({
 		blockGitHubIssueTemplates,
 		blockGitHubPRTemplate,
 		blockGitignore,
+		blockMain,
 		blockMITLicense,
 		blockPackageJson,
 		blockPrettier,


Test patch:
diff --git a/src/blocks/blockMain.test.ts b/src/blocks/blockMain.test.ts
new file mode 100644
index 0000000..ad0095f
--- /dev/null
+++ b/src/blocks/blockMain.test.ts
@@ -0,0 +1,67 @@
+import { testBlock } from "bingo-stratum-testers";
+import { describe, expect, it } from "vitest";
+
+import { blockMain } from "./blockMain.js";
+import { optionsBase } from "./options.fakes.js";
+
+describe("blockMain", () => {
+	it("without addons", () => {
+		const creation = testBlock(blockMain, { options: optionsBase });
+
+		expect(creation).toMatchInlineSnapshot(`
+			{
+			  "addons": [
+			    {
+			      "addons": {
+			        "properties": {
+			          "main": "lib/index.js",
+			        },
+			      },
+			      "block": [Function],
+			    },
+			    {
+			      "addons": {
+			        "runInCI": [
+			          "node lib/index.js",
+			        ],
+			      },
+			      "block": [Function],
+			    },
+			  ],
+			}
+		`);
+	});
+
+	it("with addons", () => {
+		const creation = testBlock(blockMain, {
+			addons: {
+				filePath: "other.js",
+				runArgs: ["--version"],
+			},
+			options: optionsBase,
+		});
+
+		expect(creation).toMatchInlineSnapshot(`
+			{
+			  "addons": [
+			    {
+			      "addons": {
+			        "properties": {
+			          "main": "other.js",
+			        },
+			      },
+			      "block": [Function],
+			    },
+			    {
+			      "addons": {
+			        "runInCI": [
+			          "node other.js --version",
+			        ],
+			      },
+			      "block": [Function],
+			    },
+			  ],
+			}
+		`);
+	});
+});
diff --git a/src/blocks/blockPackageJson.test.ts b/src/blocks/blockPackageJson.test.ts
index 4bf7135..81589f8 100644
--- a/src/blocks/blockPackageJson.test.ts
+++ b/src/blocks/blockPackageJson.test.ts
@@ -17,7 +17,7 @@ describe("blockPackageJson", () => {
 		expect(creation).toMatchInlineSnapshot(`
 			{
 			  "files": {
-			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","main":"lib/index.js","files":["README.md","package.json"]}",
+			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","files":["README.md","package.json"]}",
 			  },
 			  "scripts": [
 			    {
@@ -50,7 +50,7 @@ describe("blockPackageJson", () => {
 			    },
 			  ],
 			  "files": {
-			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","main":"lib/index.js","files":["README.md","package.json"]}",
+			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","files":["README.md","package.json"]}",
 			  },
 			  "scripts": [
 			    {
@@ -81,7 +81,7 @@ describe("blockPackageJson", () => {
 		expect(creation).toMatchInlineSnapshot(`
 			{
 			  "files": {
-			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","main":"lib/index.js","files":["README.md","package.json"],"dependencies":{"is-odd":"1.2.3"},"other":true}",
+			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","files":["README.md","package.json"],"dependencies":{"is-odd":"1.2.3"},"other":true}",
 			  },
 			  "scripts": [
 			    {
@@ -116,7 +116,7 @@ describe("blockPackageJson", () => {
 		expect(creation).toMatchInlineSnapshot(`
 			{
 			  "files": {
-			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","main":"lib/index.js","files":["README.md","package.json"],"dependencies":{"is-odd":"1.2.3"},"devDependencies":{"is-even":"4.5.6"},"other":true}",
+			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","files":["README.md","package.json"],"dependencies":{"is-odd":"1.2.3"},"devDependencies":{"is-even":"4.5.6"},"other":true}",
 			  },
 			  "scripts": [
 			    {
@@ -142,7 +142,7 @@ describe("blockPackageJson", () => {
 		expect(creation).toMatchInlineSnapshot(`
 			{
 			  "files": {
-			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","keywords":["abc","def","ghi"],"repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","main":"lib/index.js","files":["README.md","package.json"]}",
+			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","keywords":["abc","def","ghi"],"repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","files":["README.md","package.json"]}",
 			  },
 			  "scripts": [
 			    {
@@ -170,7 +170,7 @@ describe("blockPackageJson", () => {
 		expect(creation).toMatchInlineSnapshot(`
 			{
 			  "files": {
-			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","main":"lib/index.js","files":["README.md","package.json"],"packageManager":"pnpm@10.4.0","engines":{"node":">=22.0.0"}}",
+			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","files":["README.md","package.json"],"packageManager":"pnpm@10.4.0","engines":{"node":">=22.0.0"}}",
 			  },
 			  "scripts": [
 			    {
@@ -195,7 +195,7 @@ describe("blockPackageJson", () => {
 		expect(creation).toMatchInlineSnapshot(`
 			{
 			  "files": {
-			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","main":"lib/index.js","bin":"bin/index.js","files":["README.md","bin/index.js","package.json"]}",
+			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","bin":"bin/index.js","files":["README.md","bin/index.js","package.json"]}",
 			  },
 			  "scripts": [
 			    {
@@ -223,7 +223,7 @@ describe("blockPackageJson", () => {
 		expect(creation).toMatchInlineSnapshot(`
 			{
 			  "files": {
-			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","main":"lib/index.js","bin":{"absolute":"bin/absolute.js","relative":"./bin/relative.js"},"files":["README.md","bin/absolute.js","bin/relative.js","package.json"]}",
+			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","bin":{"absolute":"bin/absolute.js","relative":"./bin/relative.js"},"files":["README.md","bin/absolute.js","bin/relative.js","package.json"]}",
 			  },
 			  "scripts": [
 			    {
@@ -246,7 +246,7 @@ describe("blockPackageJson", () => {
 		expect(creation).toMatchInlineSnapshot(`
 			{
 			  "files": {
-			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","main":"lib/index.js","files":["README.md","package.json"]}",
+			    "package.json": "{"name":"test-repository","version":"0.0.0","description":"A very very very very very very very very very very very very very very very very long HTML-ish description ending with an emoji. 🧵","repository":{"type":"git","url":"git+https://github.com/test-owner/test-repository.git"},"license":"MIT","author":{"email":"npm@email.com"},"type":"module","files":["README.md","package.json"]}",
 			  },
 			  "scripts": [
 			    {
diff --git a/src/blocks/blockTSup.test.ts b/src/blocks/blockTSup.test.ts
index e567b0d..fa24c7a 100644
--- a/src/blocks/blockTSup.test.ts
+++ b/src/blocks/blockTSup.test.ts
@@ -54,9 +54,6 @@ describe("blockTSup", () => {
 			              {
 			                "run": "pnpm build",
 			              },
-			              {
-			                "run": "node lib/index.js",
-			              },
 			            ],
 			          },
 			        ],
@@ -141,9 +138,6 @@ describe("blockTSup", () => {
 			              {
 			                "run": "pnpm build",
 			              },
-			              {
-			                "run": "node lib/index.js",
-			              },
 			            ],
 			          },
 			        ],
@@ -260,9 +254,6 @@ describe("blockTSup", () => {
 			              {
 			                "run": "pnpm build",
 			              },
-			              {
-			                "run": "node lib/index.js",
-			              },
 			            ],
 			          },
 			        ],
diff --git a/src/blocks/blockTypeScript.test.ts b/src/blocks/blockTypeScript.test.ts
index 62928b8..0af3ff9 100644
--- a/src/blocks/blockTypeScript.test.ts
+++ b/src/blocks/blockTypeScript.test.ts
@@ -107,7 +107,6 @@ describe("blockTypeScript", () => {
 			          "files": [
 			            "lib/",
 			          ],
-			          "main": "lib/index.js",
 			          "scripts": {
 			            "tsc": "tsc",
 			          },
@@ -258,7 +257,6 @@ describe("blockTypeScript", () => {
 			          "files": [
 			            "lib/",
 			          ],
-			          "main": "lib/index.js",
 			          "scripts": {
 			            "tsc": "tsc",
 			          },
@@ -418,7 +416,6 @@ describe("blockTypeScript", () => {
 			          "files": [
 			            "lib/",
 			          ],
-			          "main": "lib/index.js",
 			          "scripts": {
 			            "tsc": "tsc",
 			          },
diff --git a/src/integration.test.ts b/src/integration.test.ts
index d6b3c19..0ffcf73 100644
--- a/src/integration.test.ts
+++ b/src/integration.test.ts
@@ -13,8 +13,8 @@ import {
 	blockCSpell,
 	blockESLint,
 	blockKnip,
+	blockMain,
 	blockTemplatedWith,
-	blockTSup,
 	presets,
 } from "./index.js";
 
@@ -103,7 +103,7 @@ If you're interested in learning more, see the 'getting started' docs on:
 						"trash-cli",
 					],
 				}),
-				blockTSup({
+				blockMain({
 					runArgs: ["--version"],
 				}),
 			],