Skip to content

Commit 1e5c572

Browse files
feat: added a 'common' config base, with more docs (#849)
## PR Checklist - [x] Addresses an existing open issue: fixes #835 - [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 Describes the new "common" base as what I imagine most repositories would want, and gives it the `hint: "recommended"` instead of the "everything" base. And adds some more test coverage to `augmentOptionsWithExcludes`. I think Knip might technically belong in the "everything" base per the current descriptions... but gosh darn is it so useful. I wonder what's better: including it in "common" so everyone gets the benefit? Or pushing it to "everything" so as a reward for trying that one out? For now I think the former is more likely to be beneficial. ```plaintext │ ◆ How much tooling would you like the template to set up for you? │ ○ everything The most comprehensive tooling imaginable: sorting, spellchecking, and more! │ ● essentials Bare starters plus testing and automation for all-contributors and releases. (recommended) │ ○ minimum Just bare starter tooling: building, formatting, linting, and type checking. │ ○ prompt (allow me to customize) └ ```
1 parent 00b7e2e commit 1e5c572

File tree

7 files changed

+123
-15
lines changed

7 files changed

+123
-15
lines changed

.github/DEVELOPMENT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ As described in the `README.md` file and `docs/`, this template repository comes
9393

9494
Each follows roughly the same general flow:
9595

96-
1. `bin/index.ts` uses `bin/mode.ts` determines which of the three setup scripts to run
96+
1. `bin/index.ts` uses `bin/mode.ts` to determine which of the three setup scripts to run
9797
2. `readOptions` parses in options from local files, Git commands, npm APIs, and/or files on disk
9898
3. `runOrRestore` wraps the setup script's main logic in a friendly prompt wrapper
9999
4. The setup script wraps each portion of its main logic with `withSpinner`

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
</p>
2828

2929
Note that this template is early stage, opinionated, and not endorsed by the TypeScript team.
30-
It sets up a _lot_ of tooling out of the box.
30+
It can be configured to set up a _lot_ of tooling out of the box.
3131
Each of the included tools exists for a good reason and provides real value.
3232

3333
If you don't want to use any particular tool, you can always remove it manually.
@@ -50,7 +50,7 @@ Use the corresponding docs page to get started:
5050
## Explainer
5151

5252
This template is available for anybody who wants to set up a Node application using TypeScript.
53-
It sets up the following tooling for you:
53+
It can set up the following tooling for you:
5454

5555
- [**All Contributors**](https://allcontributors.org): Tracks various kinds of contributions and displays them in a nicely formatted table in the README.md.
5656
- [**ESLint**](https://eslint.org): Static analysis for JavaScript code, configured with [typescript-eslint](https://typescript-eslint.io) for TypeScript code and other general-use plugins.

docs/FAQs.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ Each of the included tools exists for a good reason and provides real value.
2121

2222
If you don't want to use any particular tool, you can always remove it manually.
2323

24+
### What determines which "base" a tool goes into?
25+
26+
The four bases correspond to what have seemed to be the most common user needs of template consumers:
27+
28+
1. **Minimum**: Developers who just want the barest of starting templates.
29+
- They may be very new to TypeScript tooling, or they may have made an informed decision that the additional tooling isn't worth the complexity and/or time investment.
30+
- Tooling in this base is only what would be essential for a small TypeScript package that can be built, formatted, and linted.
31+
2. **Common**: The common case of users who want the minimum tooling along with common repository management.
32+
- Tooling added in this base should be essential for a TypeScript repository that additionally automates useful GitHub tasks: contributor recognition, release management, and testing.
33+
3. **Everything**: Power users (including this repository) who want as much of the latest and greatest safety checks and standardization as possible.
34+
35+
Note that users can always customize exactly with portions are kept in with `--base` **`prompt`**.
36+
2437
### Which tools can't I remove?
2538

2639
The following pieces of this template's tooling don't have options to be removed:

src/shared/options/augmentOptionsWithExcludes.test.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,54 @@ describe("augmentOptionsWithExcludes", () => {
4545
expect(actual).toBe(options);
4646
});
4747

48-
it("uses base without prompting when base is provided manually", async () => {
48+
it("uses the 'common' base without prompting when provided manually", async () => {
49+
const options = {
50+
...optionsBase,
51+
base: "common",
52+
} satisfies Options;
53+
54+
const actual = await augmentOptionsWithExcludes(options);
55+
56+
expect(actual).toEqual({
57+
...options,
58+
excludeCompliance: true,
59+
excludeLintJson: true,
60+
excludeLintMd: true,
61+
excludeLintPackageJson: true,
62+
excludeLintPackages: true,
63+
excludeLintPerfectionist: true,
64+
excludeLintSpelling: true,
65+
excludeLintYml: true,
66+
});
67+
});
68+
69+
it("uses the 'minimum' base without prompting when provided manually", async () => {
70+
const options = {
71+
...optionsBase,
72+
base: "minimum",
73+
} satisfies Options;
74+
75+
const actual = await augmentOptionsWithExcludes(options);
76+
77+
expect(actual).toEqual({
78+
...options,
79+
excludeCompliance: true,
80+
excludeContributors: true,
81+
excludeLintJson: true,
82+
excludeLintKnip: true,
83+
excludeLintMd: true,
84+
excludeLintPackageJson: true,
85+
excludeLintPackages: true,
86+
excludeLintPerfectionist: true,
87+
excludeLintSpelling: true,
88+
excludeLintYml: true,
89+
excludeReleases: true,
90+
excludeRenovate: true,
91+
excludeTests: true,
92+
});
93+
});
94+
95+
it("uses the 'everything' base without prompting when provided manually", async () => {
4996
const options = {
5097
...optionsBase,
5198
base: "everything",

src/shared/options/augmentOptionsWithExcludes.ts

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
import * as prompts from "@clack/prompts";
2+
import chalk from "chalk";
23

34
import { filterPromptCancel } from "../prompts.js";
45
import { InputBase, Options } from "../types.js";
56

6-
const exclusionDescriptions = {
7+
interface ExclusionDescription {
8+
hint: string;
9+
label: string;
10+
uncommon?: true;
11+
}
12+
13+
type ExclusionKey = keyof Options & `exclude${string}`;
14+
15+
const exclusionDescriptions: Record<ExclusionKey, ExclusionDescription> = {
716
excludeCompliance: {
817
hint: "--exclude-compliance",
918
label:
1019
"Add a GitHub Actions workflow to verify that PRs match an expected format.",
20+
uncommon: true,
1121
},
1222
excludeContributors: {
1323
hint: "--exclude-contributors",
@@ -17,6 +27,7 @@ const exclusionDescriptions = {
1727
excludeLintJson: {
1828
hint: "--exclude-lint-json",
1929
label: "Apply linting and sorting to *.json and *.jsonc files.",
30+
uncommon: true,
2031
},
2132
excludeLintKnip: {
2233
hint: "--exclude-lint-knip",
@@ -25,28 +36,34 @@ const exclusionDescriptions = {
2536
excludeLintMd: {
2637
hint: "--exclude-lint-md",
2738
label: "Apply linting to *.md files.",
39+
uncommon: true,
2840
},
2941
excludeLintPackageJson: {
3042
hint: "--exclude-lint-package-json",
3143
label: "Add npm-package-json-lint to lint for package.json correctness.",
44+
uncommon: true,
3245
},
3346
excludeLintPackages: {
3447
hint: "--exclude-lint-packages",
3548
label:
3649
"Add a pnpm dedupe workflow to ensure packages aren't duplicated unnecessarily.",
50+
uncommon: true,
3751
},
3852
excludeLintPerfectionist: {
3953
hint: "--exclude-lint-perfectionist",
4054
label:
4155
"Apply eslint-plugin-perfectionist to ensure imports, keys, and so on are in sorted order.",
56+
uncommon: true,
4257
},
4358
excludeLintSpelling: {
4459
hint: "--exclude-lint-spelling",
4560
label: "Add cspell to spell check against dictionaries of known words.",
61+
uncommon: true,
4662
},
4763
excludeLintYml: {
4864
hint: "--exclude-lint-yml",
4965
label: "Apply linting and sorting to *.yaml and *.yml files.",
66+
uncommon: true,
5067
},
5168
excludeReleases: {
5269
hint: "--exclude-releases",
@@ -55,16 +72,14 @@ const exclusionDescriptions = {
5572
},
5673
excludeRenovate: {
5774
hint: "--exclude-renovate",
58-
label: "Add a Renovate config to dependencies up-to-date with PRs.",
75+
label: "Add a Renovate config to keep dependencies up-to-date with PRs.",
5976
},
6077
excludeTests: {
6178
hint: "--exclude-tests",
6279
label:
6380
"Add Vitest tooling for fast unit tests, configured with coverage tracking.",
6481
},
65-
} as const;
66-
67-
type ExclusionKey = keyof typeof exclusionDescriptions;
82+
};
6883

6984
const exclusionKeys = Object.keys(exclusionDescriptions) as ExclusionKey[];
7085

@@ -85,19 +100,33 @@ export async function augmentOptionsWithExcludes(
85100
options.base ??
86101
filterPromptCancel<InputBase | symbol>(
87102
await prompts.select({
103+
initialValue: "common" as InputBase,
88104
message: `How much tooling would you like the template to set up for you?`,
89105
options: [
90106
{
91-
hint: "recommended",
92-
label: "Everything! 🙌",
107+
label: makeLabel(
108+
"everything",
109+
"The most comprehensive tooling imaginable: sorting, spellchecking, and more!",
110+
),
93111
value: "everything",
94112
},
95113
{
96-
label: "Just the bare essentials, please.",
114+
hint: "recommended",
115+
label: makeLabel(
116+
"common",
117+
"Bare starters plus testing and automation for all-contributors and releases.",
118+
),
119+
value: "common",
120+
},
121+
{
122+
label: makeLabel(
123+
"minimum",
124+
"Just bare starter tooling: building, formatting, linting, and type checking.",
125+
),
97126
value: "minimum",
98127
},
99128
{
100-
label: "Allow me to customize.",
129+
label: makeLabel("prompt", "(allow me to customize)"),
101130
value: "prompt",
102131
},
103132
],
@@ -108,6 +137,16 @@ export async function augmentOptionsWithExcludes(
108137
case undefined:
109138
return undefined;
110139

140+
case "common":
141+
return {
142+
...options,
143+
...Object.fromEntries(
144+
exclusionKeys
145+
.filter((exclusion) => exclusionDescriptions[exclusion].uncommon)
146+
.map((exclusion) => [exclusion, options[exclusion] ?? true]),
147+
),
148+
};
149+
111150
case "everything":
112151
return options;
113152

@@ -151,3 +190,7 @@ export async function augmentOptionsWithExcludes(
151190
};
152191
}
153192
}
193+
194+
function makeLabel(label: string, message: string) {
195+
return [chalk.bold(label), message].join("\t ");
196+
}

src/shared/options/readOptions.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,12 @@ export async function readOptions(args: string[]): Promise<OptionsParseResult> {
165165
const optionsSchema = z.object({
166166
author: z.string().optional(),
167167
base: z
168-
.union([z.literal("everything"), z.literal("minimum"), z.literal("prompt")])
168+
.union([
169+
z.literal("common"),
170+
z.literal("everything"),
171+
z.literal("minimum"),
172+
z.literal("prompt"),
173+
])
169174
.optional(),
170175
createRepository: z.boolean().optional(),
171176
description: z.string().optional(),

src/shared/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export interface PartialPackageData {
1717
repository?: { type: string; url: string } | string;
1818
}
1919

20-
export type InputBase = "everything" | "minimum" | "prompt";
20+
export type InputBase = "common" | "everything" | "minimum" | "prompt";
2121

2222
export interface Options {
2323
author?: string;

0 commit comments

Comments
 (0)