Skip to content

Commit c2d239b

Browse files
feat: add emoji option (#1984)
<!-- 👋 Hi, thanks for sending a PR to create-typescript-app! 💖. Please fill out all fields below and make sure each item is true and [x] checked. Otherwise we may not be able to review your PR. --> ## PR Checklist - [x] Addresses an existing open issue: fixes #1942 - [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 a separate `options.emoji` that defaults to the last detected emoji in `options.description`, or `"💖"` if not found. 🎁
1 parent fd1647b commit c2d239b

14 files changed

+94
-13
lines changed

.github/CONTRIBUTING.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Contributing
22

3-
Thanks for your interest in contributing to `create-typescript-app`! 💖
3+
Thanks for your interest in contributing to `create-typescript-app`! 🎁
44

55
> After this page, see [DEVELOPMENT.md](./DEVELOPMENT.md) for local development instructions.
66
@@ -94,4 +94,4 @@ Please do ping the maintainer who merged your PR if that doesn't happen within 2
9494

9595
If you made it all the way to the end, bravo dear user, we love you.
9696
Please include your favorite emoji in the bottom of your issues and PRs to signal to us that you did in fact read this file and are trying to conform to it as best as possible.
97-
💖 is a good starter if you're not sure which to use.
97+
🎁 is a good starter if you're not sure which to use.

.github/PULL_REQUEST_TEMPLATE.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- 👋 Hi, thanks for sending a PR to create-typescript-app! 💖.
1+
<!-- 👋 Hi, thanks for sending a PR to create-typescript-app! 🎁.
22
Please fill out all fields below and make sure each item is true and [x] checked.
33
Otherwise we may not be able to review your PR. -->
44

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ You can read more about `create-typescript-app` and the tooling it supports:
5959
## Development
6060

6161
See [`.github/CONTRIBUTING.md`](./.github/CONTRIBUTING.md), then [`.github/DEVELOPMENT.md`](./.github/DEVELOPMENT.md).
62-
Thanks! 💖
62+
Thanks! 🎁
6363

6464
## Contributors
6565

src/base.test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ describe("base", () => {
2525
github: "[email protected]",
2626
2727
},
28+
emoji: "🎁",
2829
existingLabels: expect.any(Array),
2930
explainer: [
3031
`\`create-typescript-app\` is a one-stop-shop solution to set up a new or existing repository with the latest and greatest TypeScript tooling.`,

src/base.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { z } from "zod";
1010

1111
import { inputFromOctokit } from "./inputs/inputFromOctokit.js";
1212
import { getExistingLabels } from "./options/getExistingLabels.js";
13+
import { parseEmojiFromDescription } from "./options/parseEmojiFromDescription.js";
1314
import { parsePackageAuthor } from "./options/parsePackageAuthor.js";
1415
import { readAllContributors } from "./options/readAllContributors.js";
1516
import { readDefaultsFromReadme } from "./options/readDefaultsFromReadme.js";
@@ -53,7 +54,7 @@ export const base = createBase({
5354
.describe("AllContributors contributors to store in .all-contributorsrc"),
5455
description: z
5556
.string()
56-
.default("A very lovely package. Hooray! 💖")
57+
.default("A very lovely package. Hooray!")
5758
.describe("'Sentence case.' description of the repository"),
5859
directory: z.string().describe("Directory to create the repository in"),
5960
documentation: z
@@ -74,6 +75,10 @@ export const base = createBase({
7475
.describe(
7576
"email address to be listed as the point of contact in docs and packages",
7677
),
78+
emoji: z
79+
.string()
80+
.optional()
81+
.describe("decorative emoji to use in descriptions and docs"),
7782
existingLabels: z
7883
.array(
7984
z.object({
@@ -196,6 +201,10 @@ export const base = createBase({
196201

197202
// TODO: Make these all use take
198203

204+
const emoji = async () => parseEmojiFromDescription(description);
205+
206+
const description = async () => await readDescription(packageData, readme);
207+
199208
const gitDefaults = tryCatchLazyValueAsync(async () =>
200209
gitUrlParse(await gitRemoteOriginUrl()),
201210
);
@@ -253,9 +262,10 @@ export const base = createBase({
253262
author,
254263
bin: async () => (await packageData()).bin,
255264
contributors: allContributors,
256-
description: async () => await readDescription(packageData, readme),
265+
description,
257266
documentation,
258267
email,
268+
emoji,
259269
existingLabels,
260270
funding: readFunding,
261271
guide: readGuide,
@@ -274,7 +284,7 @@ export const base = createBase({
274284
pnpm,
275285
repository,
276286
rulesetId,
277-
...readDefaultsFromReadme(readme, repository),
287+
...readDefaultsFromReadme(emoji, readme, repository),
278288
version,
279289
};
280290
},

src/blocks/blockCSpell.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ describe("blockCSpell", () => {
211211
"scripts": [
212212
{
213213
"commands": [
214-
"node path/to/cspell-populate-words/bin/index.mjs --words "access" --words "public" --words "description" --words "Test description" --words "directory" --words "." --words "email" --words "github" --words "[email protected]" --words "npm" --words "[email protected]" --words "owner" --words "test-owner" --words "repository" --words "test-repository" --words "title" --words "Test Title"",
214+
"node path/to/cspell-populate-words/bin/index.mjs --words "access" --words "public" --words "description" --words "Test description" --words "directory" --words "." --words "email" --words "github" --words "[email protected]" --words "npm" --words "[email protected]" --words "emoji" --words "💖" --words "owner" --words "test-owner" --words "repository" --words "test-repository" --words "title" --words "Test Title"",
215215
],
216216
"phase": 3,
217217
},

src/blocks/blockContributingDocs.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const blockContributingDocs = base.createBlock({
1111
".github": {
1212
"CONTRIBUTING.md": `# Contributing
1313
14-
Thanks for your interest in contributing to \`${options.repository}\`! 💖
14+
Thanks for your interest in contributing to \`${options.repository}\`! ${options.emoji}
1515
1616
> After this page, see [DEVELOPMENT.md](./DEVELOPMENT.md) for local development instructions.
1717
@@ -105,7 +105,7 @@ Please do ping the maintainer who merged your PR if that doesn't happen within 2
105105
106106
If you made it all the way to the end, bravo dear user, we love you.
107107
Please include your favorite emoji in the bottom of your issues and PRs to signal to us that you did in fact read this file and are trying to conform to it as best as possible.
108-
💖 is a good starter if you're not sure which to use.
108+
${options.emoji} is a good starter if you're not sure which to use.
109109
`,
110110
},
111111
},

src/blocks/blockGitHubPRTemplate.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export const blockGitHubPRTemplate = base.createBlock({
88
return {
99
files: {
1010
".github": {
11-
"PULL_REQUEST_TEMPLATE.md": `<!-- 👋 Hi, thanks for sending a PR to ${options.repository}! 💖.
11+
"PULL_REQUEST_TEMPLATE.md": `<!-- 👋 Hi, thanks for sending a PR to ${options.repository}! ${options.emoji}.
1212
Please fill out all fields below and make sure each item is true and [x] checked.
1313
Otherwise we may not be able to review your PR. -->
1414

src/blocks/blockREADME.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ ${options.usage}
4949
## Development
5050
5151
See [\`.github/CONTRIBUTING.md\`](./.github/CONTRIBUTING.md), then [\`.github/DEVELOPMENT.md\`](./.github/DEVELOPMENT.md).
52-
Thanks! 💖
52+
Thanks! ${options.emoji}
5353
${sections.map((section) => `\n${section}`).join("")}
5454
${notices.length ? `\n${notices.map((notice) => notice.trim()).join("\n\n")}` : ""}`,
5555
},

src/blocks/options.fakes.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const optionsBase = {
88
github: "[email protected]",
99
1010
},
11+
emoji: "💖",
1112
owner: "test-owner",
1213
repository: "test-repository",
1314
title: "Test Title",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { describe, expect, it, vi } from "vitest";
2+
3+
import { parseEmojiFromDescription } from "./parseEmojiFromDescription.js";
4+
5+
describe("parseEmojiFromDescription", () => {
6+
it("resolves with undefined when description is undefined", async () => {
7+
const getDescription = vi.fn().mockResolvedValue(undefined);
8+
9+
const actual = await parseEmojiFromDescription(getDescription);
10+
11+
expect(actual).toBe("💖");
12+
});
13+
14+
it("resolves with undefined when the description does not have any emoji", async () => {
15+
const getDescription = () => Promise.resolve("Hello world.");
16+
17+
const actual = await parseEmojiFromDescription(getDescription);
18+
19+
expect(actual).toBe("💖");
20+
});
21+
22+
it("resolves with the emoji when the description has one emoji", async () => {
23+
const getDescription = () => Promise.resolve("Hello. 😊");
24+
25+
const actual = await parseEmojiFromDescription(getDescription);
26+
27+
expect(actual).toBe("😊");
28+
});
29+
30+
it("resolves with the last emoji when the description has multiple emoji", async () => {
31+
const getDescription = () => Promise.resolve("Hello 🌍. 😊");
32+
33+
const actual = await parseEmojiFromDescription(getDescription);
34+
35+
expect(actual).toBe("😊");
36+
});
37+
});
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export async function parseEmojiFromDescription(
2+
getDescription: () => Promise<string | undefined>,
3+
) {
4+
return (
5+
(await getDescription())?.match(/\p{Extended_Pictographic}/gu)?.at(-1) ??
6+
"💖"
7+
);
8+
}

src/options/readDefaultsFromReadme.test.ts

+23
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@ vi.mock("./getUsageFromReadme.js", () => ({
1818
},
1919
}));
2020

21+
const emoji = () => Promise.resolve("💖");
22+
2123
describe(readDefaultsFromReadme, () => {
2224
describe("explainer", () => {
2325
it("defaults to undefined when it cannot be found", async () => {
2426
const explainer = await readDefaultsFromReadme(
27+
emoji,
2528
() => Promise.resolve(`nothing.`),
2629
() => Promise.resolve(undefined),
2730
).explainer();
@@ -31,6 +34,7 @@ describe(readDefaultsFromReadme, () => {
3134

3235
it("parses a line after badges", async () => {
3336
const explainer = await readDefaultsFromReadme(
37+
emoji,
3438
() =>
3539
Promise.resolve(`
3640
</p>
@@ -47,6 +51,7 @@ This is my project.
4751

4852
it("parses multiple lines after badges", async () => {
4953
const explainer = await readDefaultsFromReadme(
54+
emoji,
5055
() =>
5156
Promise.resolve(`
5257
</p>
@@ -64,6 +69,7 @@ It is good.
6469

6570
it("parses multiple lines after badges and a logo", async () => {
6671
const explainer = await readDefaultsFromReadme(
72+
emoji,
6773
() =>
6874
Promise.resolve(`
6975
<p align="center">
@@ -96,6 +102,7 @@ It is good.
96102
describe("logo", () => {
97103
it("defaults to undefined when it cannot be found", async () => {
98104
const logo = await readDefaultsFromReadme(
105+
emoji,
99106
() => Promise.resolve(`nothing.`),
100107
() => Promise.resolve(undefined),
101108
).logo();
@@ -105,6 +112,7 @@ It is good.
105112

106113
it("parses when found in an unquoted string", async () => {
107114
const logo = await readDefaultsFromReadme(
115+
emoji,
108116
() =>
109117
Promise.resolve(`
110118
<img src=abc/def.jpg/>`),
@@ -119,6 +127,7 @@ It is good.
119127

120128
it("parses when found in a single quoted string", async () => {
121129
const logo = await readDefaultsFromReadme(
130+
emoji,
122131
() =>
123132
Promise.resolve(`
124133
<img src='abc/def.jpg'/>`),
@@ -133,6 +142,7 @@ It is good.
133142

134143
it("parses when found in a double quoted string", async () => {
135144
const logo = await readDefaultsFromReadme(
145+
emoji,
136146
() =>
137147
Promise.resolve(`
138148
<img src="abc/def.jpg"/>`),
@@ -147,6 +157,7 @@ It is good.
147157

148158
it("includes alt text when it exists in double quotes", async () => {
149159
const logo = await readDefaultsFromReadme(
160+
emoji,
150161
() =>
151162
Promise.resolve(`
152163
<img alt="Project logo: a fancy circle" src="abc/def.jpg"/>`),
@@ -161,6 +172,7 @@ It is good.
161172

162173
it("includes alt text when it exists in single quotes", async () => {
163174
const logo = await readDefaultsFromReadme(
175+
emoji,
164176
() =>
165177
Promise.resolve(`
166178
<img alt='Project logo: a fancy circle' src='abc/def.jpg'/>`),
@@ -179,6 +191,7 @@ It is good.
179191
mockReadLogoSizing.mockReturnValueOnce(sizing);
180192

181193
const logo = await readDefaultsFromReadme(
194+
emoji,
182195
() =>
183196
Promise.resolve(`
184197
<img alt='Project logo: a fancy circle' height='117px' src='abc/def.jpg' width=' 117px'/>`),
@@ -194,6 +207,7 @@ It is good.
194207

195208
it("parses when found after a badge image", async () => {
196209
const logo = await readDefaultsFromReadme(
210+
emoji,
197211
() =>
198212
Promise.resolve(`
199213
<a href="#contributors" target="_blank"><img alt="👪 All Contributors: 48" src="https://img.shields.io/badge/%F0%9F%91%AA_all_contributors-48-21bb42.svg" /></a>
@@ -210,6 +224,7 @@ It is good.
210224

211225
it("parses when found after an h1 and many badge images", async () => {
212226
const logo = await readDefaultsFromReadme(
227+
emoji,
213228
() =>
214229
Promise.resolve(`
215230
<h1 align="center">Create TypeScript App</h1>
@@ -244,6 +259,7 @@ It is good.
244259
describe("title", () => {
245260
it("defaults to undefined when it cannot be found", async () => {
246261
const title = await readDefaultsFromReadme(
262+
emoji,
247263
() => Promise.resolve(`nothing`),
248264
() => Promise.resolve(undefined),
249265
).title();
@@ -253,6 +269,7 @@ It is good.
253269

254270
it('reads title as markdown from "README.md" when it exists', async () => {
255271
const title = await readDefaultsFromReadme(
272+
emoji,
256273
() => Promise.resolve(`# My Awesome Package`),
257274
() => Promise.resolve(undefined),
258275
).title();
@@ -262,6 +279,7 @@ It is good.
262279

263280
it('reads title as HTML from "README.md" when it exists', async () => {
264281
const title = await readDefaultsFromReadme(
282+
emoji,
265283
() => Promise.resolve('<h1 align="center">My Awesome Package</h1>'),
266284
() => Promise.resolve(undefined),
267285
).title();
@@ -271,6 +289,7 @@ It is good.
271289

272290
it("converts 'typescript' to 'TypeScript' when parsed from the repository name", async () => {
273291
const title = await readDefaultsFromReadme(
292+
emoji,
274293
() => Promise.resolve(""),
275294
() => Promise.resolve("my-typescript-app"),
276295
).title();
@@ -280,6 +299,7 @@ It is good.
280299

281300
it("converts 'eslint' to 'ESLint' when parsed from the repository name", async () => {
282301
const title = await readDefaultsFromReadme(
302+
emoji,
283303
() => Promise.resolve(""),
284304
() => Promise.resolve("my-eslint-plugin"),
285305
).title();
@@ -289,6 +309,7 @@ It is good.
289309

290310
it("returns undefined when title does not exist", async () => {
291311
const title = await readDefaultsFromReadme(
312+
emoji,
292313
() => Promise.resolve(`Other text.`),
293314
() => Promise.resolve(undefined),
294315
).title();
@@ -304,6 +325,7 @@ It is good.
304325
mockGetUsageFromReadme.mockReturnValueOnce(existing);
305326

306327
const usage = await readDefaultsFromReadme(
328+
emoji,
307329
() => Promise.resolve(""),
308330
() => Promise.resolve(undefined),
309331
).usage();
@@ -315,6 +337,7 @@ It is good.
315337
mockGetUsageFromReadme.mockReturnValueOnce(undefined);
316338

317339
const usage = await readDefaultsFromReadme(
340+
emoji,
318341
() => Promise.resolve(""),
319342
() => Promise.resolve("test-repository"),
320343
).usage();

src/options/readDefaultsFromReadme.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { getUsageFromReadme } from "./getUsageFromReadme.js";
55
import { readLogoSizing } from "./readLogoSizing.js";
66

77
export function readDefaultsFromReadme(
8+
emoji: () => Promise<string>,
89
readme: () => Promise<string>,
910
repository: () => Promise<string | undefined>,
1011
) {
@@ -83,7 +84,7 @@ npm i ${await repository()}
8384
\`\`\`ts
8485
import { greet } from "${await repository()}";
8586
86-
greet("Hello, world! 💖");
87+
greet("Hello, world! ${await emoji()}");
8788
\`\`\``
8889
);
8990
},

0 commit comments

Comments
 (0)