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

Issue:
🐛 Bug: Transition mode crashes on major-version-only semver identifier
### Bug Report Checklist

- [x] I have tried restarting my IDE and the issue persists.
- [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.

### Expected

Running `npx create-typescript-app` on a repo whose `package.json` contains partial semver identifiers should work:

```js
{
	"dependencies": {
		"debug": ">=4"
	},
}
```

### Actual

Running on https://github.com/JoshuaKGoldberg/debug-for-file/tree/017f4f35803c2fb970acf6236b110ed051fde005:

```plaintext
joshgoldberg ~/repos/debug-for-file $ npx create-typescript-app --add-cta-transitions
┌  ✨ create-typescript-app@2.15.6 ✨
│
●  Learn more on:
│    https://github.com/JoshuaKGoldberg/create-typescript-app
│
◇  Running with mode --transition using the config file:
│    create-typescript-app
│
│
◇  Detected --preset everything from existing files on disk.
│  
◇  Inferred options from existing repository
│
■  Error running create-typescript-app:
│
■  TypeError: Invalid Version: 4
│      at new SemVer (/Users/josh/repos/debug-for-file/node_modules/.pnpm/semver@7.7.1/node_modules/semver/classes/semver.js:38:13)
│      at compare (/Users/josh/repos/debug-for-file/node_modules/.pnpm/semver@7.7.1/node_modules/semver/functions/compare.js:3:3)
│      at Object.gt (/Users/josh/repos/debug-for-file/node_modules/.pnpm/semver@7.7.1/node_modules/semver/functions/gt.js:2:29)
│      at useLargerVersion (file:///Users/josh/repos/debug-for-file/node_modules/.pnpm/create-typescript-app@2.15.6_bingo-systems@0.5.4/node_modules/create-typescript-app/lib/blocks/blockPackageJson.js:113:19)
│      at file:///Users/josh/repos/debug-for-file/node_modules/.pnpm/create-typescript-app@2.15.6_bingo-systems@0.5.4/node_modules/create-typescript-app/lib/blocks/blockPackageJson.js:126:7
│      at Array.map (<anonymous>)
│      at useLargerVersions (file:///Users/josh/repos/debug-for-file/node_modules/.pnpm/create-typescript-app@2.15.6_bingo-systems@0.5.4/node_modules/create-typescript-app/lib/blocks/blockPackageJson.js:124:34)
│      at Object.produce (file:///Users/josh/repos/debug-for-file/node_modules/.pnpm/create-typescript-app@2.15.6_bingo-systems@0.5.4/node_modules/create-typescript-app/lib/blocks/blockPackageJson.js:22:26)
│      at block.produce (file:///Users/josh/repos/debug-for-file/node_modules/.pnpm/bingo-stratum@0.5.7_bingo-fs@0.5.4_bingo-systems@0.5.4_bingo@0.5.10_zod@3.24.2/node_modules/bingo-stratum/lib/creators/createBlock.js:15:32)
│      at produceBlock (file:///Users/josh/repos/debug-for-file/node_modules/.pnpm/bingo-stratum@0.5.7_bingo-fs@0.5.4_bingo-systems@0.5.4_bingo@0.5.10_zod@3.24.2/node_modules/bingo-stratum/lib/producers/produceBlock.js:9:26)
│
└  Leaving changes to the local directory on disk. 👋
```

### Additional Info

This comes from `useLargerVersion`. `existing` and `replacement` are both `>=4`.

🎁 

Pull request:
fix: coerce dependency versions in semver parsing
## PR Checklist

- [x] Addresses an existing open issue: fixes #2043
- [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

I couldn't figure out how to get the exact failure case from #2043, weirdly enough. But this PR's invalid semver handling happens to fix that failure in `debug-for-file` locally for me.

🎁 

Patch:
diff --git a/src/blocks/blockPackageJson.ts b/src/blocks/blockPackageJson.ts
index 0b5bad8..e94b393 100644
--- a/src/blocks/blockPackageJson.ts
+++ b/src/blocks/blockPackageJson.ts
@@ -119,15 +119,20 @@ function collectBinFiles(bin: Record<string, string> | string | undefined) {
 }
 
 function removeRangePrefix(version: string) {
-	return version.replaceAll(/[\^~><=]/gu, "").split(" ")[0];
+	const raw = version.replaceAll(/[\^~><=]/gu, "").split(" ")[0];
+
+	return semver.coerce(raw) ?? raw;
 }
 
 function useLargerVersion(existing: string | undefined, replacement: string) {
-	if (!existing) {
+	if (!existing || existing === replacement) {
 		return replacement;
 	}
 
-	return semver.gt(removeRangePrefix(existing), removeRangePrefix(replacement))
+	const existingCoerced = semver.coerce(removeRangePrefix(existing));
+
+	return existingCoerced &&
+		semver.gt(existingCoerced, removeRangePrefix(replacement))
 		? existing
 		: replacement;
 }


Test patch:
diff --git a/src/blocks/blockPackageJson.test.ts b/src/blocks/blockPackageJson.test.ts
index 58bcf73..4bf7135 100644
--- a/src/blocks/blockPackageJson.test.ts
+++ b/src/blocks/blockPackageJson.test.ts
@@ -260,7 +260,7 @@ describe("blockPackageJson", () => {
 		`);
 	});
 
-	it("preserves an existing dependency when the addon has an older pinned version", () => {
+	it("preserves an existing dependency when the addon has an invalid version", () => {
 		const dependency = "test-dependency";
 		const creation = testBlock(blockPackageJson, {
 			addons: {
@@ -318,6 +318,36 @@ describe("blockPackageJson", () => {
 		`);
 	});
 
+	it("uses the addon's version when the existing equivalent is invalid semver", () => {
+		const dependency = "test-dependency";
+		const creation = testBlock(blockPackageJson, {
+			addons: {
+				properties: {
+					dependencies: {
+						[dependency]: "1.0.0",
+					},
+				},
+			},
+			options: {
+				...optionsBase,
+				packageData: {
+					dependencies: {
+						[dependency]: "next",
+					},
+				},
+			},
+		});
+
+		expect(
+			// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unsafe-member-access
+			JSON.parse(creation.files!["package.json"] as string).dependencies,
+		).toMatchInlineSnapshot(`
+			{
+			  "test-dependency": "1.0.0",
+			}
+		`);
+	});
+
 	it("preserves an existing dependency when the addon has an older version minimum", () => {
 		const dependency = "test-dependency";
 		const creation = testBlock(blockPackageJson, {