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

Issue:
🚀 Feature: Add prettier-plugin-sh
### 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.

### Overview

As of https://github.com/un-ts/prettier/issues/340 -> https://github.com/un-ts/prettier/pull/341, `prettier-plugin-sh` can work on `.nvmrc`. That means it can replace the last `overrides` in `.prettierrc.json`!

https://github.com/JoshuaKGoldberg/create-typescript-app/blob/07c0abcee146346ba819ab444ddad298fb698c2a/.prettierrc.json#L3

Let's add that in. It's also a generally useful plugin for expanding the range of file extensions / syntax Prettier will format.

### Additional Info

_No response_

Pull request:
feat: add prettier-plugin-sh
## PR Checklist

- [x] Addresses an existing open issue: fixes #1263
- [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 plugin in, similar to the existing `prettier-plugin-curly` and `prettier-plugin-packagejson` plugins.

💖 

Patch:
diff --git a/.github/DEVELOPMENT.md b/.github/DEVELOPMENT.md
index 3fa32a0..30091f4 100644
--- a/.github/DEVELOPMENT.md
+++ b/.github/DEVELOPMENT.md
@@ -6,7 +6,7 @@
 After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo) and [installing pnpm](https://pnpm.io/installation):
 
 ```shell
-git clone https://github.com/<your-name-here>/create-typescript-app
+git clone https://github.com/ < your-name-here > /create-typescript-app
 cd create-typescript-app
 pnpm install
 ```
diff --git a/.prettierignore b/.prettierignore
index 613eedd..608dd8d 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,4 +1,5 @@
 .all-contributorsrc
+.husky/
 coverage*/
 lib/
 pnpm-lock.yaml
diff --git a/.prettierrc.json b/.prettierrc.json
index e3efe60..5b0fab8 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -1,6 +1,10 @@
 {
 	"$schema": "http://json.schemastore.org/prettierrc",
 	"overrides": [{ "files": ".nvmrc", "options": { "parser": "yaml" } }],
-	"plugins": ["prettier-plugin-curly", "prettier-plugin-packagejson"],
+	"plugins": [
+		"prettier-plugin-curly",
+		"prettier-plugin-sh",
+		"prettier-plugin-packagejson"
+	],
 	"useTabs": true
 }
diff --git a/docs/Tooling.md b/docs/Tooling.md
index 3b8ddc2..bb8cb7f 100644
--- a/docs/Tooling.md
+++ b/docs/Tooling.md
@@ -83,7 +83,7 @@ pnpm run build --watch
 
 [**Prettier**](https://prettier.io): Formats code for developers and enforces a consistent formatting style.
 It's run on file save per [VS Code](https://code.visualstudio.com/docs/getstarted/settings) settings and as a Git commit hook via [husky](https://typicode.github.io/husky) and [lint-staged](https://github.com/okonet/lint-staged).
-[prettier-plugin-curly](https://github.com/JoshuaKGoldberg/prettier-plugin-curly) and [prettier-plugin-packagejson](https://github.com/matzkoh/prettier-plugin-packagejson) add in more formatting as well.
+[prettier-plugin-curly](https://github.com/JoshuaKGoldberg/prettier-plugin-curly), [prettier-plugin-sh](https://github.com/un-ts/prettier/tree/master/packages/sh), and [prettier-plugin-packagejson](https://github.com/matzkoh/prettier-plugin-packagejson) add in more formatting as well.
 
 Auto-formatting all files:
 
diff --git a/package.json b/package.json
index 390628b..00ff574 100644
--- a/package.json
+++ b/package.json
@@ -93,6 +93,7 @@
 		"markdownlint-cli": "^0.41.0",
 		"prettier-plugin-curly": "^0.2.0",
 		"prettier-plugin-packagejson": "^2.5.0",
+		"prettier-plugin-sh": "^0.14.0",
 		"release-it": "^17.3.0",
 		"sentences-per-line": "^0.2.1",
 		"tsup": "^8.1.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index daa6f4c..1de755a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -159,6 +159,9 @@ importers:
       prettier-plugin-packagejson:
         specifier: ^2.5.0
         version: 2.5.0(prettier@3.1.0)
+      prettier-plugin-sh:
+        specifier: ^0.14.0
+        version: 0.14.0(prettier@3.1.0)
       release-it:
         specifier: ^17.3.0
         version: 17.3.0(typescript@5.4.3)
@@ -3418,6 +3421,9 @@ packages:
     resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
+  mvdan-sh@0.10.1:
+    resolution: {integrity: sha512-kMbrH0EObaKmK3nVRKUIIya1dpASHIEusM13S4V1ViHFuxuNxCo+arxoa6j/dbV22YBGjl7UKJm9QQKJ2Crzhg==}
+
   mz@2.7.0:
     resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
 
@@ -3735,6 +3741,12 @@ packages:
       prettier:
         optional: true
 
+  prettier-plugin-sh@0.14.0:
+    resolution: {integrity: sha512-hfXulj5+zEl/ulrO5kMuuTPKmXvOg0bnLHY1hKFNN/N+/903iZbNp8NyZBTsgI8dtkSgFfAEIQq0IQTyP1ZVFQ==}
+    engines: {node: '>=16.0.0'}
+    peerDependencies:
+      prettier: ^3.0.3
+
   prettier@3.1.0:
     resolution: {integrity: sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==}
     engines: {node: '>=14'}
@@ -3964,6 +3976,10 @@ packages:
     resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
     engines: {node: '>= 0.4'}
 
+  sh-syntax@0.4.2:
+    resolution: {integrity: sha512-/l2UZ5fhGZLVZa16XQM9/Vq/hezGGbdHeVEA01uWjOL1+7Ek/gt6FquW0iKKws4a9AYPYvlz6RyVvjh3JxOteg==}
+    engines: {node: '>=16.0.0'}
+
   shebang-command@2.0.0:
     resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
     engines: {node: '>=8'}
@@ -8077,6 +8093,8 @@ snapshots:
 
   mute-stream@1.0.0: {}
 
+  mvdan-sh@0.10.1: {}
+
   mz@2.7.0:
     dependencies:
       any-promise: 1.3.0
@@ -8429,6 +8447,12 @@ snapshots:
     optionalDependencies:
       prettier: 3.1.0
 
+  prettier-plugin-sh@0.14.0(prettier@3.1.0):
+    dependencies:
+      mvdan-sh: 0.10.1
+      prettier: 3.1.0
+      sh-syntax: 0.4.2
+
   prettier@3.1.0: {}
 
   pretty-format@29.7.0:
@@ -8715,6 +8739,10 @@ snapshots:
       functions-have-names: 1.2.3
       has-property-descriptors: 1.0.2
 
+  sh-syntax@0.4.2:
+    dependencies:
+      tslib: 2.6.2
+
   shebang-command@2.0.0:
     dependencies:
       shebang-regex: 3.0.0
diff --git a/src/steps/finalizeDependencies.ts b/src/steps/finalizeDependencies.ts
index 3dbd696..42b4f0c 100644
--- a/src/steps/finalizeDependencies.ts
+++ b/src/steps/finalizeDependencies.ts
@@ -16,6 +16,7 @@ export async function finalizeDependencies(options: Options) {
 		"lint-staged",
 		"prettier",
 		"prettier-plugin-curly",
+		"prettier-plugin-sh",
 		"prettier-plugin-packagejson",
 		"tsup",
 		"typescript",
diff --git a/src/steps/writing/creation/rootFiles.ts b/src/steps/writing/creation/rootFiles.ts
index a341861..36ab969 100644
--- a/src/steps/writing/creation/rootFiles.ts
+++ b/src/steps/writing/creation/rootFiles.ts
@@ -28,6 +28,7 @@ export async function createRootFiles(options: Options) {
 		".nvmrc": `20.12.2\n`,
 		".prettierignore": formatIgnoreFile([
 			...(options.excludeAllContributors ? [] : [".all-contributorsrc"]),
+			".husky/",
 			...(options.excludeTests ? [] : ["coverage/"]),
 			"lib/",
 			"pnpm-lock.yaml",
@@ -40,7 +41,11 @@ export async function createRootFiles(options: Options) {
 					options: { parser: "yaml" },
 				},
 			],
-			plugins: ["prettier-plugin-curly", "prettier-plugin-packagejson"],
+			plugins: [
+				"prettier-plugin-curly",
+				"prettier-plugin-sh",
+				"prettier-plugin-packagejson",
+			],
 			useTabs: true,
 		}),
 		...(!options.excludeReleases && {


Test patch:
diff --git a/script/__snapshots__/migrate-test-e2e.ts.snap b/script/__snapshots__/migrate-test-e2e.ts.snap
index cef671e..37f5a1c 100644
--- a/script/__snapshots__/migrate-test-e2e.ts.snap
+++ b/script/__snapshots__/migrate-test-e2e.ts.snap
@@ -29,6 +29,7 @@ exports[`expected file changes > .prettierignore 1`] = `
 +++ b/.prettierignore
 @@ ... @@
  .all-contributorsrc
+ .husky/
 -coverage*/
 +coverage/
  lib/
diff --git a/src/steps/finalizeDependencies.test.ts b/src/steps/finalizeDependencies.test.ts
index 95e4879..05d2ed5 100644
--- a/src/steps/finalizeDependencies.test.ts
+++ b/src/steps/finalizeDependencies.test.ts
@@ -38,7 +38,7 @@ describe("finalize", () => {
 		expect(mockExecaCommand.mock.calls).toMatchInlineSnapshot(`
 			[
 			  [
-			    "pnpm add @eslint-community/eslint-plugin-eslint-comments@latest @eslint/js@latest @release-it/conventional-changelog@latest @types/eslint-plugin-markdown@latest @vitest/coverage-v8@latest all-contributors-cli@latest console-fail-test@latest cspell@latest eslint@latest eslint-plugin-jsdoc@latest eslint-plugin-jsonc@latest eslint-plugin-markdown@latest eslint-plugin-n@latest eslint-plugin-package-json@latest eslint-plugin-perfectionist@latest eslint-plugin-regexp@latest eslint-plugin-vitest@latest eslint-plugin-yml@latest husky@latest jsonc-eslint-parser@latest knip@latest lint-staged@latest markdownlint@latest markdownlint-cli@latest prettier@latest prettier-plugin-curly@latest prettier-plugin-packagejson@latest release-it@latest sentences-per-line@latest tsup@latest typescript@latest typescript-eslint@latest vitest@latest -D",
+			    "pnpm add @eslint-community/eslint-plugin-eslint-comments@latest @eslint/js@latest @release-it/conventional-changelog@latest @types/eslint-plugin-markdown@latest @vitest/coverage-v8@latest all-contributors-cli@latest console-fail-test@latest cspell@latest eslint@latest eslint-plugin-jsdoc@latest eslint-plugin-jsonc@latest eslint-plugin-markdown@latest eslint-plugin-n@latest eslint-plugin-package-json@latest eslint-plugin-perfectionist@latest eslint-plugin-regexp@latest eslint-plugin-vitest@latest eslint-plugin-yml@latest husky@latest jsonc-eslint-parser@latest knip@latest lint-staged@latest markdownlint@latest markdownlint-cli@latest prettier@latest prettier-plugin-curly@latest prettier-plugin-packagejson@latest prettier-plugin-sh@latest release-it@latest sentences-per-line@latest tsup@latest typescript@latest typescript-eslint@latest vitest@latest -D",
 			  ],
 			  [
 			    "npx all-contributors-cli generate",
@@ -59,7 +59,7 @@ describe("finalize", () => {
 		expect(mockExecaCommand.mock.calls).toMatchInlineSnapshot(`
 			[
 			  [
-			    "pnpm add @eslint-community/eslint-plugin-eslint-comments@latest @eslint/js@latest @release-it/conventional-changelog@latest @types/eslint-plugin-markdown@latest @vitest/coverage-v8@latest all-contributors-cli@latest console-fail-test@latest cspell@latest eslint@latest eslint-plugin-jsdoc@latest eslint-plugin-jsonc@latest eslint-plugin-markdown@latest eslint-plugin-n@latest eslint-plugin-package-json@latest eslint-plugin-perfectionist@latest eslint-plugin-regexp@latest eslint-plugin-vitest@latest eslint-plugin-yml@latest husky@latest jsonc-eslint-parser@latest knip@latest lint-staged@latest markdownlint@latest markdownlint-cli@latest prettier@latest prettier-plugin-curly@latest prettier-plugin-packagejson@latest release-it@latest sentences-per-line@latest tsup@latest typescript@latest typescript-eslint@latest vitest@latest -D --offline",
+			    "pnpm add @eslint-community/eslint-plugin-eslint-comments@latest @eslint/js@latest @release-it/conventional-changelog@latest @types/eslint-plugin-markdown@latest @vitest/coverage-v8@latest all-contributors-cli@latest console-fail-test@latest cspell@latest eslint@latest eslint-plugin-jsdoc@latest eslint-plugin-jsonc@latest eslint-plugin-markdown@latest eslint-plugin-n@latest eslint-plugin-package-json@latest eslint-plugin-perfectionist@latest eslint-plugin-regexp@latest eslint-plugin-vitest@latest eslint-plugin-yml@latest husky@latest jsonc-eslint-parser@latest knip@latest lint-staged@latest markdownlint@latest markdownlint-cli@latest prettier@latest prettier-plugin-curly@latest prettier-plugin-packagejson@latest prettier-plugin-sh@latest release-it@latest sentences-per-line@latest tsup@latest typescript@latest typescript-eslint@latest vitest@latest -D --offline",
 			  ],
 			  [
 			    "npx all-contributors-cli generate",
@@ -92,7 +92,7 @@ describe("finalize", () => {
 		expect(mockExecaCommand.mock.calls).toMatchInlineSnapshot(`
 			[
 			  [
-			    "pnpm add @eslint-community/eslint-plugin-eslint-comments@latest @eslint/js@latest @types/eslint-plugin-markdown@latest eslint@latest eslint-plugin-jsdoc@latest eslint-plugin-n@latest eslint-plugin-regexp@latest husky@latest lint-staged@latest prettier@latest prettier-plugin-curly@latest prettier-plugin-packagejson@latest tsup@latest typescript@latest typescript-eslint@latest -D",
+			    "pnpm add @eslint-community/eslint-plugin-eslint-comments@latest @eslint/js@latest @types/eslint-plugin-markdown@latest eslint@latest eslint-plugin-jsdoc@latest eslint-plugin-n@latest eslint-plugin-regexp@latest husky@latest lint-staged@latest prettier@latest prettier-plugin-curly@latest prettier-plugin-packagejson@latest prettier-plugin-sh@latest tsup@latest typescript@latest typescript-eslint@latest -D",
 			  ],
 			  [
 			    "pnpm dedupe",