Skip to content

Commit 7f1350e

Browse files
feat: add --guide options (#983)
## PR Checklist - [x] Addresses an existing open issue: fixes #982 - [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 `--guide` with the accompanying `--guide-title`, similar to the existing `--logo` and `--logo-alt` options. It will be inferred from an existing `.github/DEVELOPMENT.md` when present.
1 parent 88f87b1 commit 7f1350e

File tree

15 files changed

+266
-32
lines changed

15 files changed

+266
-32
lines changed

.github/DEVELOPMENT.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Development
22

3+
> If you'd like a more guided walkthrough, see [Contributing to a create-typescript-app Repository](https://www.joshuakgoldberg.com/blog/contributing-to-a-create-typescript-app-repository).
4+
> It'll walk you through the common activities you'll need to contribute.
5+
36
After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo) and [installing pnpm](https://pnpm.io/installation):
47

58
```shell

docs/FAQs.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# FAQs
22

3+
## Do you have a guide to working with a `create-typescript-app` repository?
4+
5+
Yes!
6+
See [Contributing to a create-typescript-app Repository](https://www.joshuakgoldberg.com/blog/contributing-to-a-create-typescript-app-repository).
7+
It'll walk you through the common activities you'll need to contribute to a repository scaffolded with `create-typescript-app`.
8+
39
## Can I use _(insert tool here)_ with this template?
410

511
Yes!
@@ -61,13 +67,13 @@ You can always copy & paste them in manually, and/or re-run `npx create-typescri
6167

6268
See [🚀 Feature: Add a script to sync the tooling updates from forked template repo #498](https://github.com/JoshuaKGoldberg/create-typescript-app/issues/498): it will likely eventually be possible.
6369

64-
### What about `eslint-config-prettier`?
70+
## What about `eslint-config-prettier`?
6571

6672
[`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier) is an ESLint plugin that serves only to turn off all rules that are unnecessary or might conflict with formatters such as Prettier.
6773
None of the ESLint configs enabled by this repository's tooling leave any rules enabled that would need to be disabled.
6874
Using `eslint-config-prettier` would be redundant.
6975

70-
### What determines which "base" a tool goes into?
76+
## What determines which "base" a tool goes into?
7177

7278
The four bases correspond to what have seemed to be the most common user needs of template consumers:
7379

@@ -80,7 +86,7 @@ The four bases correspond to what have seemed to be the most common user needs o
8086

8187
Note that users can always customize exactly with portions are kept in with `--base` **`prompt`**.
8288

83-
### Which tools can't I remove?
89+
## Which tools can't I remove?
8490

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

@@ -99,7 +105,7 @@ Each of the included tools exists for a good reason and provides real value.
99105

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

102-
### What tooling does this package use that isn't part of created repositories?
108+
## What tooling does this package use that isn't part of created repositories?
103109

104110
Glad you asked!
105111
These are the projects used across many parts of `create-typescript-app`:

docs/Options.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,12 @@ The setup scripts also allow for optional overrides of the following inputs whos
5757
- `--email` _(`string`)_: Email address to be listed as the point of contact in docs and packages (e.g. `[email protected]`)
5858
- Optionally, `--email-github` _(`string`)_ and/or `--email-npm` _(`string`)_ may be provided to use different emails in `.md` files and `package.json`, respectively
5959
- `--funding` _(`string`)_: GitHub organization or username to mention in `funding.yml` (by default, `owner`)
60+
- `--guide` _(`string`)_: Link to a contribution guide to place at the top of the development docs
61+
- `--guide-title` _(`string`)_: If `--guide` is provided or detected from an existing DEVELOPMENT.md, the text title to place in the guide link
6062
- `--keywords` _(`string[]`)_: Any number of keywords to include in `package.json` (by default, none)
6163
- This can be specified any number of times, like `--keywords apple --keywords "banana cherry"`
6264
- `--logo` _(`string`)_: Local image file in the repository to display near the top of the README.md as a logo
63-
- `--logo-alt` _(`string`)_: If `--logo` is provided or detected from an existing README.md, alt text that describes the image will be prompted for if not provided
65+
- `--logo-alt` _(`string`)_: If `--logo` is provided or detected from an existing README.md, alt text that describes the image (will be prompted for if not provided)
6466
- `--preserve-generated-from` _(`boolean`)_: Whether to keep the GitHub repository _generated from_ notice (by default, `false`)
6567

6668
For example, customizing the ownership and users associated with a new repository:

src/create/createRerunSuggestion.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,21 @@ describe("createRerunSuggestion", () => {
5050
);
5151
});
5252

53+
it("includes stringified guide when it exists", () => {
54+
const actual = createRerunSuggestion({
55+
...options,
56+
guide: {
57+
href: "https://example.com",
58+
title: "Test Title",
59+
},
60+
mode: "initialize",
61+
});
62+
63+
expect(actual).toMatchInlineSnapshot(
64+
'"npx create-typescript-app --mode initialize --base everything --access public --author TestAuthor --description \\"Test description.\\" --directory . --email-github [email protected] --email-npm [email protected] --exclude-all-contributors true --exclude-compliance true --exclude-lint-jsdoc true --exclude-lint-json true --exclude-lint-knip true --exclude-lint-package-json true --exclude-lint-perfectionist true --guide https://example.com --guide-title \\"Test Title\\" --keywords \\"abc def ghi jkl mno pqr\\" --mode initialize --owner TestOwner --repository test-repository --skip-github-api true --skip-install true --skip-removal true --title \\"Test Title\\""',
65+
);
66+
});
67+
5368
it("includes stringified logo when it exists", () => {
5469
const actual = createRerunSuggestion({
5570
...options,

src/create/createRerunSuggestion.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@ function getFirstMatchingArg(key: string) {
1010
export function createRerunSuggestion(options: Partial<Options>): string {
1111
const optionsNormalized = {
1212
...options,
13-
...(options.email
13+
email: undefined,
14+
...(options.email && {
15+
emailGitHub: options.email.github,
16+
emailNpm: options.email.npm,
17+
}),
18+
...(options.guide
1419
? {
15-
email: undefined,
16-
emailGitHub: options.email.github,
17-
emailNpm: options.email.npm,
20+
guide: options.guide.href,
21+
guideTitle: options.guide.title,
1822
}
19-
: { email: undefined }),
23+
: { guide: undefined }),
2024
...(options.logo
2125
? {
2226
logo: options.logo.src,

src/shared/options/args.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export const allArgOptions = {
2929
"exclude-renovate": { type: "boolean" },
3030
"exclude-tests": { type: "boolean" },
3131
funding: { type: "string" },
32+
guide: { type: "string" },
33+
"guide-title": { type: "string" },
3234
keywords: { multiple: true, type: "string" },
3335
logo: { type: "string" },
3436
"logo-alt": { type: "string" },

src/shared/options/createOptionDefaults/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { tryCatchAsync } from "../../tryCatchAsync.js";
1010
import { tryCatchLazyValueAsync } from "../../tryCatchLazyValueAsync.js";
1111
import { PromptedOptions } from "../../types.js";
1212
import { parsePackageAuthor } from "./parsePackageAuthor.js";
13+
import { readDefaultsFromDevelopment } from "./readDefaultsFromDevelopment.js";
1314
import { readDefaultsFromReadme } from "./readDefaultsFromReadme.js";
1415

1516
export function createOptionDefaults(promptedOptions?: PromptedOptions) {
@@ -57,6 +58,7 @@ export function createOptionDefaults(promptedOptions?: PromptedOptions) {
5758
promptedOptions?.repository ??
5859
(await gitDefaults())?.name ??
5960
(await packageData()).name,
61+
...readDefaultsFromDevelopment(),
6062
...readDefaultsFromReadme(),
6163
};
6264
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { describe, expect, it, vi } from "vitest";
2+
3+
import { readDefaultsFromDevelopment } from "./readDefaultsFromDevelopment.js";
4+
5+
const mockReadFileSafe = vi.fn();
6+
7+
vi.mock("../../readFileSafe.js", () => ({
8+
get readFileSafe() {
9+
return mockReadFileSafe;
10+
},
11+
}));
12+
13+
describe("readDefaultsFromDevelopment", () => {
14+
describe("guide", () => {
15+
it("defaults to undefined when .github/DEVELOPMENT.md cannot be found", async () => {
16+
mockReadFileSafe.mockResolvedValue("");
17+
18+
const guide = await readDefaultsFromDevelopment().guide();
19+
20+
expect(guide).toBeUndefined();
21+
});
22+
23+
it("reads guide when it exists", async () => {
24+
mockReadFileSafe.mockResolvedValue(`# Development
25+
26+
> If you'd like a more guided walkthrough, see [Contributing to a create-typescript-app Repository](https://www.joshuakgoldberg.com/blog/contributing-to-a-create-typescript-app-repository).
27+
> It'll walk you through the common activities you'll need to contribute.
28+
`);
29+
30+
const guide = await readDefaultsFromDevelopment().guide();
31+
32+
expect(guide).toBe(
33+
"https://www.joshuakgoldberg.com/blog/contributing-to-a-create-typescript-app-repository",
34+
);
35+
});
36+
});
37+
38+
describe("guideTitle", () => {
39+
it("defaults to undefined when .github/DEVELOPMENT.md cannot be found", async () => {
40+
mockReadFileSafe.mockResolvedValue("");
41+
42+
const guideTitle = await readDefaultsFromDevelopment().guideTitle();
43+
44+
expect(guideTitle).toBeUndefined();
45+
});
46+
47+
it("reads guideTitle when it exists", async () => {
48+
mockReadFileSafe.mockResolvedValue(`# Development
49+
50+
> If you'd like a more guided walkthrough, see [Contributing to a create-typescript-app Repository](https://www.joshuakgoldberg.com/blog/contributing-to-a-create-typescript-app-repository).
51+
> It'll walk you through the common activities you'll need to contribute.
52+
`);
53+
54+
const guideTitle = await readDefaultsFromDevelopment().guideTitle();
55+
56+
expect(guideTitle).toBe(
57+
"Contributing to a create-typescript-app Repository",
58+
);
59+
});
60+
});
61+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import lazyValue from "lazy-value";
2+
3+
import { readFileSafe } from "../../readFileSafe.js";
4+
5+
export function readDefaultsFromDevelopment() {
6+
const development = lazyValue(
7+
async () => await readFileSafe(".github/DEVELOPMENT.md", ""),
8+
);
9+
10+
const guideTag = lazyValue(async () =>
11+
(await development()).match(
12+
/> .*guided walkthrough, see \[((?!\[).+)\]\((.+)\)/i,
13+
),
14+
);
15+
16+
return {
17+
guide: async () => (await guideTag())?.[2],
18+
guideTitle: async () => (await guideTag())?.[1],
19+
};
20+
}

src/shared/options/optionsSchema.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ export const optionsSchemaShape = {
3939
excludeRenovate: z.boolean().optional(),
4040
excludeTests: z.boolean().optional(),
4141
funding: z.string().optional(),
42+
guide: z.string().url().optional(),
43+
guideTitle: z.string().optional(),
4244
keywords: z.array(z.string()).optional(),
4345
logo: z.string().optional(),
4446
logoAlt: z.string().optional(),

0 commit comments

Comments
 (0)