From 04ec1c3c1ebb7e7adcc1cee90b436b73eb54fe5e Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 21:09:45 -0400 Subject: [PATCH 01/19] chore: use --auto in end-to-end migration test --- script/migrate-test-e2e.ts | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/script/migrate-test-e2e.ts b/script/migrate-test-e2e.ts index 5468e144..ace03716 100644 --- a/script/migrate-test-e2e.ts +++ b/script/migrate-test-e2e.ts @@ -4,8 +4,6 @@ import * as fs from "node:fs/promises"; import { rimraf } from "rimraf"; import { assert, describe, expect, test } from "vitest"; -import packageData from "../package.json" assert { type: "json" }; - const filesExpectedToBeChanged = [ "README.md", "knip.json", @@ -21,21 +19,6 @@ const filesThatMightBeChanged = new Set([ "script/__snapshots__/migrate-test-e2e.ts.snap", ]); -const { - author: { email: emailNpm, name: authorName }, - description, - name: repository, -} = packageData; -const emailGithub = "github@joshuakgoldberg.com"; -const bin = "./bin/index.js"; -const guide = - "https://www.joshuakgoldberg.com/blog/contributing-to-a-create-typescript-app-repository"; -const guideTitle = "Contributing to a create-typescript-app Repository"; -const logo = "./docs/create-typescript-app.png"; -const logoAlt = `Project logo: the TypeScript blue square with rounded corners, but a plus sign instead of 'TS'`; -const owner = "JoshuaKGoldberg"; -const title = "Create TypeScript App"; - await rimraf("coverage*"); const originalReadme = (await fs.readFile("README.md")).toString(); @@ -46,7 +29,7 @@ const originalSnapshots = ( await $({ stdio: "inherit", -})`c8 -o ./coverage -r html -r lcov --src src node ${bin} --base everything --author ${authorName} --mode migrate --bin ${bin} --description ${description} --email-github ${emailGithub} --email-npm ${emailNpm} --guide ${guide} --guide-title ${guideTitle} --logo ${logo} --logo-alt ${logoAlt} --owner ${owner} --title ${title} --repository ${repository} --skip-all-contributors-api --skip-github-api --skip-install`; +})`c8 -o ./coverage -r html -r lcov --src src node ./bin/index.js --auto --mode migrate --skip-all-contributors-api --skip-github-api --skip-install`; // All Contributors seems to not be using Prettier to format files... await fs.writeFile( From a7d1505b0f6f69442951100dabd0663bca6b8f99 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 21:41:12 -0400 Subject: [PATCH 02/19] Progress: github email, I think --- .../createOptionDefaults/index.test.ts | 85 +++++++++++++++---- .../options/createOptionDefaults/index.ts | 10 ++- .../readGitHubEmail.test.ts | 52 ++++++++++++ .../createOptionDefaults/readGitHubEmail.ts | 12 +++ 4 files changed, 141 insertions(+), 18 deletions(-) create mode 100644 src/shared/options/createOptionDefaults/readGitHubEmail.test.ts create mode 100644 src/shared/options/createOptionDefaults/readGitHubEmail.ts diff --git a/src/shared/options/createOptionDefaults/index.test.ts b/src/shared/options/createOptionDefaults/index.test.ts index 6876c254..bd05a1f3 100644 --- a/src/shared/options/createOptionDefaults/index.test.ts +++ b/src/shared/options/createOptionDefaults/index.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import { createOptionDefaults } from "./index.js"; @@ -34,6 +34,14 @@ vi.mock("../../packages.js", () => ({ }, })); +const mockReadGitHubEmail = vi.fn(); + +vi.mock("./readGitHubEmail.js", () => ({ + get readGitHubEmail() { + return mockReadGitHubEmail; + }, +})); + describe("createOptionDefaults", () => { describe("bin", () => { it("returns undefined when package data does not have a bin", async () => { @@ -56,24 +64,30 @@ describe("createOptionDefaults", () => { }); describe("email", () => { + beforeEach(() => { + mockNpmUser.mockImplementation((username: string) => ({ + email: `npm-${username}@test.com`, + })); + }); + it("returns the npm whoami email from npm when only an npm exists", async () => { mock$.mockImplementation(([command]: string[]) => - command === "npm whoami" ? { stdout: "npm-username" } : undefined, + command === "npm whoami" ? { stdout: "username" } : undefined, ); - mockNpmUser.mockImplementation((username: string) => ({ - email: `test@${username}.com`, - })); + mockReadGitHubEmail.mockResolvedValueOnce(undefined); const actual = await createOptionDefaults().email(); expect(actual).toEqual({ - github: "test@npm-username.com", - npm: "test@npm-username.com", + git: "npm-username@test.com", + github: "npm-username@test.com", + npm: "npm-username@test.com", }); }); it("returns the npm whoami email from npm when only a package author email exists", async () => { mock$.mockResolvedValue({ stdout: "" }); + mockReadGitHubEmail.mockResolvedValueOnce(undefined); mockReadPackageData.mockResolvedValue({ author: { email: "test@package.com", @@ -83,46 +97,85 @@ describe("createOptionDefaults", () => { const actual = await createOptionDefaults().email(); expect(actual).toEqual({ + git: "test@package.com", github: "test@package.com", npm: "test@package.com", }); }); + it("returns the github email when only a github email exists", async () => { + mock$.mockResolvedValue({ stdout: "" }); + mockReadPackageData.mockResolvedValueOnce({}); + mockReadGitHubEmail.mockResolvedValueOnce("github@test.com"); + + const actual = await createOptionDefaults().email(); + + expect(actual).toEqual({ + git: "github@test.com", + github: "github@test.com", + npm: "github@test.com", + }); + }); + it("returns the git user email when only a git user email exists", async () => { mock$.mockImplementation(([command]: string[]) => command === "git config --get user.email" - ? { stdout: "test@git.com" } + ? { stdout: "git@test.com" } : undefined, ); + mockReadGitHubEmail.mockResolvedValueOnce(undefined); + mockReadPackageData.mockResolvedValue({}); + + const actual = await createOptionDefaults().email(); + + expect(actual).toEqual({ + git: "git@test.com", + github: "git@test.com", + npm: "git@test.com", + }); + }); + + it("returns both the git user email and the npm user email when only those two exist", async () => { + mock$.mockImplementation(([command]: string[]) => ({ + stdout: + command === "git config --get user.email" + ? "git@test.com" + : "username", + })); + mockReadGitHubEmail.mockResolvedValueOnce(undefined); mockReadPackageData.mockResolvedValue({}); const actual = await createOptionDefaults().email(); expect(actual).toEqual({ - github: "test@git.com", - npm: "test@git.com", + git: "git@test.com", + github: "git@test.com", + npm: "npm-username@test.com", }); }); - it("returns both the git user email and the npm user email when both exist", async () => { + it("returns all three emails when they all exist", async () => { mock$.mockImplementation(([command]: string[]) => ({ stdout: command === "git config --get user.email" - ? "test@git.com" - : "npm-username", + ? "git@test.com" + : "username", })); + mockReadGitHubEmail.mockResolvedValueOnce("github@test.com"); mockReadPackageData.mockResolvedValue({}); const actual = await createOptionDefaults().email(); expect(actual).toEqual({ - github: "test@git.com", - npm: "test@npm-username.com", + git: "git@test.com", + github: "github@test.com", + npm: "npm-username@test.com", }); }); - it("returns undefined when neither git nor npm emails exist", async () => { + it("returns undefined when none of the emails exist", async () => { mock$.mockResolvedValue({ stdout: "" }); + mockReadGitHubEmail.mockResolvedValueOnce(undefined); mockReadPackageData.mockResolvedValue({}); const actual = await createOptionDefaults().email(); diff --git a/src/shared/options/createOptionDefaults/index.ts b/src/shared/options/createOptionDefaults/index.ts index 4d72a744..bea3fde2 100644 --- a/src/shared/options/createOptionDefaults/index.ts +++ b/src/shared/options/createOptionDefaults/index.ts @@ -12,6 +12,7 @@ import { PromptedOptions } from "../../types.js"; import { parsePackageAuthor } from "./parsePackageAuthor.js"; import { readDefaultsFromDevelopment } from "./readDefaultsFromDevelopment.js"; import { readDefaultsFromReadme } from "./readDefaultsFromReadme.js"; +import { readGitHubEmail } from "./readGitHubEmail.js"; export function createOptionDefaults(promptedOptions?: PromptedOptions) { const gitDefaults = tryCatchLazyValueAsync(async () => @@ -33,6 +34,7 @@ export function createOptionDefaults(promptedOptions?: PromptedOptions) { bin: async () => (await packageData()).bin, description: async () => (await packageData()).description, email: async () => { + const githubEmail = await readGitHubEmail(); const gitEmail = await tryCatchAsync( async () => (await $`git config --get user.email`).stdout, ); @@ -40,8 +42,12 @@ export function createOptionDefaults(promptedOptions?: PromptedOptions) { (await npmDefaults())?.email ?? (await packageAuthor()).email; /* eslint-disable @typescript-eslint/no-non-null-assertion */ - return gitEmail || npmEmail - ? { github: (gitEmail || npmEmail)!, npm: (npmEmail || gitEmail)! } + return gitEmail || githubEmail || npmEmail + ? { + git: (gitEmail || githubEmail || npmEmail)!, + github: (githubEmail || gitEmail || npmEmail)!, + npm: (npmEmail || gitEmail || githubEmail)!, + } : undefined; /* eslint-enable @typescript-eslint/no-non-null-assertion */ }, diff --git a/src/shared/options/createOptionDefaults/readGitHubEmail.test.ts b/src/shared/options/createOptionDefaults/readGitHubEmail.test.ts new file mode 100644 index 00000000..f472abeb --- /dev/null +++ b/src/shared/options/createOptionDefaults/readGitHubEmail.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it, vi } from "vitest"; + +import { readGitHubEmail } from "./readGitHubEmail.js"; + +const mockReadFileSafe = vi.fn(); + +vi.mock("../../readFileSafe.js", () => ({ + get readFileSafe() { + return mockReadFileSafe; + }, +})); + +describe("readGitHubEmail", () => { + it("returns undefined when it cannot be found", async () => { + mockReadFileSafe.mockResolvedValue("nothing."); + + const email = await readGitHubEmail(); + + expect(email).toBeUndefined(); + }); + + it("returns undefined when a different template", async () => { + mockReadFileSafe.mockResolvedValue( + `## Other Code of Conduct + +for enforcement at +invalid@test.com. +`, + ); + + const email = await readGitHubEmail(); + + expect(email).toBeUndefined(); + }); + + it("returns the email when it matches the template", async () => { + const expected = `email@test.com`; + + mockReadFileSafe.mockResolvedValue( + `## Contributor Covenant Code of Conduct + +reported to the community leaders responsible for enforcement at +${expected}. +All complaints will be reviewed and investigated promptly and fairly. +`, + ); + + const email = await readGitHubEmail(); + + expect(email).toBe(expected); + }); +}); diff --git a/src/shared/options/createOptionDefaults/readGitHubEmail.ts b/src/shared/options/createOptionDefaults/readGitHubEmail.ts new file mode 100644 index 00000000..ea52beac --- /dev/null +++ b/src/shared/options/createOptionDefaults/readGitHubEmail.ts @@ -0,0 +1,12 @@ +import { readFileSafe } from "../../readFileSafe.js"; + +export async function readGitHubEmail() { + // The create-typescript-app template puts the GitHub email in the CoC. + // If they seem to be using the template, we can go with that. + const codeOfConduct = await readFileSafe(".github/CODE_OF_CONDUCT.md", ""); + if (!codeOfConduct.includes("Contributor Covenant Code of Conduct")) { + return undefined; + } + + return /for enforcement at[\r\n]+(.+)\.[\r\n]+All/.exec(codeOfConduct)?.[1]; +} From b0a37d2cca02217713d54335d8a668f811072893 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 21:45:21 -0400 Subject: [PATCH 03/19] bin --- src/shared/options/readOptions.test.ts | 1 + src/shared/options/readOptions.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/shared/options/readOptions.test.ts b/src/shared/options/readOptions.test.ts index 2be4a603..b9b3dec7 100644 --- a/src/shared/options/readOptions.test.ts +++ b/src/shared/options/readOptions.test.ts @@ -112,6 +112,7 @@ vi.mock("./createOptionDefaults/index.js", () => ({ createOptionDefaults() { return { author: vi.fn(), + bin: vi.fn(), description: vi.fn(), email: vi.fn(), funding: vi.fn(), diff --git a/src/shared/options/readOptions.ts b/src/shared/options/readOptions.ts index 75aab677..9d5256d6 100644 --- a/src/shared/options/readOptions.ts +++ b/src/shared/options/readOptions.ts @@ -264,6 +264,7 @@ export async function readOptions( ...options, access: options.access ?? "public", author: options.author ?? (await defaults.owner()), + bin: options.bin ?? (await defaults.bin()), description: options.description, directory: options.directory ?? promptedOptions.directory ?? options.repository, From b801c1be72faf985c8ac2cce0a7ecf518d79fec7 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 21:51:20 -0400 Subject: [PATCH 04/19] fixed up guide --- .../readDefaultsFromDevelopment.test.ts | 33 +++---------------- .../readDefaultsFromDevelopment.ts | 24 +++++++------- src/shared/options/readOptions.test.ts | 1 + src/shared/options/readOptions.ts | 2 +- 4 files changed, 20 insertions(+), 40 deletions(-) diff --git a/src/shared/options/createOptionDefaults/readDefaultsFromDevelopment.test.ts b/src/shared/options/createOptionDefaults/readDefaultsFromDevelopment.test.ts index e585f311..1bb51c5b 100644 --- a/src/shared/options/createOptionDefaults/readDefaultsFromDevelopment.test.ts +++ b/src/shared/options/createOptionDefaults/readDefaultsFromDevelopment.test.ts @@ -20,7 +20,7 @@ describe("readDefaultsFromDevelopment", () => { expect(guide).toBeUndefined(); }); - it("reads guide when it exists", async () => { + it("reads the href and title when the tag exists", async () => { mockReadFileSafe.mockResolvedValue(`# Development > 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). @@ -29,33 +29,10 @@ describe("readDefaultsFromDevelopment", () => { const guide = await readDefaultsFromDevelopment().guide(); - expect(guide).toBe( - "https://www.joshuakgoldberg.com/blog/contributing-to-a-create-typescript-app-repository", - ); - }); - }); - - describe("guideTitle", () => { - it("defaults to undefined when .github/DEVELOPMENT.md cannot be found", async () => { - mockReadFileSafe.mockResolvedValue(""); - - const guideTitle = await readDefaultsFromDevelopment().guideTitle(); - - expect(guideTitle).toBeUndefined(); - }); - - it("reads guideTitle when it exists", async () => { - mockReadFileSafe.mockResolvedValue(`# Development - -> 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). -> It'll walk you through the common activities you'll need to contribute. -`); - - const guideTitle = await readDefaultsFromDevelopment().guideTitle(); - - expect(guideTitle).toBe( - "Contributing to a create-typescript-app Repository", - ); + expect(guide).toEqual({ + href: "https://www.joshuakgoldberg.com/blog/contributing-to-a-create-typescript-app-repository", + title: "Contributing to a create-typescript-app Repository", + }); }); }); }); diff --git a/src/shared/options/createOptionDefaults/readDefaultsFromDevelopment.ts b/src/shared/options/createOptionDefaults/readDefaultsFromDevelopment.ts index f3c24039..b0ebe260 100644 --- a/src/shared/options/createOptionDefaults/readDefaultsFromDevelopment.ts +++ b/src/shared/options/createOptionDefaults/readDefaultsFromDevelopment.ts @@ -3,18 +3,20 @@ import lazyValue from "lazy-value"; import { readFileSafe } from "../../readFileSafe.js"; export function readDefaultsFromDevelopment() { - const development = lazyValue( - async () => await readFileSafe(".github/DEVELOPMENT.md", ""), - ); + return { + guide: lazyValue(async () => { + const tag = /> .*guided walkthrough, see \[((?!\[).+)\]\((.+)\)/i.exec( + await readFileSafe(".github/DEVELOPMENT.md", ""), + ); - const guideTag = lazyValue(async () => - /> .*guided walkthrough, see \[((?!\[).+)\]\((.+)\)/i.exec( - await development(), - ), - ); + if (!tag) { + return undefined; + } - return { - guide: async () => (await guideTag())?.[2], - guideTitle: async () => (await guideTag())?.[1], + return { + href: tag[2], + title: tag[1], + }; + }), }; } diff --git a/src/shared/options/readOptions.test.ts b/src/shared/options/readOptions.test.ts index b9b3dec7..8df87a8f 100644 --- a/src/shared/options/readOptions.test.ts +++ b/src/shared/options/readOptions.test.ts @@ -116,6 +116,7 @@ vi.mock("./createOptionDefaults/index.js", () => ({ description: vi.fn(), email: vi.fn(), funding: vi.fn(), + guide: vi.fn(), logo: vi.fn(), owner: vi.fn(), repository: vi.fn(), diff --git a/src/shared/options/readOptions.ts b/src/shared/options/readOptions.ts index 9d5256d6..46925d24 100644 --- a/src/shared/options/readOptions.ts +++ b/src/shared/options/readOptions.ts @@ -270,7 +270,7 @@ export async function readOptions( options.directory ?? promptedOptions.directory ?? options.repository, email, funding: options.funding ?? (await defaults.funding()), - guide, + guide: guide ?? (await defaults.guide()), logo, mode, owner: options.owner, From afa66c258a04e35d5eeb25938bd1e8a518896dbe Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 22:22:58 -0400 Subject: [PATCH 05/19] Allow --email-github more formally --- docs/Options.md | 2 +- src/bin/help.test.ts | 4 + src/shared/options/args.ts | 5 ++ .../options/detectEmailRedundancy.test.ts | 76 +++++++++++-------- src/shared/options/detectEmailRedundancy.ts | 31 ++++++-- src/shared/options/optionsSchema.ts | 1 + src/shared/options/readOptions.test.ts | 5 ++ src/shared/options/readOptions.ts | 12 ++- 8 files changed, 94 insertions(+), 42 deletions(-) diff --git a/docs/Options.md b/docs/Options.md index bedb99cd..ce8f4f23 100644 --- a/docs/Options.md +++ b/docs/Options.md @@ -56,7 +56,7 @@ The setup scripts also allow for optional overrides of the following inputs whos - `--bin` _(`string`)_: `package.json` bin value to include for npx-style running. - `--directory` _(`string`)_: Directory to create the repository in (by default, the same name as the repository) - `--email` _(`string`)_: Email address to be listed as the point of contact in docs and packages (e.g. `example@joshuakgoldberg.com`) - - Optionally, `--email-github` _(`string`)_ and/or `--email-npm` _(`string`)_ may be provided to use different emails in `.md` files and `package.json`, respectively + - Optionally, `--email-` _(`string`)_, `--email-github` _(`string`)_, and/or `--email-npm` _(`string`)_ may be provided to use different emails in Git operations, `.md` files, and `package.json`, respectively - `--funding` _(`string`)_: GitHub organization or username to mention in `funding.yml` (by default, `owner`) - `--guide` _(`string`)_: Link to a contribution guide to place at the top of the development docs - `--guide-title` _(`string`)_: If `--guide` is provided or detected from an existing DEVELOPMENT.md, the text title to place in the guide link diff --git a/src/bin/help.test.ts b/src/bin/help.test.ts index 951430cd..851e42b5 100644 --- a/src/bin/help.test.ts +++ b/src/bin/help.test.ts @@ -135,6 +135,10 @@ describe("logHelpText", () => { --email (string): Email address to be listed as the point of contact in docs and packages (e.g. example@joshuakgoldberg.com)", ], + [ + " + --email-git (string): Optionally, may be provided to use different emails in Git operations", + ], [ " --email-github (string): Optionally, may be provided to use different emails in .md diff --git a/src/shared/options/args.ts b/src/shared/options/args.ts index ed3a88d2..9a47350a 100644 --- a/src/shared/options/args.ts +++ b/src/shared/options/args.ts @@ -62,6 +62,11 @@ export const allArgOptions = { docsSection: "optional", type: "string", }, + "email-git": { + description: `Optionally, may be provided to use different emails in Git operations`, + docsSection: "optional", + type: "string", + }, "email-github": { description: `Optionally, may be provided to use different emails in ${chalk.cyanBright( ".md", diff --git a/src/shared/options/detectEmailRedundancy.test.ts b/src/shared/options/detectEmailRedundancy.test.ts index 3091a609..83e75fde 100644 --- a/src/shared/options/detectEmailRedundancy.test.ts +++ b/src/shared/options/detectEmailRedundancy.test.ts @@ -1,38 +1,50 @@ -import { describe, expect, it } from "vitest"; +import { describe, expect, it, test } from "vitest"; -import { detectEmailRedundancy } from "./detectEmailRedundancy.js"; +import { detectEmailRedundancy, EmailValues } from "./detectEmailRedundancy.js"; describe("detectEmailRedundancy", () => { - it("returns undefined when only email is specified", () => { - expect(detectEmailRedundancy({ email: "test@email.com" })).toBeUndefined(); - }); - - it("returns undefined when email-github and email-npm are specified while email is not", () => { - expect( - detectEmailRedundancy({ - "email-github": "test@email.com", - "email-npm": "test@email.com", - }), - ).toBeUndefined(); - }); - - it("returns a complaint when email-github is specified while email and email-npm are not", () => { - expect( - detectEmailRedundancy({ - "email-github": "test@email.com", - }), - ).toBe( - "If --email-github is specified, either --email or --email-npm should be.", - ); - }); - - it("returns a complaint when email-npm is specified while email and email-github are not", () => { + test.each<[(keyof EmailValues)[], string | undefined]>([ + [[], undefined], + [["email"], undefined], + [["email", "email-git"], undefined], + [["email", "email-github"], undefined], + [["email", "email-npm"], undefined], + [["email", "email-git", "email-github"], undefined], + [["email", "email-git", "email-npm"], undefined], + [["email", "email-github", "email-npm"], undefined], + [ + ["email", "email-git", "email-github", "email-npm"], + "If --email-git, --email-github, and --email-npm are specified, --email should not be.", + ], + [ + ["email-git"], + "If --email-git is specified, either --email or both --email-github and --email-npm should be.", + ], + [ + ["email-github"], + "If --email-github is specified, either --email or both --email-git and --email-npm should be.", + ], + [ + ["email-npm"], + "If --email-npm is specified, either --email or both --email-git and --email-github should be.", + ], + [ + ["email-git", "email-github"], + "If --email-git and --email-github are specified, either --email or --email-npm should be.", + ], + [ + ["email-git", "email-npm"], + "If --email-git and --email-npm are specified, either --email or --email-github should be.", + ], + [ + ["email-github", "email-npm"], + "If --email-github and --email-npm are specified, either --email or --email-git should be.", + ], + ])("%o", (keys, expected) => { expect( - detectEmailRedundancy({ - "email-npm": "test@email.com", - }), - ).toBe( - "If --email-npm is specified, either --email or --email-github should be.", - ); + detectEmailRedundancy( + Object.fromEntries(keys.map((key) => [key, `${key}@test.com`])), + ), + ).toBe(expected); }); }); diff --git a/src/shared/options/detectEmailRedundancy.ts b/src/shared/options/detectEmailRedundancy.ts index 15044b47..b8f7c1ba 100644 --- a/src/shared/options/detectEmailRedundancy.ts +++ b/src/shared/options/detectEmailRedundancy.ts @@ -1,23 +1,40 @@ export interface EmailValues { email?: boolean | string; + "email-git"?: boolean | string; "email-github"?: boolean | string; "email-npm"?: boolean | string; } +const specificKeys = ["email-git", "email-github", "email-npm"] as const; + export function detectEmailRedundancy(values: EmailValues) { + const present = specificKeys + .filter((key) => !!values[key]) + .map((key) => `--${key}`); + if (values.email) { - return values["email-github"] && values["email-npm"] - ? "--email should not be specified if both --email-github and --email-npm are specified." + return present.length === 3 + ? "If --email-git, --email-github, and --email-npm are specified, --email should not be." : undefined; } - if (values["email-github"] && !values["email-npm"]) { - return "If --email-github is specified, either --email or --email-npm should be."; + if (!values.email && !present.length) { + return undefined; } - if (values["email-npm"] && !values["email-github"]) { - return "If --email-npm is specified, either --email or --email-github should be."; + if (present.length === specificKeys.length) { + return undefined; } - return undefined; + const specified = + present.length === 1 ? `${present[0]} is` : `${present.join(" and ")} are`; + + const missing = specificKeys + .filter((key) => !values[key]) + .map((key) => `--${key}`); + + const required = + missing.length === 1 ? missing[0] : `both ${missing.join(" and ")}`; + + return `If ${specified} specified, either --email or ${required} should be.`; } diff --git a/src/shared/options/optionsSchema.ts b/src/shared/options/optionsSchema.ts index e6a097a1..54a431c7 100644 --- a/src/shared/options/optionsSchema.ts +++ b/src/shared/options/optionsSchema.ts @@ -17,6 +17,7 @@ export const optionsSchemaShape = { directory: z.string().optional(), email: z .object({ + git: z.string().email(), github: z.string().email(), npm: z.string().email(), }) diff --git a/src/shared/options/readOptions.test.ts b/src/shared/options/readOptions.test.ts index 8df87a8f..8e49b5a8 100644 --- a/src/shared/options/readOptions.test.ts +++ b/src/shared/options/readOptions.test.ts @@ -558,6 +558,7 @@ describe("readOptions", () => { description: "mock", directory: "mock", email: { + git: "mock", github: "mock", npm: "mock", }, @@ -594,6 +595,7 @@ describe("readOptions", () => { description: "mock", directory: "mock", email: { + git: "mock", github: "mock", npm: "mock", }, @@ -648,6 +650,7 @@ describe("readOptions", () => { description, directory: repository, email: { + git: "mock", github: "mock", npm: "mock", }, @@ -706,6 +709,7 @@ describe("readOptions", () => { description, directory: repository, email: { + git: "mock", github: "mock", npm: "mock", }, @@ -732,6 +736,7 @@ describe("readOptions", () => { "description": "Test description.", "directory": "test-repository", "email": { + "git": "mock", "github": "mock", "npm": "mock", }, diff --git a/src/shared/options/readOptions.ts b/src/shared/options/readOptions.ts index 46925d24..a90c34e9 100644 --- a/src/shared/options/readOptions.ts +++ b/src/shared/options/readOptions.ts @@ -59,8 +59,12 @@ export async function readOptions( description: values.description, directory: values.directory, email: - (values.email ?? values["email-github"] ?? values["email-npm"]) + (values.email ?? + values["email-git"] ?? + values["email-github"] ?? + values["email-npm"]) ? { + git: values.email ?? values["email-git"], github: values.email ?? values["email-github"], npm: values.email ?? values["email-npm"], } @@ -257,7 +261,11 @@ export async function readOptions( return { cancelled: true, error: emailOption.error, options }; } - email = { github: emailOption.value, npm: emailOption.value }; + email = { + git: emailOption.value, + github: emailOption.value, + npm: emailOption.value, + }; } const augmentedOptions = await augmentOptionsWithExcludes({ From 2c8596069a5635c1cd6477be3706b8b9fe1225be Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 22:24:56 -0400 Subject: [PATCH 06/19] Revert "Allow --email-github more formally" This reverts commit afa66c258a04e35d5eeb25938bd1e8a518896dbe. --- docs/Options.md | 2 +- src/bin/help.test.ts | 4 - src/shared/options/args.ts | 5 -- .../options/detectEmailRedundancy.test.ts | 76 ++++++++----------- src/shared/options/detectEmailRedundancy.ts | 31 ++------ src/shared/options/optionsSchema.ts | 1 - src/shared/options/readOptions.test.ts | 5 -- src/shared/options/readOptions.ts | 12 +-- 8 files changed, 42 insertions(+), 94 deletions(-) diff --git a/docs/Options.md b/docs/Options.md index ce8f4f23..bedb99cd 100644 --- a/docs/Options.md +++ b/docs/Options.md @@ -56,7 +56,7 @@ The setup scripts also allow for optional overrides of the following inputs whos - `--bin` _(`string`)_: `package.json` bin value to include for npx-style running. - `--directory` _(`string`)_: Directory to create the repository in (by default, the same name as the repository) - `--email` _(`string`)_: Email address to be listed as the point of contact in docs and packages (e.g. `example@joshuakgoldberg.com`) - - Optionally, `--email-` _(`string`)_, `--email-github` _(`string`)_, and/or `--email-npm` _(`string`)_ may be provided to use different emails in Git operations, `.md` files, and `package.json`, respectively + - Optionally, `--email-github` _(`string`)_ and/or `--email-npm` _(`string`)_ may be provided to use different emails in `.md` files and `package.json`, respectively - `--funding` _(`string`)_: GitHub organization or username to mention in `funding.yml` (by default, `owner`) - `--guide` _(`string`)_: Link to a contribution guide to place at the top of the development docs - `--guide-title` _(`string`)_: If `--guide` is provided or detected from an existing DEVELOPMENT.md, the text title to place in the guide link diff --git a/src/bin/help.test.ts b/src/bin/help.test.ts index 851e42b5..951430cd 100644 --- a/src/bin/help.test.ts +++ b/src/bin/help.test.ts @@ -135,10 +135,6 @@ describe("logHelpText", () => { --email (string): Email address to be listed as the point of contact in docs and packages (e.g. example@joshuakgoldberg.com)", ], - [ - " - --email-git (string): Optionally, may be provided to use different emails in Git operations", - ], [ " --email-github (string): Optionally, may be provided to use different emails in .md diff --git a/src/shared/options/args.ts b/src/shared/options/args.ts index 9a47350a..ed3a88d2 100644 --- a/src/shared/options/args.ts +++ b/src/shared/options/args.ts @@ -62,11 +62,6 @@ export const allArgOptions = { docsSection: "optional", type: "string", }, - "email-git": { - description: `Optionally, may be provided to use different emails in Git operations`, - docsSection: "optional", - type: "string", - }, "email-github": { description: `Optionally, may be provided to use different emails in ${chalk.cyanBright( ".md", diff --git a/src/shared/options/detectEmailRedundancy.test.ts b/src/shared/options/detectEmailRedundancy.test.ts index 83e75fde..3091a609 100644 --- a/src/shared/options/detectEmailRedundancy.test.ts +++ b/src/shared/options/detectEmailRedundancy.test.ts @@ -1,50 +1,38 @@ -import { describe, expect, it, test } from "vitest"; +import { describe, expect, it } from "vitest"; -import { detectEmailRedundancy, EmailValues } from "./detectEmailRedundancy.js"; +import { detectEmailRedundancy } from "./detectEmailRedundancy.js"; describe("detectEmailRedundancy", () => { - test.each<[(keyof EmailValues)[], string | undefined]>([ - [[], undefined], - [["email"], undefined], - [["email", "email-git"], undefined], - [["email", "email-github"], undefined], - [["email", "email-npm"], undefined], - [["email", "email-git", "email-github"], undefined], - [["email", "email-git", "email-npm"], undefined], - [["email", "email-github", "email-npm"], undefined], - [ - ["email", "email-git", "email-github", "email-npm"], - "If --email-git, --email-github, and --email-npm are specified, --email should not be.", - ], - [ - ["email-git"], - "If --email-git is specified, either --email or both --email-github and --email-npm should be.", - ], - [ - ["email-github"], - "If --email-github is specified, either --email or both --email-git and --email-npm should be.", - ], - [ - ["email-npm"], - "If --email-npm is specified, either --email or both --email-git and --email-github should be.", - ], - [ - ["email-git", "email-github"], - "If --email-git and --email-github are specified, either --email or --email-npm should be.", - ], - [ - ["email-git", "email-npm"], - "If --email-git and --email-npm are specified, either --email or --email-github should be.", - ], - [ - ["email-github", "email-npm"], - "If --email-github and --email-npm are specified, either --email or --email-git should be.", - ], - ])("%o", (keys, expected) => { + it("returns undefined when only email is specified", () => { + expect(detectEmailRedundancy({ email: "test@email.com" })).toBeUndefined(); + }); + + it("returns undefined when email-github and email-npm are specified while email is not", () => { + expect( + detectEmailRedundancy({ + "email-github": "test@email.com", + "email-npm": "test@email.com", + }), + ).toBeUndefined(); + }); + + it("returns a complaint when email-github is specified while email and email-npm are not", () => { + expect( + detectEmailRedundancy({ + "email-github": "test@email.com", + }), + ).toBe( + "If --email-github is specified, either --email or --email-npm should be.", + ); + }); + + it("returns a complaint when email-npm is specified while email and email-github are not", () => { expect( - detectEmailRedundancy( - Object.fromEntries(keys.map((key) => [key, `${key}@test.com`])), - ), - ).toBe(expected); + detectEmailRedundancy({ + "email-npm": "test@email.com", + }), + ).toBe( + "If --email-npm is specified, either --email or --email-github should be.", + ); }); }); diff --git a/src/shared/options/detectEmailRedundancy.ts b/src/shared/options/detectEmailRedundancy.ts index b8f7c1ba..15044b47 100644 --- a/src/shared/options/detectEmailRedundancy.ts +++ b/src/shared/options/detectEmailRedundancy.ts @@ -1,40 +1,23 @@ export interface EmailValues { email?: boolean | string; - "email-git"?: boolean | string; "email-github"?: boolean | string; "email-npm"?: boolean | string; } -const specificKeys = ["email-git", "email-github", "email-npm"] as const; - export function detectEmailRedundancy(values: EmailValues) { - const present = specificKeys - .filter((key) => !!values[key]) - .map((key) => `--${key}`); - if (values.email) { - return present.length === 3 - ? "If --email-git, --email-github, and --email-npm are specified, --email should not be." + return values["email-github"] && values["email-npm"] + ? "--email should not be specified if both --email-github and --email-npm are specified." : undefined; } - if (!values.email && !present.length) { - return undefined; + if (values["email-github"] && !values["email-npm"]) { + return "If --email-github is specified, either --email or --email-npm should be."; } - if (present.length === specificKeys.length) { - return undefined; + if (values["email-npm"] && !values["email-github"]) { + return "If --email-npm is specified, either --email or --email-github should be."; } - const specified = - present.length === 1 ? `${present[0]} is` : `${present.join(" and ")} are`; - - const missing = specificKeys - .filter((key) => !values[key]) - .map((key) => `--${key}`); - - const required = - missing.length === 1 ? missing[0] : `both ${missing.join(" and ")}`; - - return `If ${specified} specified, either --email or ${required} should be.`; + return undefined; } diff --git a/src/shared/options/optionsSchema.ts b/src/shared/options/optionsSchema.ts index 54a431c7..e6a097a1 100644 --- a/src/shared/options/optionsSchema.ts +++ b/src/shared/options/optionsSchema.ts @@ -17,7 +17,6 @@ export const optionsSchemaShape = { directory: z.string().optional(), email: z .object({ - git: z.string().email(), github: z.string().email(), npm: z.string().email(), }) diff --git a/src/shared/options/readOptions.test.ts b/src/shared/options/readOptions.test.ts index 8e49b5a8..8df87a8f 100644 --- a/src/shared/options/readOptions.test.ts +++ b/src/shared/options/readOptions.test.ts @@ -558,7 +558,6 @@ describe("readOptions", () => { description: "mock", directory: "mock", email: { - git: "mock", github: "mock", npm: "mock", }, @@ -595,7 +594,6 @@ describe("readOptions", () => { description: "mock", directory: "mock", email: { - git: "mock", github: "mock", npm: "mock", }, @@ -650,7 +648,6 @@ describe("readOptions", () => { description, directory: repository, email: { - git: "mock", github: "mock", npm: "mock", }, @@ -709,7 +706,6 @@ describe("readOptions", () => { description, directory: repository, email: { - git: "mock", github: "mock", npm: "mock", }, @@ -736,7 +732,6 @@ describe("readOptions", () => { "description": "Test description.", "directory": "test-repository", "email": { - "git": "mock", "github": "mock", "npm": "mock", }, diff --git a/src/shared/options/readOptions.ts b/src/shared/options/readOptions.ts index a90c34e9..46925d24 100644 --- a/src/shared/options/readOptions.ts +++ b/src/shared/options/readOptions.ts @@ -59,12 +59,8 @@ export async function readOptions( description: values.description, directory: values.directory, email: - (values.email ?? - values["email-git"] ?? - values["email-github"] ?? - values["email-npm"]) + (values.email ?? values["email-github"] ?? values["email-npm"]) ? { - git: values.email ?? values["email-git"], github: values.email ?? values["email-github"], npm: values.email ?? values["email-npm"], } @@ -261,11 +257,7 @@ export async function readOptions( return { cancelled: true, error: emailOption.error, options }; } - email = { - git: emailOption.value, - github: emailOption.value, - npm: emailOption.value, - }; + email = { github: emailOption.value, npm: emailOption.value }; } const augmentedOptions = await augmentOptionsWithExcludes({ From 50c38dd05d55aea46f7baccf4f5eabbf29ca3b59 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 22:27:07 -0400 Subject: [PATCH 07/19] What it actually needed was to try the .md first... --- src/shared/options/createOptionDefaults/index.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/shared/options/createOptionDefaults/index.ts b/src/shared/options/createOptionDefaults/index.ts index bea3fde2..3f12736f 100644 --- a/src/shared/options/createOptionDefaults/index.ts +++ b/src/shared/options/createOptionDefaults/index.ts @@ -34,19 +34,19 @@ export function createOptionDefaults(promptedOptions?: PromptedOptions) { bin: async () => (await packageData()).bin, description: async () => (await packageData()).description, email: async () => { - const githubEmail = await readGitHubEmail(); - const gitEmail = await tryCatchAsync( - async () => (await $`git config --get user.email`).stdout, - ); + const githubEmail = + (await readGitHubEmail()) ?? + (await tryCatchAsync( + async () => (await $`git config --get user.email`).stdout, + )); const npmEmail = (await npmDefaults())?.email ?? (await packageAuthor()).email; /* eslint-disable @typescript-eslint/no-non-null-assertion */ - return gitEmail || githubEmail || npmEmail + return githubEmail || npmEmail ? { - git: (gitEmail || githubEmail || npmEmail)!, - github: (githubEmail || gitEmail || npmEmail)!, - npm: (npmEmail || gitEmail || githubEmail)!, + github: (githubEmail || npmEmail)!, + npm: (npmEmail || githubEmail)!, } : undefined; /* eslint-enable @typescript-eslint/no-non-null-assertion */ From fd8eb1df2be099e01593e464070b83a9ca954ba8 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 22:28:34 -0400 Subject: [PATCH 08/19] fix git/github email tests --- src/shared/options/createOptionDefaults/index.test.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/shared/options/createOptionDefaults/index.test.ts b/src/shared/options/createOptionDefaults/index.test.ts index bd05a1f3..519a477c 100644 --- a/src/shared/options/createOptionDefaults/index.test.ts +++ b/src/shared/options/createOptionDefaults/index.test.ts @@ -79,7 +79,6 @@ describe("createOptionDefaults", () => { const actual = await createOptionDefaults().email(); expect(actual).toEqual({ - git: "npm-username@test.com", github: "npm-username@test.com", npm: "npm-username@test.com", }); @@ -97,7 +96,6 @@ describe("createOptionDefaults", () => { const actual = await createOptionDefaults().email(); expect(actual).toEqual({ - git: "test@package.com", github: "test@package.com", npm: "test@package.com", }); @@ -111,7 +109,6 @@ describe("createOptionDefaults", () => { const actual = await createOptionDefaults().email(); expect(actual).toEqual({ - git: "github@test.com", github: "github@test.com", npm: "github@test.com", }); @@ -129,7 +126,6 @@ describe("createOptionDefaults", () => { const actual = await createOptionDefaults().email(); expect(actual).toEqual({ - git: "git@test.com", github: "git@test.com", npm: "git@test.com", }); @@ -148,7 +144,6 @@ describe("createOptionDefaults", () => { const actual = await createOptionDefaults().email(); expect(actual).toEqual({ - git: "git@test.com", github: "git@test.com", npm: "npm-username@test.com", }); @@ -167,7 +162,6 @@ describe("createOptionDefaults", () => { const actual = await createOptionDefaults().email(); expect(actual).toEqual({ - git: "git@test.com", github: "github@test.com", npm: "npm-username@test.com", }); From 71a74a874d046ab0043a563007494df840f8c8df Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 22:39:04 -0400 Subject: [PATCH 09/19] Fixed title, somehow --- .../options/createOptionDefaults/readDefaultsFromReadme.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts index 1a89c5df..d968d069 100644 --- a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts +++ b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts @@ -15,9 +15,7 @@ export function readDefaultsFromReadme() { ?.match(/src\s*=(.+)?\/>/)?.[1] ?.replaceAll(/^['"]|['"]$/g, ""), title: async () => - /^(?:# |)(.*?)(?:<\/h1>)?$/i - .exec(await readme())?.[1] - ?.trim() - .replace(/<[^>]+(?:>|$)/g, ""), + (/^(.+)<\/h1>/.exec(await readme()) || + /^# (.+)/.exec(await readme()))?.[1], }; } From 2813816118f0ed89802b0e6bb7d821e4443f1205 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 22:45:23 -0400 Subject: [PATCH 10/19] dear heavens function.name --- package.json | 2 +- src/shared/options/createOptionDefaults/index.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4d39f598..09e6f79b 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ }, "license": "MIT", "author": { - "name": "Josh Goldberg", + "name": "Josh Goldberg โœจ", "email": "npm@joshuakgoldberg.com" }, "type": "module", diff --git a/src/shared/options/createOptionDefaults/index.ts b/src/shared/options/createOptionDefaults/index.ts index 3f12736f..3e51daea 100644 --- a/src/shared/options/createOptionDefaults/index.ts +++ b/src/shared/options/createOptionDefaults/index.ts @@ -30,7 +30,8 @@ export function createOptionDefaults(promptedOptions?: PromptedOptions) { ); return { - author: async () => (await packageAuthor()).author ?? npmDefaults.name, + author: async () => + (await packageAuthor()).author ?? (await npmDefaults())?.name, bin: async () => (await packageData()).bin, description: async () => (await packageData()).description, email: async () => { From 2f2e3ed153cfc0886fc00679c5da5a5d4751f35d Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 22:50:22 -0400 Subject: [PATCH 11/19] ok linter --- .../options/createOptionDefaults/readDefaultsFromReadme.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts index d968d069..1ffc78d4 100644 --- a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts +++ b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts @@ -15,7 +15,7 @@ export function readDefaultsFromReadme() { ?.match(/src\s*=(.+)?\/>/)?.[1] ?.replaceAll(/^['"]|['"]$/g, ""), title: async () => - (/^(.+)<\/h1>/.exec(await readme()) || + (/^(.+)<\/h1>/.exec(await readme()) ?? /^# (.+)/.exec(await readme()))?.[1], }; } From 3e5d91f11f26c7b7a014846c90a07c5c086f01ff Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 23:11:50 -0400 Subject: [PATCH 12/19] author: author default over owner --- src/shared/options/readOptions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/shared/options/readOptions.ts b/src/shared/options/readOptions.ts index 46925d24..d13a5bfe 100644 --- a/src/shared/options/readOptions.ts +++ b/src/shared/options/readOptions.ts @@ -263,7 +263,8 @@ export async function readOptions( const augmentedOptions = await augmentOptionsWithExcludes({ ...options, access: options.access ?? "public", - author: options.author ?? (await defaults.owner()), + author: + options.author ?? (await defaults.author()) ?? (await defaults.owner()), bin: options.bin ?? (await defaults.bin()), description: options.description, directory: From 038ca05f0cd9c6112c91d1637422d5c350fa20dd Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 23:13:08 -0400 Subject: [PATCH 13/19] Bump up intro section close by 2 for \n\n --- src/steps/writeReadme/findIntroSectionClose.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/steps/writeReadme/findIntroSectionClose.ts b/src/steps/writeReadme/findIntroSectionClose.ts index 27d8bffd..38bc2033 100644 --- a/src/steps/writeReadme/findIntroSectionClose.ts +++ b/src/steps/writeReadme/findIntroSectionClose.ts @@ -5,7 +5,7 @@ export function findIntroSectionClose(contents: string) { const projectLogoMatch = /Project logo.+/.exec(contents); if (projectLogoMatch) { - return contents.indexOf("\n", projectLogoMatch.index) + 2; + return contents.indexOf("\n", projectLogoMatch.index + 2) + 2; } // Next: before a first code block or h2, presumably following badges From 17fa0c680cdf3b3d7e9d0df2679f4181b31e3879 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 23:21:12 -0400 Subject: [PATCH 14/19] Logo too --- .../readDefaultsFromReadme.test.ts | 41 +++++++++++++++++-- .../readDefaultsFromReadme.ts | 23 +++++++++-- src/shared/options/readOptions.ts | 2 +- 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts index ae708bce..c560844b 100644 --- a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts +++ b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts @@ -25,7 +25,10 @@ describe("readDefaultsFromReadme", () => { const logo = await readDefaultsFromReadme().logo(); - expect(logo).toBe("abc/def.jpg"); + expect(logo).toEqual({ + alt: "Project logo", + src: "abc/def.jpg", + }); }); it("parses when found in a single quoted string", async () => { @@ -33,7 +36,10 @@ describe("readDefaultsFromReadme", () => { const logo = await readDefaultsFromReadme().logo(); - expect(logo).toBe("abc/def.jpg"); + expect(logo).toEqual({ + alt: "Project logo", + src: "abc/def.jpg", + }); }); it("parses when found in a double quoted string", async () => { @@ -41,7 +47,36 @@ describe("readDefaultsFromReadme", () => { const logo = await readDefaultsFromReadme().logo(); - expect(logo).toBe("abc/def.jpg"); + expect(logo).toEqual({ + alt: "Project logo", + src: "abc/def.jpg", + }); + }); + + it("includes alt text when it exists in double quotes", async () => { + mockReadFileSafe.mockResolvedValue( + 'Project logo: a fancy circle', + ); + + const logo = await readDefaultsFromReadme().logo(); + + expect(logo).toEqual({ + alt: "Project logo: a fancy circle", + src: "abc/def.jpg", + }); + }); + + it("includes alt text when it exists in single quotes", async () => { + mockReadFileSafe.mockResolvedValue( + "Project logo: a fancy circle", + ); + + const logo = await readDefaultsFromReadme().logo(); + + expect(logo).toEqual({ + alt: "Project logo: a fancy circle", + src: "abc/def.jpg", + }); }); }); diff --git a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts index 1ffc78d4..b590477a 100644 --- a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts +++ b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts @@ -10,10 +10,25 @@ export function readDefaultsFromReadme() { ); return { - logo: async () => - (await imageTag()) - ?.match(/src\s*=(.+)?\/>/)?.[1] - ?.replaceAll(/^['"]|['"]$/g, ""), + logo: async () => { + const tag = await imageTag(); + if (!tag) { + return undefined; + } + + const src = /src\s*=(.+)?\/>/ + .exec(tag)?.[1] + ?.replaceAll(/^['"]|['"]$/g, ""); + + if (!src) { + return undefined; + } + + return { + alt: /alt=['"](.+)['"] src=['"].+['"]/.exec(tag)?.[1] ?? "Project logo", + src, + }; + }, title: async () => (/^(.+)<\/h1>/.exec(await readme()) ?? /^# (.+)/.exec(await readme()))?.[1], diff --git a/src/shared/options/readOptions.ts b/src/shared/options/readOptions.ts index d13a5bfe..4a491729 100644 --- a/src/shared/options/readOptions.ts +++ b/src/shared/options/readOptions.ts @@ -272,7 +272,7 @@ export async function readOptions( email, funding: options.funding ?? (await defaults.funding()), guide: guide ?? (await defaults.guide()), - logo, + logo: logo ?? (await defaults.logo()), mode, owner: options.owner, repository, From 3acc090bdcb91052a5da76c4a29a82f945b21d6e Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 23:27:21 -0400 Subject: [PATCH 15/19] logo should not be indented --- .../readDefaultsFromReadme.test.ts | 14 ++++++++++++++ .../createOptionDefaults/readDefaultsFromReadme.ts | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts index c560844b..a7694282 100644 --- a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts +++ b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts @@ -20,6 +20,20 @@ describe("readDefaultsFromReadme", () => { expect(logo).toBeUndefined(); }); + it("parses when found after a badge image", async () => { + mockReadFileSafe.mockResolvedValue(` + ๐Ÿ‘ช All Contributors: 48 + +`); + + const logo = await readDefaultsFromReadme().logo(); + + expect(logo).toEqual({ + alt: "Project logo", + src: "abc/def.jpg", + }); + }); + it("parses when found in an unquoted string", async () => { mockReadFileSafe.mockResolvedValue(""); diff --git a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts index b590477a..41822d70 100644 --- a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts +++ b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts @@ -6,7 +6,7 @@ export function readDefaultsFromReadme() { const readme = lazyValue(async () => await readFileSafe("README.md", "")); const imageTag = lazyValue( - async () => //.exec(await readme())?.[0], + async () => /(?:^|\n)/.exec(await readme())?.[0], ); return { From 1217e12901d2c6a8cbea0ee25a4029f83f5e0a37 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 23:42:48 -0400 Subject: [PATCH 16/19] Fixed up logo detection, again --- .../readDefaultsFromReadme.test.ts | 83 ++++++++++++++----- .../readDefaultsFromReadme.ts | 7 +- 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts index a7694282..f733e175 100644 --- a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts +++ b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.test.ts @@ -13,16 +13,17 @@ vi.mock("../../readFileSafe.js", () => ({ describe("readDefaultsFromReadme", () => { describe("logo", () => { it("defaults to undefined when it cannot be found", async () => { - mockReadFileSafe.mockResolvedValue("nothing."); + mockReadFileSafe.mockResolvedValue(` +nothing. +`); const logo = await readDefaultsFromReadme().logo(); expect(logo).toBeUndefined(); }); - it("parses when found after a badge image", async () => { + it("parses when found in an unquoted string", async () => { mockReadFileSafe.mockResolvedValue(` - ๐Ÿ‘ช All Contributors: 48 `); @@ -34,8 +35,10 @@ describe("readDefaultsFromReadme", () => { }); }); - it("parses when found in an unquoted string", async () => { - mockReadFileSafe.mockResolvedValue(""); + it("parses when found in a single quoted string", async () => { + mockReadFileSafe.mockResolvedValue(` + +`); const logo = await readDefaultsFromReadme().logo(); @@ -45,8 +48,10 @@ describe("readDefaultsFromReadme", () => { }); }); - it("parses when found in a single quoted string", async () => { - mockReadFileSafe.mockResolvedValue(""); + it("parses when found in a double quoted string", async () => { + mockReadFileSafe.mockResolvedValue(` + +`); const logo = await readDefaultsFromReadme().logo(); @@ -56,21 +61,23 @@ describe("readDefaultsFromReadme", () => { }); }); - it("parses when found in a double quoted string", async () => { - mockReadFileSafe.mockResolvedValue(''); + it("includes alt text when it exists in double quotes", async () => { + mockReadFileSafe.mockResolvedValue(` +Project logo: a fancy circle +`); const logo = await readDefaultsFromReadme().logo(); expect(logo).toEqual({ - alt: "Project logo", + alt: "Project logo: a fancy circle", src: "abc/def.jpg", }); }); - it("includes alt text when it exists in double quotes", async () => { - mockReadFileSafe.mockResolvedValue( - 'Project logo: a fancy circle', - ); + it("includes alt text when it exists in single quotes", async () => { + mockReadFileSafe.mockResolvedValue(` +Project logo: a fancy circle, +`); const logo = await readDefaultsFromReadme().logo(); @@ -80,23 +87,55 @@ describe("readDefaultsFromReadme", () => { }); }); - it("includes alt text when it exists in single quotes", async () => { - mockReadFileSafe.mockResolvedValue( - "Project logo: a fancy circle", - ); + it("parses when found after a badge image", async () => { + mockReadFileSafe.mockResolvedValue(` + ๐Ÿ‘ช All Contributors: 48 + +`); const logo = await readDefaultsFromReadme().logo(); expect(logo).toEqual({ - alt: "Project logo: a fancy circle", + alt: "Project logo", src: "abc/def.jpg", }); }); + + it("parses when found after an h1 and many badge images", async () => { + mockReadFileSafe.mockResolvedValue(`

Create TypeScript App

+ +

Quickstart-friendly TypeScript template with comprehensive, configurable, opinionated tooling. ๐Ÿ’

+ +

+ + + ๐Ÿ‘ช All Contributors: 48 + + + ๐Ÿค Code of Conduct: Kept + ๐Ÿงช Coverage + ๐Ÿ“ License: MIT + ๐Ÿ“ฆ npm version + ๐Ÿ’ช TypeScript: Strict +

+ +Project logo: the TypeScript blue square with rounded corners, but a plus sign instead of 'TS' +`); + + const logo = await readDefaultsFromReadme().logo(); + + expect(logo).toEqual({ + alt: "Project logo: the TypeScript blue square with rounded corners, but a plus sign instead of 'TS'", + src: "./docs/create-typescript-app.png", + }); + }); }); describe("title", () => { it("defaults to undefined when it cannot be found", async () => { - mockReadFileSafe.mockResolvedValue("nothing."); + mockReadFileSafe.mockResolvedValue(` +nothing. +`); const title = await readDefaultsFromReadme().title(); @@ -104,7 +143,7 @@ describe("readDefaultsFromReadme", () => { }); it('reads title as markdown from "README.md" when it exists', async () => { - mockReadFileSafe.mockResolvedValue("# My Awesome Package"); + mockReadFileSafe.mockResolvedValue(`# My Awesome Package`); const title = await readDefaultsFromReadme().title(); @@ -122,7 +161,7 @@ describe("readDefaultsFromReadme", () => { }); it("returns undefined when title does not exist", async () => { - mockReadFileSafe.mockResolvedValue(""); + mockReadFileSafe.mockResolvedValue(`Other text.`); const title = await readDefaultsFromReadme().title(); diff --git a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts index 41822d70..68afdf5f 100644 --- a/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts +++ b/src/shared/options/createOptionDefaults/readDefaultsFromReadme.ts @@ -6,17 +6,18 @@ export function readDefaultsFromReadme() { const readme = lazyValue(async () => await readFileSafe("README.md", "")); const imageTag = lazyValue( - async () => /(?:^|\n)/.exec(await readme())?.[0], + async () => /\n/.exec(await readme())?.[0], ); return { logo: async () => { const tag = await imageTag(); + if (!tag) { return undefined; } - const src = /src\s*=(.+)?\/>/ + const src = /src\s*=(.+)['"/]>/ .exec(tag)?.[1] ?.replaceAll(/^['"]|['"]$/g, ""); @@ -25,7 +26,7 @@ export function readDefaultsFromReadme() { } return { - alt: /alt=['"](.+)['"] src=['"].+['"]/.exec(tag)?.[1] ?? "Project logo", + alt: /alt=['"](.+)['"]\s*src=/.exec(tag)?.[1] ?? "Project logo", src, }; }, From cfc14a23118d3b7c4407ed434d3c01ae093c9992 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 23:46:05 -0400 Subject: [PATCH 17/19] Unnecessary + 2 --- .all-contributorsrc | 835 ++++++++---------- .github/codecov.yml | 5 - .github/workflows/ci.yml | 2 - .gitignore | 2 +- .prettierignore | 2 +- cspell.json | 10 +- eslint.config.js | 26 +- knip.json | 5 +- .../writeReadme/findIntroSectionClose.ts | 2 +- 9 files changed, 371 insertions(+), 518 deletions(-) delete mode 100644 .github/codecov.yml diff --git a/.all-contributorsrc b/.all-contributorsrc index bafd7ea6..e3c73793 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1,477 +1,362 @@ { - "badgeTemplate": "\t\"๐Ÿ‘ช\" src=\"https://img.shields.io/badge/%F0%9F%91%AA_all_contributors-<%= contributors.length %>-21bb42.svg\" />", - "commit": false, - "commitConvention": "angular", - "commitType": "docs", - "contributors": [ - { - "avatar_url": "https://avatars.githubusercontent.com/u/3335181?v=4", - "contributions": [ - "bug", - "code", - "maintenance", - "review", - "tool", - "doc", - "infra", - "test", - "ideas" - ], - "login": "JoshuaKGoldberg", - "name": "Josh Goldberg", - "profile": "http://www.joshuakgoldberg.com" - }, - { - "login": "sinchang", - "name": "Jeff Wen", - "avatar_url": "https://avatars.githubusercontent.com/u/3297859?v=4", - "profile": "https://sinchang.me", - "contributions": [ - "code" - ] - }, - { - "login": "Pinjasaur", - "name": "Paul Esch-Laurent", - "avatar_url": "https://avatars.githubusercontent.com/u/6335792?v=4", - "profile": "https://paulisaweso.me/", - "contributions": [ - "code" - ] - }, - { - "login": "NazCodeland", - "name": "NazCodeland", - "avatar_url": "https://avatars.githubusercontent.com/u/113494366?v=4", - "profile": "https://github.com/NazCodeland", - "contributions": [ - "code" - ] - }, - { - "login": "johnnyreilly", - "name": "John Reilly", - "avatar_url": "https://avatars.githubusercontent.com/u/1010525?v=4", - "profile": "https://blog.johnnyreilly.com/", - "contributions": [ - "code", - "ideas", - "bug", - "maintenance", - "doc" - ] - }, - { - "login": "webpro", - "name": "Lars Kappert", - "avatar_url": "https://avatars.githubusercontent.com/u/456426?v=4", - "profile": "https://webpro.nl", - "contributions": [ - "code" - ] - }, - { - "login": "RebeccaStevens", - "name": "Rebecca Stevens", - "avatar_url": "https://avatars.githubusercontent.com/u/7224206?v=4", - "profile": "https://github.com/RebeccaStevens", - "contributions": [ - "code", - "infra" - ] - }, - { - "login": "ronthetech", - "name": "Ron Jean-Francois", - "avatar_url": "https://avatars.githubusercontent.com/u/105710107?v=4", - "profile": "http://ronjeanfrancois.com", - "contributions": [ - "code", - "infra" - ] - }, - { - "login": "nowyDEV", - "name": "Dominik Nowik", - "avatar_url": "https://avatars.githubusercontent.com/u/12304307?v=4", - "profile": "https://github.com/nowyDEV", - "contributions": [ - "tool", - "code", - "ideas" - ] - }, - { - "login": "TAKANOME-DEV", - "name": "takanomedev", - "avatar_url": "https://avatars.githubusercontent.com/u/79809121?v=4", - "profile": "https://github.com/TAKANOME-DEV", - "contributions": [ - "code" - ] - }, - { - "login": "emday4prez", - "name": "Emerson", - "avatar_url": "https://avatars.githubusercontent.com/u/35363144?v=4", - "profile": "https://github.com/emday4prez", - "contributions": [ - "code" - ] - }, - { - "login": "jsjoeio", - "name": "Joe Previte", - "avatar_url": "https://avatars.githubusercontent.com/u/3806031?v=4", - "profile": "https://typescriptcourse.com/tutorials", - "contributions": [ - "bug", - "code" - ] - }, - { - "login": "navin-moorthy", - "name": "Navin Moorthy", - "avatar_url": "https://avatars.githubusercontent.com/u/39694575?v=4", - "profile": "https://navinmoorthy.me/", - "contributions": [ - "bug", - "code", - "ideas" - ] - }, - { - "login": "garuna-m6", - "name": "Anurag", - "avatar_url": "https://avatars.githubusercontent.com/u/23234342?v=4", - "profile": "https://github.com/garuna-m6", - "contributions": [ - "code" - ] - }, - { - "login": "danielroe", - "name": "Daniel Roe", - "avatar_url": "https://avatars.githubusercontent.com/u/28706372?v=4", - "profile": "https://roe.dev/", - "contributions": [ - "code" - ] - }, - { - "login": "the-lazy-learner", - "name": "Sudhansu", - "avatar_url": "https://avatars.githubusercontent.com/u/13695177?v=4", - "profile": "https://github.com/the-lazy-learner", - "contributions": [ - "code" - ] - }, - { - "login": "RNR1", - "name": "Ron Braha", - "avatar_url": "https://avatars.githubusercontent.com/u/45559220?v=4", - "profile": "https://linktr.ee/ronbraha", - "contributions": [ - "code", - "design", - "test" - ] - }, - { - "login": "tungbq", - "name": "Tung Bui (Leo)", - "avatar_url": "https://avatars.githubusercontent.com/u/85242618?v=4", - "profile": "https://github.com/tungbq", - "contributions": [ - "code" - ] - }, - { - "login": "orta", - "name": "Orta Therox", - "avatar_url": "https://avatars.githubusercontent.com/u/49038?v=4", - "profile": "https://orta.io", - "contributions": [ - "code" - ] - }, - { - "login": "promise-dash", - "name": "Promise Dash", - "avatar_url": "https://avatars.githubusercontent.com/u/86062880?v=4", - "profile": "https://github.com/promise-dash", - "contributions": [ - "code" - ] - }, - { - "login": "jolg42", - "name": "Joรซl Galeran", - "avatar_url": "https://avatars.githubusercontent.com/u/1328733?v=4", - "profile": "https://twitter.com/Jolg42", - "contributions": [ - "code" - ] - }, - { - "login": "kristo-baricevic", - "name": "Kristo Baricevic", - "avatar_url": "https://avatars.githubusercontent.com/u/108290619?v=4", - "profile": "https://kristo-baricevic.github.io/", - "contributions": [ - "code" - ] - }, - { - "login": "ryota-murakami", - "name": "Ryota Murakami", - "avatar_url": "https://avatars.githubusercontent.com/u/5501268?v=4", - "profile": "https://ryota-murakami.github.io/", - "contributions": [ - "code", - "bug" - ] - }, - { - "login": "ruthwikreddy09", - "name": "Ruthwik", - "avatar_url": "https://avatars.githubusercontent.com/u/126862059?v=4", - "profile": "https://github.com/RuthwikReddy09", - "contributions": [ - "code" - ] - }, - { - "login": "jdwilkin4", - "name": "Jessica Wilkins ", - "avatar_url": "https://avatars.githubusercontent.com/u/67210629?v=4", - "profile": "https://jessicawilkins.dev/", - "contributions": [ - "code" - ] - }, - { - "login": "vasanth9", - "name": "Vasanth Kumar Cheepurupalli", - "avatar_url": "https://avatars.githubusercontent.com/u/42891954?v=4", - "profile": "https://github.com/vasanth9", - "contributions": [ - "code" - ] - }, - { - "login": "conrmahr", - "name": "Conor Meagher", - "avatar_url": "https://avatars.githubusercontent.com/u/363781?v=4", - "profile": "https://conormeagher.com/", - "contributions": [ - "code" - ] - }, - { - "login": "DanexQ", - "name": "Daniel", - "avatar_url": "https://avatars.githubusercontent.com/u/72567464?v=4", - "profile": "https://github.com/DanexQ", - "contributions": [ - "infra" - ] - }, - { - "login": "jaas666", - "name": "Juan A.", - "avatar_url": "https://avatars.githubusercontent.com/u/30204147?v=4", - "profile": "https://github.com/jaas666", - "contributions": [ - "code", - "doc" - ] - }, - { - "login": "katt", - "name": "Alex / KATT", - "avatar_url": "https://avatars.githubusercontent.com/u/459267?v=4", - "profile": "https://katt.dev", - "contributions": [ - "bug" - ] - }, - { - "login": "dertimonius", - "name": "Timon Jurschitsch", - "avatar_url": "https://avatars.githubusercontent.com/u/103483059?v=4", - "profile": "https://www.linkedin.com/in/timonjurschitsch/", - "contributions": [ - "code" - ] - }, - { - "login": "biplobsd", - "name": "Biplob Sutradhar", - "avatar_url": "https://avatars.githubusercontent.com/u/43641536?v=4", - "profile": "http://biplobsd.me", - "contributions": [ - "code" - ] - }, - { - "login": "mrswastik-robot", - "name": "Swastik Patel", - "avatar_url": "https://avatars.githubusercontent.com/u/107865087?v=4", - "profile": "https://github.com/mrswastik-robot", - "contributions": [ - "doc" - ] - }, - { - "login": "gv14982", - "name": "Graham Vasquez", - "avatar_url": "https://avatars.githubusercontent.com/u/7041175?v=4", - "profile": "https://gvasquez.dev", - "contributions": [ - "code" - ] - }, - { - "login": "dominicduffin1", - "name": "Dominic Duffin", - "avatar_url": "https://avatars.githubusercontent.com/u/26224873?v=4", - "profile": "https://dominicduffin.uk", - "contributions": [ - "code" - ] - }, - { - "login": "5hraddha", - "name": "Shraddha", - "avatar_url": "https://avatars.githubusercontent.com/u/27571141?v=4", - "profile": "https://www.shraddha.tech", - "contributions": [ - "code" - ] - }, - { - "login": "xl4624", - "name": "Xiaomin Liu", - "avatar_url": "https://avatars.githubusercontent.com/u/116298054?v=4", - "profile": "https://github.com/xl4624", - "contributions": [ - "code" - ] - }, - { - "login": "jamiemagee", - "name": "Jamie Magee", - "avatar_url": "https://avatars.githubusercontent.com/u/1358764?v=4", - "profile": "https://jamiemagee.co.uk", - "contributions": [ - "ideas" - ] - }, - { - "login": "praveenshinde3", - "name": "Praveen Shinde", - "avatar_url": "https://avatars.githubusercontent.com/u/107350270?v=4", - "profile": "https://praveenshinde.vercel.app/", - "contributions": [ - "code" - ] - }, - { - "login": "aslemammad", - "name": "Mohammad Bagher Abiyat", - "avatar_url": "https://avatars.githubusercontent.com/u/37929992?v=4", - "profile": "https://github.com/Aslemammad", - "contributions": [ - "code" - ] - }, - { - "login": "lcforbes", - "name": "lcforbes", - "avatar_url": "https://avatars.githubusercontent.com/u/42080532?v=4", - "profile": "https://github.com/lcforbes", - "contributions": [ - "bug" - ] - }, - { - "login": "danvk", - "name": "Dan Vanderkam", - "avatar_url": "https://avatars.githubusercontent.com/u/98301?v=4", - "profile": "https://effectivetypescript.com", - "contributions": [ - "bug", - "ideas", - "tool" - ] - }, - { - "login": "nandertga", - "name": "nandertga", - "avatar_url": "https://avatars.githubusercontent.com/u/65074195?v=4", - "profile": "http://nandertga.ddns.net", - "contributions": [ - "code" - ] - }, - { - "login": "demianparkhomenko", - "name": "Demian Parkhomenko", - "avatar_url": "https://avatars.githubusercontent.com/u/95881717?v=4", - "profile": "https://linktr.ee/DemianParkhomenko", - "contributions": [ - "bug", - "code" - ] - }, - { - "login": "niklas-wortmann", - "name": "Jan-Niklas W.", - "avatar_url": "https://avatars.githubusercontent.com/u/6104311?v=4", - "profile": "https://niklas-wortmann.com", - "contributions": [ - "code" - ] - }, - { - "login": "rubiesonthesky", - "name": "rubiesonthesky", - "avatar_url": "https://avatars.githubusercontent.com/u/2591240?v=4", - "profile": "https://github.com/rubiesonthesky", - "contributions": [ - "ideas", - "code" - ] - }, - { - "login": "FarazPatankar", - "name": "Faraz Patankar", - "avatar_url": "https://avatars.githubusercontent.com/u/10681116?v=4", - "profile": "https://farazpatankar.com/", - "contributions": [ - "bug" - ] - }, - { - "login": "fb55", - "name": "Felix Boehm", - "avatar_url": "https://avatars.githubusercontent.com/u/188768?v=4", - "profile": "https://feedic.com", - "contributions": [ - "bug" - ] - } - ], - "contributorsPerLine": 7, - "contributorsSortAlphabetically": true, - "files": [ - "README.md" - ], - "imageSize": 100, - "projectName": "create-typescript-app", - "projectOwner": "JoshuaKGoldberg", - "repoHost": "https://github.com", - "repoType": "github" + "badgeTemplate": "\t\"๐Ÿ‘ช\" src=\"https://img.shields.io/badge/%F0%9F%91%AA_all_contributors-<%= contributors.length %>-21bb42.svg\" />", + "commit": false, + "commitConvention": "angular", + "commitType": "docs", + "contributors": [ + { + "avatar_url": "https://avatars.githubusercontent.com/u/3335181?v=4", + "contributions": [ + "bug", + "code", + "maintenance", + "review", + "tool", + "doc", + "infra", + "test", + "ideas" + ], + "login": "JoshuaKGoldberg", + "name": "Josh Goldberg", + "profile": "http://www.joshuakgoldberg.com" + }, + { + "login": "sinchang", + "name": "Jeff Wen", + "avatar_url": "https://avatars.githubusercontent.com/u/3297859?v=4", + "profile": "https://sinchang.me", + "contributions": ["code"] + }, + { + "login": "Pinjasaur", + "name": "Paul Esch-Laurent", + "avatar_url": "https://avatars.githubusercontent.com/u/6335792?v=4", + "profile": "https://paulisaweso.me/", + "contributions": ["code"] + }, + { + "login": "NazCodeland", + "name": "NazCodeland", + "avatar_url": "https://avatars.githubusercontent.com/u/113494366?v=4", + "profile": "https://github.com/NazCodeland", + "contributions": ["code"] + }, + { + "login": "johnnyreilly", + "name": "John Reilly", + "avatar_url": "https://avatars.githubusercontent.com/u/1010525?v=4", + "profile": "https://blog.johnnyreilly.com/", + "contributions": ["code", "ideas", "bug", "maintenance", "doc"] + }, + { + "login": "webpro", + "name": "Lars Kappert", + "avatar_url": "https://avatars.githubusercontent.com/u/456426?v=4", + "profile": "https://webpro.nl", + "contributions": ["code"] + }, + { + "login": "RebeccaStevens", + "name": "Rebecca Stevens", + "avatar_url": "https://avatars.githubusercontent.com/u/7224206?v=4", + "profile": "https://github.com/RebeccaStevens", + "contributions": ["code", "infra"] + }, + { + "login": "ronthetech", + "name": "Ron Jean-Francois", + "avatar_url": "https://avatars.githubusercontent.com/u/105710107?v=4", + "profile": "http://ronjeanfrancois.com", + "contributions": ["code", "infra"] + }, + { + "login": "nowyDEV", + "name": "Dominik Nowik", + "avatar_url": "https://avatars.githubusercontent.com/u/12304307?v=4", + "profile": "https://github.com/nowyDEV", + "contributions": ["tool", "code", "ideas"] + }, + { + "login": "TAKANOME-DEV", + "name": "takanomedev", + "avatar_url": "https://avatars.githubusercontent.com/u/79809121?v=4", + "profile": "https://github.com/TAKANOME-DEV", + "contributions": ["code"] + }, + { + "login": "emday4prez", + "name": "Emerson", + "avatar_url": "https://avatars.githubusercontent.com/u/35363144?v=4", + "profile": "https://github.com/emday4prez", + "contributions": ["code"] + }, + { + "login": "jsjoeio", + "name": "Joe Previte", + "avatar_url": "https://avatars.githubusercontent.com/u/3806031?v=4", + "profile": "https://typescriptcourse.com/tutorials", + "contributions": ["bug", "code"] + }, + { + "login": "navin-moorthy", + "name": "Navin Moorthy", + "avatar_url": "https://avatars.githubusercontent.com/u/39694575?v=4", + "profile": "https://navinmoorthy.me/", + "contributions": ["bug", "code", "ideas"] + }, + { + "login": "garuna-m6", + "name": "Anurag", + "avatar_url": "https://avatars.githubusercontent.com/u/23234342?v=4", + "profile": "https://github.com/garuna-m6", + "contributions": ["code"] + }, + { + "login": "danielroe", + "name": "Daniel Roe", + "avatar_url": "https://avatars.githubusercontent.com/u/28706372?v=4", + "profile": "https://roe.dev/", + "contributions": ["code"] + }, + { + "login": "the-lazy-learner", + "name": "Sudhansu", + "avatar_url": "https://avatars.githubusercontent.com/u/13695177?v=4", + "profile": "https://github.com/the-lazy-learner", + "contributions": ["code"] + }, + { + "login": "RNR1", + "name": "Ron Braha", + "avatar_url": "https://avatars.githubusercontent.com/u/45559220?v=4", + "profile": "https://linktr.ee/ronbraha", + "contributions": ["code", "design", "test"] + }, + { + "login": "tungbq", + "name": "Tung Bui (Leo)", + "avatar_url": "https://avatars.githubusercontent.com/u/85242618?v=4", + "profile": "https://github.com/tungbq", + "contributions": ["code"] + }, + { + "login": "orta", + "name": "Orta Therox", + "avatar_url": "https://avatars.githubusercontent.com/u/49038?v=4", + "profile": "https://orta.io", + "contributions": ["code"] + }, + { + "login": "promise-dash", + "name": "Promise Dash", + "avatar_url": "https://avatars.githubusercontent.com/u/86062880?v=4", + "profile": "https://github.com/promise-dash", + "contributions": ["code"] + }, + { + "login": "jolg42", + "name": "Joรซl Galeran", + "avatar_url": "https://avatars.githubusercontent.com/u/1328733?v=4", + "profile": "https://twitter.com/Jolg42", + "contributions": ["code"] + }, + { + "login": "kristo-baricevic", + "name": "Kristo Baricevic", + "avatar_url": "https://avatars.githubusercontent.com/u/108290619?v=4", + "profile": "https://kristo-baricevic.github.io/", + "contributions": ["code"] + }, + { + "login": "ryota-murakami", + "name": "Ryota Murakami", + "avatar_url": "https://avatars.githubusercontent.com/u/5501268?v=4", + "profile": "https://ryota-murakami.github.io/", + "contributions": ["code", "bug"] + }, + { + "login": "ruthwikreddy09", + "name": "Ruthwik", + "avatar_url": "https://avatars.githubusercontent.com/u/126862059?v=4", + "profile": "https://github.com/RuthwikReddy09", + "contributions": ["code"] + }, + { + "login": "jdwilkin4", + "name": "Jessica Wilkins ", + "avatar_url": "https://avatars.githubusercontent.com/u/67210629?v=4", + "profile": "https://jessicawilkins.dev/", + "contributions": ["code"] + }, + { + "login": "vasanth9", + "name": "Vasanth Kumar Cheepurupalli", + "avatar_url": "https://avatars.githubusercontent.com/u/42891954?v=4", + "profile": "https://github.com/vasanth9", + "contributions": ["code"] + }, + { + "login": "conrmahr", + "name": "Conor Meagher", + "avatar_url": "https://avatars.githubusercontent.com/u/363781?v=4", + "profile": "https://conormeagher.com/", + "contributions": ["code"] + }, + { + "login": "DanexQ", + "name": "Daniel", + "avatar_url": "https://avatars.githubusercontent.com/u/72567464?v=4", + "profile": "https://github.com/DanexQ", + "contributions": ["infra"] + }, + { + "login": "jaas666", + "name": "Juan A.", + "avatar_url": "https://avatars.githubusercontent.com/u/30204147?v=4", + "profile": "https://github.com/jaas666", + "contributions": ["code", "doc"] + }, + { + "login": "katt", + "name": "Alex / KATT", + "avatar_url": "https://avatars.githubusercontent.com/u/459267?v=4", + "profile": "https://katt.dev", + "contributions": ["bug"] + }, + { + "login": "dertimonius", + "name": "Timon Jurschitsch", + "avatar_url": "https://avatars.githubusercontent.com/u/103483059?v=4", + "profile": "https://www.linkedin.com/in/timonjurschitsch/", + "contributions": ["code"] + }, + { + "login": "biplobsd", + "name": "Biplob Sutradhar", + "avatar_url": "https://avatars.githubusercontent.com/u/43641536?v=4", + "profile": "http://biplobsd.me", + "contributions": ["code"] + }, + { + "login": "mrswastik-robot", + "name": "Swastik Patel", + "avatar_url": "https://avatars.githubusercontent.com/u/107865087?v=4", + "profile": "https://github.com/mrswastik-robot", + "contributions": ["doc"] + }, + { + "login": "gv14982", + "name": "Graham Vasquez", + "avatar_url": "https://avatars.githubusercontent.com/u/7041175?v=4", + "profile": "https://gvasquez.dev", + "contributions": ["code"] + }, + { + "login": "dominicduffin1", + "name": "Dominic Duffin", + "avatar_url": "https://avatars.githubusercontent.com/u/26224873?v=4", + "profile": "https://dominicduffin.uk", + "contributions": ["code"] + }, + { + "login": "5hraddha", + "name": "Shraddha", + "avatar_url": "https://avatars.githubusercontent.com/u/27571141?v=4", + "profile": "https://www.shraddha.tech", + "contributions": ["code"] + }, + { + "login": "xl4624", + "name": "Xiaomin Liu", + "avatar_url": "https://avatars.githubusercontent.com/u/116298054?v=4", + "profile": "https://github.com/xl4624", + "contributions": ["code"] + }, + { + "login": "jamiemagee", + "name": "Jamie Magee", + "avatar_url": "https://avatars.githubusercontent.com/u/1358764?v=4", + "profile": "https://jamiemagee.co.uk", + "contributions": ["ideas"] + }, + { + "login": "praveenshinde3", + "name": "Praveen Shinde", + "avatar_url": "https://avatars.githubusercontent.com/u/107350270?v=4", + "profile": "https://praveenshinde.vercel.app/", + "contributions": ["code"] + }, + { + "login": "aslemammad", + "name": "Mohammad Bagher Abiyat", + "avatar_url": "https://avatars.githubusercontent.com/u/37929992?v=4", + "profile": "https://github.com/Aslemammad", + "contributions": ["code"] + }, + { + "login": "lcforbes", + "name": "lcforbes", + "avatar_url": "https://avatars.githubusercontent.com/u/42080532?v=4", + "profile": "https://github.com/lcforbes", + "contributions": ["bug"] + }, + { + "login": "danvk", + "name": "Dan Vanderkam", + "avatar_url": "https://avatars.githubusercontent.com/u/98301?v=4", + "profile": "https://effectivetypescript.com", + "contributions": ["bug", "ideas", "tool"] + }, + { + "login": "nandertga", + "name": "nandertga", + "avatar_url": "https://avatars.githubusercontent.com/u/65074195?v=4", + "profile": "http://nandertga.ddns.net", + "contributions": ["code"] + }, + { + "login": "demianparkhomenko", + "name": "Demian Parkhomenko", + "avatar_url": "https://avatars.githubusercontent.com/u/95881717?v=4", + "profile": "https://linktr.ee/DemianParkhomenko", + "contributions": ["bug", "code"] + }, + { + "login": "niklas-wortmann", + "name": "Jan-Niklas W.", + "avatar_url": "https://avatars.githubusercontent.com/u/6104311?v=4", + "profile": "https://niklas-wortmann.com", + "contributions": ["code"] + }, + { + "login": "rubiesonthesky", + "name": "rubiesonthesky", + "avatar_url": "https://avatars.githubusercontent.com/u/2591240?v=4", + "profile": "https://github.com/rubiesonthesky", + "contributions": ["ideas", "code"] + }, + { + "login": "FarazPatankar", + "name": "Faraz Patankar", + "avatar_url": "https://avatars.githubusercontent.com/u/10681116?v=4", + "profile": "https://farazpatankar.com/", + "contributions": ["bug"] + }, + { + "login": "fb55", + "name": "Felix Boehm", + "avatar_url": "https://avatars.githubusercontent.com/u/188768?v=4", + "profile": "https://feedic.com", + "contributions": ["bug"] + } + ], + "contributorsPerLine": 7, + "contributorsSortAlphabetically": true, + "files": ["README.md"], + "imageSize": 100, + "projectName": "create-typescript-app", + "projectOwner": "JoshuaKGoldberg", + "repoHost": "https://github.com", + "repoType": "github" } diff --git a/.github/codecov.yml b/.github/codecov.yml deleted file mode 100644 index 70976385..00000000 --- a/.github/codecov.yml +++ /dev/null @@ -1,5 +0,0 @@ -codecov: - notify: - after_n_builds: 4 -comment: - after_n_builds: 4 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ded183f6..b8a1897e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,8 +58,6 @@ jobs: - uses: ./.github/actions/prepare - run: pnpm run test --coverage - uses: codecov/codecov-action@v3 - with: - flags: unit type_check: name: Type Check runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 32ffbca9..a8bff3bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -coverage*/ +coverage/ lib/ node_modules/ diff --git a/.prettierignore b/.prettierignore index 608dd8d9..b084585e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,5 @@ .all-contributorsrc .husky/ -coverage*/ +coverage/ lib/ pnpm-lock.yaml diff --git a/cspell.json b/cspell.json index fee2a6f1..bc91023a 100644 --- a/cspell.json +++ b/cspell.json @@ -1,10 +1,9 @@ { "dictionaries": ["typescript"], "ignorePaths": [ - "./coverage*", - "./script/__snapshots__", ".github", "CHANGELOG.md", + "coverage", "lib", "node_modules", "pnpm-lock.yaml" @@ -14,23 +13,24 @@ "apexskier", "arethetypeswrong", "automerge", - "codecov", "codespace", "contributorsrc", "execa", "infile", + "joshuakgoldberg", "knip", + "markdownlint", "markdownlintignore", "mtfoley", "npmignore", "npmpackagejsonlintrc", "outro", "packagejson", - "precommit", "quickstart", "tada", "tseslint", "tsup", - "vitest" + "vitest", + "wontfix" ] } diff --git a/eslint.config.js b/eslint.config.js index 1c3b9c99..fcc853cc 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,13 +1,3 @@ -/* -๐Ÿ‘‹ Hi! This ESLint configuration contains a lot more stuff than many repos'! -You can read from it to see all sorts of linting goodness, but don't worry - -it's not something you need to exhaustively understand immediately. ๐Ÿ’™ - -If you're interested in learning more, see the 'getting started' docs on: -- ESLint: https://eslint.org -- typescript-eslint: https://typescript-eslint.io -*/ - import eslint from "@eslint/js"; import comments from "@eslint-community/eslint-plugin-eslint-comments/configs"; import jsdoc from "eslint-plugin-jsdoc"; @@ -56,7 +46,7 @@ export default tseslint.config( languageOptions: { parserOptions: { projectService: { - allowDefaultProject: ["*.config.*s", "bin/*.js", "script/*.ts"], + allowDefaultProject: ["*.*s", "eslint.config.js"], defaultProject: "./tsconfig.json", }, tsconfigRootDir: import.meta.dirname, @@ -81,21 +71,7 @@ export default tseslint.config( "no-constant-condition": "off", // These on-by-default rules work well for this repo if configured - "@typescript-eslint/no-unnecessary-condition": [ - "error", - { - allowConstantLoopConditions: true, - }, - ], "@typescript-eslint/no-unused-vars": ["error", { caughtErrors: "all" }], - "@typescript-eslint/prefer-nullish-coalescing": [ - "error", - { ignorePrimitives: true }, - ], - "@typescript-eslint/restrict-template-expressions": [ - "error", - { allowBoolean: true, allowNullish: true, allowNumber: true }, - ], "n/no-unsupported-features/node-builtins": [ "error", { allowExperimental: true }, diff --git a/knip.json b/knip.json index 50718f20..d73a20f3 100644 --- a/knip.json +++ b/knip.json @@ -1,7 +1,6 @@ { "$schema": "https://unpkg.com/knip@latest/schema.json", - "entry": ["src/index.ts!", "script/*e2e.js"], - "ignoreBinaries": ["gh"], + "entry": ["src/index.ts!"], "ignoreExportsUsedInFile": { "interface": true, "type": true }, - "project": ["src/**/*.ts!", "script/**/*.js"] + "project": ["src/**/*.ts!"] } diff --git a/src/steps/writeReadme/findIntroSectionClose.ts b/src/steps/writeReadme/findIntroSectionClose.ts index 38bc2033..27d8bffd 100644 --- a/src/steps/writeReadme/findIntroSectionClose.ts +++ b/src/steps/writeReadme/findIntroSectionClose.ts @@ -5,7 +5,7 @@ export function findIntroSectionClose(contents: string) { const projectLogoMatch = /Project logo.+/.exec(contents); if (projectLogoMatch) { - return contents.indexOf("\n", projectLogoMatch.index + 2) + 2; + return contents.indexOf("\n", projectLogoMatch.index) + 2; } // Next: before a first code block or h2, presumably following badges From d884c4219d606176145ff0ee1d16e6f743c1e052 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 23:48:06 -0400 Subject: [PATCH 18/19] Revert "Unnecessary + 2" This reverts commit cfc14a23118d3b7c4407ed434d3c01ae093c9992. --- .all-contributorsrc | 835 ++++++++++-------- .github/codecov.yml | 5 + .github/workflows/ci.yml | 2 + .gitignore | 2 +- .prettierignore | 2 +- cspell.json | 10 +- eslint.config.js | 26 +- knip.json | 5 +- .../writeReadme/findIntroSectionClose.ts | 2 +- 9 files changed, 518 insertions(+), 371 deletions(-) create mode 100644 .github/codecov.yml diff --git a/.all-contributorsrc b/.all-contributorsrc index e3c73793..bafd7ea6 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1,362 +1,477 @@ { - "badgeTemplate": "\t\"๐Ÿ‘ช\" src=\"https://img.shields.io/badge/%F0%9F%91%AA_all_contributors-<%= contributors.length %>-21bb42.svg\" />", - "commit": false, - "commitConvention": "angular", - "commitType": "docs", - "contributors": [ - { - "avatar_url": "https://avatars.githubusercontent.com/u/3335181?v=4", - "contributions": [ - "bug", - "code", - "maintenance", - "review", - "tool", - "doc", - "infra", - "test", - "ideas" - ], - "login": "JoshuaKGoldberg", - "name": "Josh Goldberg", - "profile": "http://www.joshuakgoldberg.com" - }, - { - "login": "sinchang", - "name": "Jeff Wen", - "avatar_url": "https://avatars.githubusercontent.com/u/3297859?v=4", - "profile": "https://sinchang.me", - "contributions": ["code"] - }, - { - "login": "Pinjasaur", - "name": "Paul Esch-Laurent", - "avatar_url": "https://avatars.githubusercontent.com/u/6335792?v=4", - "profile": "https://paulisaweso.me/", - "contributions": ["code"] - }, - { - "login": "NazCodeland", - "name": "NazCodeland", - "avatar_url": "https://avatars.githubusercontent.com/u/113494366?v=4", - "profile": "https://github.com/NazCodeland", - "contributions": ["code"] - }, - { - "login": "johnnyreilly", - "name": "John Reilly", - "avatar_url": "https://avatars.githubusercontent.com/u/1010525?v=4", - "profile": "https://blog.johnnyreilly.com/", - "contributions": ["code", "ideas", "bug", "maintenance", "doc"] - }, - { - "login": "webpro", - "name": "Lars Kappert", - "avatar_url": "https://avatars.githubusercontent.com/u/456426?v=4", - "profile": "https://webpro.nl", - "contributions": ["code"] - }, - { - "login": "RebeccaStevens", - "name": "Rebecca Stevens", - "avatar_url": "https://avatars.githubusercontent.com/u/7224206?v=4", - "profile": "https://github.com/RebeccaStevens", - "contributions": ["code", "infra"] - }, - { - "login": "ronthetech", - "name": "Ron Jean-Francois", - "avatar_url": "https://avatars.githubusercontent.com/u/105710107?v=4", - "profile": "http://ronjeanfrancois.com", - "contributions": ["code", "infra"] - }, - { - "login": "nowyDEV", - "name": "Dominik Nowik", - "avatar_url": "https://avatars.githubusercontent.com/u/12304307?v=4", - "profile": "https://github.com/nowyDEV", - "contributions": ["tool", "code", "ideas"] - }, - { - "login": "TAKANOME-DEV", - "name": "takanomedev", - "avatar_url": "https://avatars.githubusercontent.com/u/79809121?v=4", - "profile": "https://github.com/TAKANOME-DEV", - "contributions": ["code"] - }, - { - "login": "emday4prez", - "name": "Emerson", - "avatar_url": "https://avatars.githubusercontent.com/u/35363144?v=4", - "profile": "https://github.com/emday4prez", - "contributions": ["code"] - }, - { - "login": "jsjoeio", - "name": "Joe Previte", - "avatar_url": "https://avatars.githubusercontent.com/u/3806031?v=4", - "profile": "https://typescriptcourse.com/tutorials", - "contributions": ["bug", "code"] - }, - { - "login": "navin-moorthy", - "name": "Navin Moorthy", - "avatar_url": "https://avatars.githubusercontent.com/u/39694575?v=4", - "profile": "https://navinmoorthy.me/", - "contributions": ["bug", "code", "ideas"] - }, - { - "login": "garuna-m6", - "name": "Anurag", - "avatar_url": "https://avatars.githubusercontent.com/u/23234342?v=4", - "profile": "https://github.com/garuna-m6", - "contributions": ["code"] - }, - { - "login": "danielroe", - "name": "Daniel Roe", - "avatar_url": "https://avatars.githubusercontent.com/u/28706372?v=4", - "profile": "https://roe.dev/", - "contributions": ["code"] - }, - { - "login": "the-lazy-learner", - "name": "Sudhansu", - "avatar_url": "https://avatars.githubusercontent.com/u/13695177?v=4", - "profile": "https://github.com/the-lazy-learner", - "contributions": ["code"] - }, - { - "login": "RNR1", - "name": "Ron Braha", - "avatar_url": "https://avatars.githubusercontent.com/u/45559220?v=4", - "profile": "https://linktr.ee/ronbraha", - "contributions": ["code", "design", "test"] - }, - { - "login": "tungbq", - "name": "Tung Bui (Leo)", - "avatar_url": "https://avatars.githubusercontent.com/u/85242618?v=4", - "profile": "https://github.com/tungbq", - "contributions": ["code"] - }, - { - "login": "orta", - "name": "Orta Therox", - "avatar_url": "https://avatars.githubusercontent.com/u/49038?v=4", - "profile": "https://orta.io", - "contributions": ["code"] - }, - { - "login": "promise-dash", - "name": "Promise Dash", - "avatar_url": "https://avatars.githubusercontent.com/u/86062880?v=4", - "profile": "https://github.com/promise-dash", - "contributions": ["code"] - }, - { - "login": "jolg42", - "name": "Joรซl Galeran", - "avatar_url": "https://avatars.githubusercontent.com/u/1328733?v=4", - "profile": "https://twitter.com/Jolg42", - "contributions": ["code"] - }, - { - "login": "kristo-baricevic", - "name": "Kristo Baricevic", - "avatar_url": "https://avatars.githubusercontent.com/u/108290619?v=4", - "profile": "https://kristo-baricevic.github.io/", - "contributions": ["code"] - }, - { - "login": "ryota-murakami", - "name": "Ryota Murakami", - "avatar_url": "https://avatars.githubusercontent.com/u/5501268?v=4", - "profile": "https://ryota-murakami.github.io/", - "contributions": ["code", "bug"] - }, - { - "login": "ruthwikreddy09", - "name": "Ruthwik", - "avatar_url": "https://avatars.githubusercontent.com/u/126862059?v=4", - "profile": "https://github.com/RuthwikReddy09", - "contributions": ["code"] - }, - { - "login": "jdwilkin4", - "name": "Jessica Wilkins ", - "avatar_url": "https://avatars.githubusercontent.com/u/67210629?v=4", - "profile": "https://jessicawilkins.dev/", - "contributions": ["code"] - }, - { - "login": "vasanth9", - "name": "Vasanth Kumar Cheepurupalli", - "avatar_url": "https://avatars.githubusercontent.com/u/42891954?v=4", - "profile": "https://github.com/vasanth9", - "contributions": ["code"] - }, - { - "login": "conrmahr", - "name": "Conor Meagher", - "avatar_url": "https://avatars.githubusercontent.com/u/363781?v=4", - "profile": "https://conormeagher.com/", - "contributions": ["code"] - }, - { - "login": "DanexQ", - "name": "Daniel", - "avatar_url": "https://avatars.githubusercontent.com/u/72567464?v=4", - "profile": "https://github.com/DanexQ", - "contributions": ["infra"] - }, - { - "login": "jaas666", - "name": "Juan A.", - "avatar_url": "https://avatars.githubusercontent.com/u/30204147?v=4", - "profile": "https://github.com/jaas666", - "contributions": ["code", "doc"] - }, - { - "login": "katt", - "name": "Alex / KATT", - "avatar_url": "https://avatars.githubusercontent.com/u/459267?v=4", - "profile": "https://katt.dev", - "contributions": ["bug"] - }, - { - "login": "dertimonius", - "name": "Timon Jurschitsch", - "avatar_url": "https://avatars.githubusercontent.com/u/103483059?v=4", - "profile": "https://www.linkedin.com/in/timonjurschitsch/", - "contributions": ["code"] - }, - { - "login": "biplobsd", - "name": "Biplob Sutradhar", - "avatar_url": "https://avatars.githubusercontent.com/u/43641536?v=4", - "profile": "http://biplobsd.me", - "contributions": ["code"] - }, - { - "login": "mrswastik-robot", - "name": "Swastik Patel", - "avatar_url": "https://avatars.githubusercontent.com/u/107865087?v=4", - "profile": "https://github.com/mrswastik-robot", - "contributions": ["doc"] - }, - { - "login": "gv14982", - "name": "Graham Vasquez", - "avatar_url": "https://avatars.githubusercontent.com/u/7041175?v=4", - "profile": "https://gvasquez.dev", - "contributions": ["code"] - }, - { - "login": "dominicduffin1", - "name": "Dominic Duffin", - "avatar_url": "https://avatars.githubusercontent.com/u/26224873?v=4", - "profile": "https://dominicduffin.uk", - "contributions": ["code"] - }, - { - "login": "5hraddha", - "name": "Shraddha", - "avatar_url": "https://avatars.githubusercontent.com/u/27571141?v=4", - "profile": "https://www.shraddha.tech", - "contributions": ["code"] - }, - { - "login": "xl4624", - "name": "Xiaomin Liu", - "avatar_url": "https://avatars.githubusercontent.com/u/116298054?v=4", - "profile": "https://github.com/xl4624", - "contributions": ["code"] - }, - { - "login": "jamiemagee", - "name": "Jamie Magee", - "avatar_url": "https://avatars.githubusercontent.com/u/1358764?v=4", - "profile": "https://jamiemagee.co.uk", - "contributions": ["ideas"] - }, - { - "login": "praveenshinde3", - "name": "Praveen Shinde", - "avatar_url": "https://avatars.githubusercontent.com/u/107350270?v=4", - "profile": "https://praveenshinde.vercel.app/", - "contributions": ["code"] - }, - { - "login": "aslemammad", - "name": "Mohammad Bagher Abiyat", - "avatar_url": "https://avatars.githubusercontent.com/u/37929992?v=4", - "profile": "https://github.com/Aslemammad", - "contributions": ["code"] - }, - { - "login": "lcforbes", - "name": "lcforbes", - "avatar_url": "https://avatars.githubusercontent.com/u/42080532?v=4", - "profile": "https://github.com/lcforbes", - "contributions": ["bug"] - }, - { - "login": "danvk", - "name": "Dan Vanderkam", - "avatar_url": "https://avatars.githubusercontent.com/u/98301?v=4", - "profile": "https://effectivetypescript.com", - "contributions": ["bug", "ideas", "tool"] - }, - { - "login": "nandertga", - "name": "nandertga", - "avatar_url": "https://avatars.githubusercontent.com/u/65074195?v=4", - "profile": "http://nandertga.ddns.net", - "contributions": ["code"] - }, - { - "login": "demianparkhomenko", - "name": "Demian Parkhomenko", - "avatar_url": "https://avatars.githubusercontent.com/u/95881717?v=4", - "profile": "https://linktr.ee/DemianParkhomenko", - "contributions": ["bug", "code"] - }, - { - "login": "niklas-wortmann", - "name": "Jan-Niklas W.", - "avatar_url": "https://avatars.githubusercontent.com/u/6104311?v=4", - "profile": "https://niklas-wortmann.com", - "contributions": ["code"] - }, - { - "login": "rubiesonthesky", - "name": "rubiesonthesky", - "avatar_url": "https://avatars.githubusercontent.com/u/2591240?v=4", - "profile": "https://github.com/rubiesonthesky", - "contributions": ["ideas", "code"] - }, - { - "login": "FarazPatankar", - "name": "Faraz Patankar", - "avatar_url": "https://avatars.githubusercontent.com/u/10681116?v=4", - "profile": "https://farazpatankar.com/", - "contributions": ["bug"] - }, - { - "login": "fb55", - "name": "Felix Boehm", - "avatar_url": "https://avatars.githubusercontent.com/u/188768?v=4", - "profile": "https://feedic.com", - "contributions": ["bug"] - } - ], - "contributorsPerLine": 7, - "contributorsSortAlphabetically": true, - "files": ["README.md"], - "imageSize": 100, - "projectName": "create-typescript-app", - "projectOwner": "JoshuaKGoldberg", - "repoHost": "https://github.com", - "repoType": "github" + "badgeTemplate": "\t\"๐Ÿ‘ช\" src=\"https://img.shields.io/badge/%F0%9F%91%AA_all_contributors-<%= contributors.length %>-21bb42.svg\" />", + "commit": false, + "commitConvention": "angular", + "commitType": "docs", + "contributors": [ + { + "avatar_url": "https://avatars.githubusercontent.com/u/3335181?v=4", + "contributions": [ + "bug", + "code", + "maintenance", + "review", + "tool", + "doc", + "infra", + "test", + "ideas" + ], + "login": "JoshuaKGoldberg", + "name": "Josh Goldberg", + "profile": "http://www.joshuakgoldberg.com" + }, + { + "login": "sinchang", + "name": "Jeff Wen", + "avatar_url": "https://avatars.githubusercontent.com/u/3297859?v=4", + "profile": "https://sinchang.me", + "contributions": [ + "code" + ] + }, + { + "login": "Pinjasaur", + "name": "Paul Esch-Laurent", + "avatar_url": "https://avatars.githubusercontent.com/u/6335792?v=4", + "profile": "https://paulisaweso.me/", + "contributions": [ + "code" + ] + }, + { + "login": "NazCodeland", + "name": "NazCodeland", + "avatar_url": "https://avatars.githubusercontent.com/u/113494366?v=4", + "profile": "https://github.com/NazCodeland", + "contributions": [ + "code" + ] + }, + { + "login": "johnnyreilly", + "name": "John Reilly", + "avatar_url": "https://avatars.githubusercontent.com/u/1010525?v=4", + "profile": "https://blog.johnnyreilly.com/", + "contributions": [ + "code", + "ideas", + "bug", + "maintenance", + "doc" + ] + }, + { + "login": "webpro", + "name": "Lars Kappert", + "avatar_url": "https://avatars.githubusercontent.com/u/456426?v=4", + "profile": "https://webpro.nl", + "contributions": [ + "code" + ] + }, + { + "login": "RebeccaStevens", + "name": "Rebecca Stevens", + "avatar_url": "https://avatars.githubusercontent.com/u/7224206?v=4", + "profile": "https://github.com/RebeccaStevens", + "contributions": [ + "code", + "infra" + ] + }, + { + "login": "ronthetech", + "name": "Ron Jean-Francois", + "avatar_url": "https://avatars.githubusercontent.com/u/105710107?v=4", + "profile": "http://ronjeanfrancois.com", + "contributions": [ + "code", + "infra" + ] + }, + { + "login": "nowyDEV", + "name": "Dominik Nowik", + "avatar_url": "https://avatars.githubusercontent.com/u/12304307?v=4", + "profile": "https://github.com/nowyDEV", + "contributions": [ + "tool", + "code", + "ideas" + ] + }, + { + "login": "TAKANOME-DEV", + "name": "takanomedev", + "avatar_url": "https://avatars.githubusercontent.com/u/79809121?v=4", + "profile": "https://github.com/TAKANOME-DEV", + "contributions": [ + "code" + ] + }, + { + "login": "emday4prez", + "name": "Emerson", + "avatar_url": "https://avatars.githubusercontent.com/u/35363144?v=4", + "profile": "https://github.com/emday4prez", + "contributions": [ + "code" + ] + }, + { + "login": "jsjoeio", + "name": "Joe Previte", + "avatar_url": "https://avatars.githubusercontent.com/u/3806031?v=4", + "profile": "https://typescriptcourse.com/tutorials", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "navin-moorthy", + "name": "Navin Moorthy", + "avatar_url": "https://avatars.githubusercontent.com/u/39694575?v=4", + "profile": "https://navinmoorthy.me/", + "contributions": [ + "bug", + "code", + "ideas" + ] + }, + { + "login": "garuna-m6", + "name": "Anurag", + "avatar_url": "https://avatars.githubusercontent.com/u/23234342?v=4", + "profile": "https://github.com/garuna-m6", + "contributions": [ + "code" + ] + }, + { + "login": "danielroe", + "name": "Daniel Roe", + "avatar_url": "https://avatars.githubusercontent.com/u/28706372?v=4", + "profile": "https://roe.dev/", + "contributions": [ + "code" + ] + }, + { + "login": "the-lazy-learner", + "name": "Sudhansu", + "avatar_url": "https://avatars.githubusercontent.com/u/13695177?v=4", + "profile": "https://github.com/the-lazy-learner", + "contributions": [ + "code" + ] + }, + { + "login": "RNR1", + "name": "Ron Braha", + "avatar_url": "https://avatars.githubusercontent.com/u/45559220?v=4", + "profile": "https://linktr.ee/ronbraha", + "contributions": [ + "code", + "design", + "test" + ] + }, + { + "login": "tungbq", + "name": "Tung Bui (Leo)", + "avatar_url": "https://avatars.githubusercontent.com/u/85242618?v=4", + "profile": "https://github.com/tungbq", + "contributions": [ + "code" + ] + }, + { + "login": "orta", + "name": "Orta Therox", + "avatar_url": "https://avatars.githubusercontent.com/u/49038?v=4", + "profile": "https://orta.io", + "contributions": [ + "code" + ] + }, + { + "login": "promise-dash", + "name": "Promise Dash", + "avatar_url": "https://avatars.githubusercontent.com/u/86062880?v=4", + "profile": "https://github.com/promise-dash", + "contributions": [ + "code" + ] + }, + { + "login": "jolg42", + "name": "Joรซl Galeran", + "avatar_url": "https://avatars.githubusercontent.com/u/1328733?v=4", + "profile": "https://twitter.com/Jolg42", + "contributions": [ + "code" + ] + }, + { + "login": "kristo-baricevic", + "name": "Kristo Baricevic", + "avatar_url": "https://avatars.githubusercontent.com/u/108290619?v=4", + "profile": "https://kristo-baricevic.github.io/", + "contributions": [ + "code" + ] + }, + { + "login": "ryota-murakami", + "name": "Ryota Murakami", + "avatar_url": "https://avatars.githubusercontent.com/u/5501268?v=4", + "profile": "https://ryota-murakami.github.io/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "ruthwikreddy09", + "name": "Ruthwik", + "avatar_url": "https://avatars.githubusercontent.com/u/126862059?v=4", + "profile": "https://github.com/RuthwikReddy09", + "contributions": [ + "code" + ] + }, + { + "login": "jdwilkin4", + "name": "Jessica Wilkins ", + "avatar_url": "https://avatars.githubusercontent.com/u/67210629?v=4", + "profile": "https://jessicawilkins.dev/", + "contributions": [ + "code" + ] + }, + { + "login": "vasanth9", + "name": "Vasanth Kumar Cheepurupalli", + "avatar_url": "https://avatars.githubusercontent.com/u/42891954?v=4", + "profile": "https://github.com/vasanth9", + "contributions": [ + "code" + ] + }, + { + "login": "conrmahr", + "name": "Conor Meagher", + "avatar_url": "https://avatars.githubusercontent.com/u/363781?v=4", + "profile": "https://conormeagher.com/", + "contributions": [ + "code" + ] + }, + { + "login": "DanexQ", + "name": "Daniel", + "avatar_url": "https://avatars.githubusercontent.com/u/72567464?v=4", + "profile": "https://github.com/DanexQ", + "contributions": [ + "infra" + ] + }, + { + "login": "jaas666", + "name": "Juan A.", + "avatar_url": "https://avatars.githubusercontent.com/u/30204147?v=4", + "profile": "https://github.com/jaas666", + "contributions": [ + "code", + "doc" + ] + }, + { + "login": "katt", + "name": "Alex / KATT", + "avatar_url": "https://avatars.githubusercontent.com/u/459267?v=4", + "profile": "https://katt.dev", + "contributions": [ + "bug" + ] + }, + { + "login": "dertimonius", + "name": "Timon Jurschitsch", + "avatar_url": "https://avatars.githubusercontent.com/u/103483059?v=4", + "profile": "https://www.linkedin.com/in/timonjurschitsch/", + "contributions": [ + "code" + ] + }, + { + "login": "biplobsd", + "name": "Biplob Sutradhar", + "avatar_url": "https://avatars.githubusercontent.com/u/43641536?v=4", + "profile": "http://biplobsd.me", + "contributions": [ + "code" + ] + }, + { + "login": "mrswastik-robot", + "name": "Swastik Patel", + "avatar_url": "https://avatars.githubusercontent.com/u/107865087?v=4", + "profile": "https://github.com/mrswastik-robot", + "contributions": [ + "doc" + ] + }, + { + "login": "gv14982", + "name": "Graham Vasquez", + "avatar_url": "https://avatars.githubusercontent.com/u/7041175?v=4", + "profile": "https://gvasquez.dev", + "contributions": [ + "code" + ] + }, + { + "login": "dominicduffin1", + "name": "Dominic Duffin", + "avatar_url": "https://avatars.githubusercontent.com/u/26224873?v=4", + "profile": "https://dominicduffin.uk", + "contributions": [ + "code" + ] + }, + { + "login": "5hraddha", + "name": "Shraddha", + "avatar_url": "https://avatars.githubusercontent.com/u/27571141?v=4", + "profile": "https://www.shraddha.tech", + "contributions": [ + "code" + ] + }, + { + "login": "xl4624", + "name": "Xiaomin Liu", + "avatar_url": "https://avatars.githubusercontent.com/u/116298054?v=4", + "profile": "https://github.com/xl4624", + "contributions": [ + "code" + ] + }, + { + "login": "jamiemagee", + "name": "Jamie Magee", + "avatar_url": "https://avatars.githubusercontent.com/u/1358764?v=4", + "profile": "https://jamiemagee.co.uk", + "contributions": [ + "ideas" + ] + }, + { + "login": "praveenshinde3", + "name": "Praveen Shinde", + "avatar_url": "https://avatars.githubusercontent.com/u/107350270?v=4", + "profile": "https://praveenshinde.vercel.app/", + "contributions": [ + "code" + ] + }, + { + "login": "aslemammad", + "name": "Mohammad Bagher Abiyat", + "avatar_url": "https://avatars.githubusercontent.com/u/37929992?v=4", + "profile": "https://github.com/Aslemammad", + "contributions": [ + "code" + ] + }, + { + "login": "lcforbes", + "name": "lcforbes", + "avatar_url": "https://avatars.githubusercontent.com/u/42080532?v=4", + "profile": "https://github.com/lcforbes", + "contributions": [ + "bug" + ] + }, + { + "login": "danvk", + "name": "Dan Vanderkam", + "avatar_url": "https://avatars.githubusercontent.com/u/98301?v=4", + "profile": "https://effectivetypescript.com", + "contributions": [ + "bug", + "ideas", + "tool" + ] + }, + { + "login": "nandertga", + "name": "nandertga", + "avatar_url": "https://avatars.githubusercontent.com/u/65074195?v=4", + "profile": "http://nandertga.ddns.net", + "contributions": [ + "code" + ] + }, + { + "login": "demianparkhomenko", + "name": "Demian Parkhomenko", + "avatar_url": "https://avatars.githubusercontent.com/u/95881717?v=4", + "profile": "https://linktr.ee/DemianParkhomenko", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "niklas-wortmann", + "name": "Jan-Niklas W.", + "avatar_url": "https://avatars.githubusercontent.com/u/6104311?v=4", + "profile": "https://niklas-wortmann.com", + "contributions": [ + "code" + ] + }, + { + "login": "rubiesonthesky", + "name": "rubiesonthesky", + "avatar_url": "https://avatars.githubusercontent.com/u/2591240?v=4", + "profile": "https://github.com/rubiesonthesky", + "contributions": [ + "ideas", + "code" + ] + }, + { + "login": "FarazPatankar", + "name": "Faraz Patankar", + "avatar_url": "https://avatars.githubusercontent.com/u/10681116?v=4", + "profile": "https://farazpatankar.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "fb55", + "name": "Felix Boehm", + "avatar_url": "https://avatars.githubusercontent.com/u/188768?v=4", + "profile": "https://feedic.com", + "contributions": [ + "bug" + ] + } + ], + "contributorsPerLine": 7, + "contributorsSortAlphabetically": true, + "files": [ + "README.md" + ], + "imageSize": 100, + "projectName": "create-typescript-app", + "projectOwner": "JoshuaKGoldberg", + "repoHost": "https://github.com", + "repoType": "github" } diff --git a/.github/codecov.yml b/.github/codecov.yml new file mode 100644 index 00000000..70976385 --- /dev/null +++ b/.github/codecov.yml @@ -0,0 +1,5 @@ +codecov: + notify: + after_n_builds: 4 +comment: + after_n_builds: 4 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8a1897e..ded183f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,6 +58,8 @@ jobs: - uses: ./.github/actions/prepare - run: pnpm run test --coverage - uses: codecov/codecov-action@v3 + with: + flags: unit type_check: name: Type Check runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index a8bff3bd..32ffbca9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -coverage/ +coverage*/ lib/ node_modules/ diff --git a/.prettierignore b/.prettierignore index b084585e..608dd8d9 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,5 @@ .all-contributorsrc .husky/ -coverage/ +coverage*/ lib/ pnpm-lock.yaml diff --git a/cspell.json b/cspell.json index bc91023a..fee2a6f1 100644 --- a/cspell.json +++ b/cspell.json @@ -1,9 +1,10 @@ { "dictionaries": ["typescript"], "ignorePaths": [ + "./coverage*", + "./script/__snapshots__", ".github", "CHANGELOG.md", - "coverage", "lib", "node_modules", "pnpm-lock.yaml" @@ -13,24 +14,23 @@ "apexskier", "arethetypeswrong", "automerge", + "codecov", "codespace", "contributorsrc", "execa", "infile", - "joshuakgoldberg", "knip", - "markdownlint", "markdownlintignore", "mtfoley", "npmignore", "npmpackagejsonlintrc", "outro", "packagejson", + "precommit", "quickstart", "tada", "tseslint", "tsup", - "vitest", - "wontfix" + "vitest" ] } diff --git a/eslint.config.js b/eslint.config.js index fcc853cc..1c3b9c99 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,3 +1,13 @@ +/* +๐Ÿ‘‹ Hi! This ESLint configuration contains a lot more stuff than many repos'! +You can read from it to see all sorts of linting goodness, but don't worry - +it's not something you need to exhaustively understand immediately. ๐Ÿ’™ + +If you're interested in learning more, see the 'getting started' docs on: +- ESLint: https://eslint.org +- typescript-eslint: https://typescript-eslint.io +*/ + import eslint from "@eslint/js"; import comments from "@eslint-community/eslint-plugin-eslint-comments/configs"; import jsdoc from "eslint-plugin-jsdoc"; @@ -46,7 +56,7 @@ export default tseslint.config( languageOptions: { parserOptions: { projectService: { - allowDefaultProject: ["*.*s", "eslint.config.js"], + allowDefaultProject: ["*.config.*s", "bin/*.js", "script/*.ts"], defaultProject: "./tsconfig.json", }, tsconfigRootDir: import.meta.dirname, @@ -71,7 +81,21 @@ export default tseslint.config( "no-constant-condition": "off", // These on-by-default rules work well for this repo if configured + "@typescript-eslint/no-unnecessary-condition": [ + "error", + { + allowConstantLoopConditions: true, + }, + ], "@typescript-eslint/no-unused-vars": ["error", { caughtErrors: "all" }], + "@typescript-eslint/prefer-nullish-coalescing": [ + "error", + { ignorePrimitives: true }, + ], + "@typescript-eslint/restrict-template-expressions": [ + "error", + { allowBoolean: true, allowNullish: true, allowNumber: true }, + ], "n/no-unsupported-features/node-builtins": [ "error", { allowExperimental: true }, diff --git a/knip.json b/knip.json index d73a20f3..50718f20 100644 --- a/knip.json +++ b/knip.json @@ -1,6 +1,7 @@ { "$schema": "https://unpkg.com/knip@latest/schema.json", - "entry": ["src/index.ts!"], + "entry": ["src/index.ts!", "script/*e2e.js"], + "ignoreBinaries": ["gh"], "ignoreExportsUsedInFile": { "interface": true, "type": true }, - "project": ["src/**/*.ts!"] + "project": ["src/**/*.ts!", "script/**/*.js"] } diff --git a/src/steps/writeReadme/findIntroSectionClose.ts b/src/steps/writeReadme/findIntroSectionClose.ts index 27d8bffd..38bc2033 100644 --- a/src/steps/writeReadme/findIntroSectionClose.ts +++ b/src/steps/writeReadme/findIntroSectionClose.ts @@ -5,7 +5,7 @@ export function findIntroSectionClose(contents: string) { const projectLogoMatch = /Project logo.+/.exec(contents); if (projectLogoMatch) { - return contents.indexOf("\n", projectLogoMatch.index) + 2; + return contents.indexOf("\n", projectLogoMatch.index + 2) + 2; } // Next: before a first code block or h2, presumably following badges From 973f8222f75ebeafc97573d2c8119a3244c8ba20 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 10 Aug 2024 23:48:24 -0400 Subject: [PATCH 19/19] Unnecessary + 2, this time no artifacts --- src/steps/writeReadme/findIntroSectionClose.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/steps/writeReadme/findIntroSectionClose.ts b/src/steps/writeReadme/findIntroSectionClose.ts index 38bc2033..27d8bffd 100644 --- a/src/steps/writeReadme/findIntroSectionClose.ts +++ b/src/steps/writeReadme/findIntroSectionClose.ts @@ -5,7 +5,7 @@ export function findIntroSectionClose(contents: string) { const projectLogoMatch = /Project logo.+/.exec(contents); if (projectLogoMatch) { - return contents.indexOf("\n", projectLogoMatch.index + 2) + 2; + return contents.indexOf("\n", projectLogoMatch.index) + 2; } // Next: before a first code block or h2, presumably following badges