diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 346cc5e35..07fbfb32d 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -37,7 +37,7 @@ module.exports = {
files: ["**/*.ts"],
parser: "@typescript-eslint/parser",
rules: {
- // These off-by-default rules work well for this repo and we like them on.
+ // These off-/differently-configured-by-default rules work well for this repo and we like them on.
"jsdoc/informative-docs": "error",
"logical-assignment-operators": [
"error",
@@ -53,6 +53,15 @@ module.exports = {
"jsdoc/require-returns": "off",
},
},
+ {
+ files: "**/*.md/*.ts",
+ rules: {
+ "n/no-missing-import": [
+ "error",
+ { allowModules: ["create-typescript-app"] },
+ ],
+ },
+ },
{
excludedFiles: ["**/*.md/*.ts"],
extends: [
diff --git a/script/create-test-e2e.js b/script/create-test-e2e.js
index bdff17148..552ab33c6 100644
--- a/script/create-test-e2e.js
+++ b/script/create-test-e2e.js
@@ -4,7 +4,7 @@ import { strict as assert } from "node:assert";
const author = "Test Author";
const description = "Test description.";
const email = "test@email.com";
-const repository = "test-repository";
+const repository = "create-typescript-app";
const owner = "TestOwner";
const title = "Test Title";
diff --git a/src/bin/index.ts b/src/bin/index.ts
index 7380671ba..b8778c319 100644
--- a/src/bin/index.ts
+++ b/src/bin/index.ts
@@ -57,7 +57,7 @@ export async function bin(args: string[]) {
prompts.log.info(
[
chalk.italic(`Tip: to run again with the same input values, use:`),
- chalk.blue(createRerunSuggestion(mode, options)),
+ chalk.blue(createRerunSuggestion(options)),
].join(" "),
);
diff --git a/src/create/createRerunSuggestion.test.ts b/src/create/createRerunSuggestion.test.ts
index 4ae61e24d..437d7e994 100644
--- a/src/create/createRerunSuggestion.test.ts
+++ b/src/create/createRerunSuggestion.test.ts
@@ -29,6 +29,7 @@ const options = {
excludeTests: undefined,
funding: undefined,
logo: undefined,
+ mode: "create",
owner: "TestOwner",
repository: "test-repository",
skipGitHubApi: true,
@@ -41,37 +42,39 @@ const options = {
describe("createRerunSuggestion", () => {
it("includes key-value pairs with mixed truthy and falsy values", () => {
- const actual = createRerunSuggestion("initialize", options);
+ const actual = createRerunSuggestion(options);
expect(actual).toMatchInlineSnapshot(
- '"npx create-typescript-app --mode initialize --base everything --access public --author TestAuthor --create-repository true --description \\"Test description.\\" --email-github github@email.com --email-npm npm@email.com --exclude-compliance true --exclude-contributors true --exclude-lint-jsdoc true --exclude-lint-json true --exclude-lint-knip true --exclude-lint-package-json true --exclude-lint-perfectionist true --owner TestOwner --repository test-repository --skip-github-api true --skip-install true --skip-removal true --title \\"Test Title\\""',
+ '"npx create-typescript-app --mode create --base everything --access public --author TestAuthor --create-repository true --description \\"Test description.\\" --email-github github@email.com --email-npm npm@email.com --exclude-compliance true --exclude-contributors true --exclude-lint-jsdoc true --exclude-lint-json true --exclude-lint-knip true --exclude-lint-package-json true --exclude-lint-perfectionist true --mode create --owner TestOwner --repository test-repository --skip-github-api true --skip-install true --skip-removal true --title \\"Test Title\\""',
);
});
it("includes stringified logo when it exists", () => {
- const actual = createRerunSuggestion("initialize", {
+ const actual = createRerunSuggestion({
...options,
logo: {
alt: "Test alt.",
src: "test/src.png",
},
+ mode: "initialize",
});
expect(actual).toMatchInlineSnapshot(
- '"npx create-typescript-app --mode initialize --base everything --access public --author TestAuthor --create-repository true --description \\"Test description.\\" --email-github github@email.com --email-npm npm@email.com --exclude-compliance true --exclude-contributors true --exclude-lint-jsdoc true --exclude-lint-json true --exclude-lint-knip true --exclude-lint-package-json true --exclude-lint-perfectionist true --logo test/src.png --logo-alt \\"Test alt.\\" --owner TestOwner --repository test-repository --skip-github-api true --skip-install true --skip-removal true --title \\"Test Title\\""',
+ '"npx create-typescript-app --mode initialize --base everything --access public --author TestAuthor --create-repository true --description \\"Test description.\\" --email-github github@email.com --email-npm npm@email.com --exclude-compliance true --exclude-contributors true --exclude-lint-jsdoc true --exclude-lint-json true --exclude-lint-knip true --exclude-lint-package-json true --exclude-lint-perfectionist true --logo test/src.png --logo-alt \\"Test alt.\\" --mode initialize --owner TestOwner --repository test-repository --skip-github-api true --skip-install true --skip-removal true --title \\"Test Title\\""',
);
});
it("includes exclusions when they exist", () => {
- const actual = createRerunSuggestion("initialize", {
+ const actual = createRerunSuggestion({
...options,
excludeCompliance: true,
excludeLintMd: true,
excludeLintSpelling: true,
+ mode: "initialize",
});
expect(actual).toMatchInlineSnapshot(
- '"npx create-typescript-app --mode initialize --base everything --access public --author TestAuthor --create-repository true --description \\"Test description.\\" --email-github github@email.com --email-npm npm@email.com --exclude-compliance true --exclude-contributors true --exclude-lint-jsdoc true --exclude-lint-json true --exclude-lint-knip true --exclude-lint-md true --exclude-lint-package-json true --exclude-lint-perfectionist true --exclude-lint-spelling true --owner TestOwner --repository test-repository --skip-github-api true --skip-install true --skip-removal true --title \\"Test Title\\""',
+ '"npx create-typescript-app --mode initialize --base everything --access public --author TestAuthor --create-repository true --description \\"Test description.\\" --email-github github@email.com --email-npm npm@email.com --exclude-compliance true --exclude-contributors true --exclude-lint-jsdoc true --exclude-lint-json true --exclude-lint-knip true --exclude-lint-md true --exclude-lint-package-json true --exclude-lint-perfectionist true --exclude-lint-spelling true --mode initialize --owner TestOwner --repository test-repository --skip-github-api true --skip-install true --skip-removal true --title \\"Test Title\\""',
);
});
});
diff --git a/src/create/createRerunSuggestion.ts b/src/create/createRerunSuggestion.ts
index 6923462ba..bdfc0a317 100644
--- a/src/create/createRerunSuggestion.ts
+++ b/src/create/createRerunSuggestion.ts
@@ -1,4 +1,3 @@
-import { Mode } from "../bin/mode.js";
import { allArgOptions } from "../shared/options/args.js";
import { Options } from "../shared/types.js";
@@ -8,10 +7,7 @@ function getFirstMatchingArg(key: string) {
);
}
-export function createRerunSuggestion(
- mode: Mode,
- options: Partial,
-): string {
+export function createRerunSuggestion(options: Partial): string {
const optionsNormalized = {
...options,
...(options.email
@@ -42,5 +38,5 @@ export function createRerunSuggestion(
})
.join(" ");
- return `npx create-typescript-app --mode ${mode} ${args}`;
+ return `npx create-typescript-app --mode ${options.mode} ${args}`;
}
diff --git a/src/create/createWithOptions.ts b/src/create/createWithOptions.ts
index fcaceecd2..ca9ba7b78 100644
--- a/src/create/createWithOptions.ts
+++ b/src/create/createWithOptions.ts
@@ -16,7 +16,7 @@ export async function createWithOptions({ github, options }: GitHubAndOptions) {
[
"Writing structure",
async () => {
- await writeStructure(options, "create");
+ await writeStructure(options);
},
],
[
diff --git a/src/create/index.ts b/src/create/index.ts
index 4b77341e4..61535d107 100644
--- a/src/create/index.ts
+++ b/src/create/index.ts
@@ -11,7 +11,7 @@ import { createRerunSuggestion } from "./createRerunSuggestion.js";
import { createWithOptions } from "./createWithOptions.js";
export async function create(args: string[]): Promise {
- const inputs = await readOptions(args);
+ const inputs = await readOptions(args, "create");
if (inputs.cancelled) {
return {
code: StatusCodes.Cancelled,
@@ -49,8 +49,9 @@ export async function create(args: string[]): Promise {
"Consider creating a GitHub repository from the new directory:",
lines: [
`cd ${inputs.options.repository}`,
- createRerunSuggestion("initialize", {
+ createRerunSuggestion({
...inputs.options,
+ mode: "initialize",
skipGitHubApi: false,
skipInstall: false,
}),
diff --git a/src/initialize/index.ts b/src/initialize/index.ts
index d9a9d08f5..a6b7c2c8b 100644
--- a/src/initialize/index.ts
+++ b/src/initialize/index.ts
@@ -7,7 +7,7 @@ import { runOrRestore } from "../shared/runOrRestore.js";
import { initializeWithOptions } from "./initializeWithOptions.js";
export const initialize: ModeRunner = async (args) => {
- const inputs = await readOptions(args);
+ const inputs = await readOptions(args, "initialize");
if (inputs.cancelled) {
return {
code: StatusCodes.Cancelled,
diff --git a/src/initialize/initializeWithOptions.ts b/src/initialize/initializeWithOptions.ts
index c47fd832e..1a8a63b6b 100644
--- a/src/initialize/initializeWithOptions.ts
+++ b/src/initialize/initializeWithOptions.ts
@@ -19,7 +19,7 @@ export async function initializeWithOptions({
[
"Updating local files",
async () => {
- await updateLocalFiles(options, "initialize");
+ await updateLocalFiles(options);
},
],
["Updating README.md", updateReadme],
diff --git a/src/migrate/index.ts b/src/migrate/index.ts
index f7698a0ef..be6cbf6e0 100644
--- a/src/migrate/index.ts
+++ b/src/migrate/index.ts
@@ -7,7 +7,7 @@ import { runOrRestore } from "../shared/runOrRestore.js";
import { migrateWithOptions } from "./migrateWithOptions.js";
export const migrate: ModeRunner = async (args) => {
- const inputs = await readOptions(args);
+ const inputs = await readOptions(args, "migrate");
if (inputs.cancelled) {
return {
code: StatusCodes.Cancelled,
diff --git a/src/migrate/migrateWithOptions.ts b/src/migrate/migrateWithOptions.ts
index 0fc48e89c..b862a9459 100644
--- a/src/migrate/migrateWithOptions.ts
+++ b/src/migrate/migrateWithOptions.ts
@@ -19,7 +19,7 @@ export async function migrateWithOptions({
[
"Writing structure",
async () => {
- await writeStructure(options, "migrate");
+ await writeStructure(options);
},
],
[
@@ -31,7 +31,7 @@ export async function migrateWithOptions({
[
"Updating local files",
async () => {
- await updateLocalFiles(options, "migrate");
+ await updateLocalFiles(options);
},
],
[
diff --git a/src/shared/options/args.ts b/src/shared/options/args.ts
index 3a9d05de4..157994685 100644
--- a/src/shared/options/args.ts
+++ b/src/shared/options/args.ts
@@ -29,6 +29,7 @@ export const allArgOptions = {
funding: { type: "string" },
logo: { type: "string" },
"logo-alt": { type: "string" },
+ mode: { type: "string" },
owner: { type: "string" },
repository: { type: "string" },
"skip-github-api": { type: "boolean" },
diff --git a/src/shared/options/augmentOptionsWithExcludes.test.ts b/src/shared/options/augmentOptionsWithExcludes.test.ts
index fcad32346..7855426b0 100644
--- a/src/shared/options/augmentOptionsWithExcludes.test.ts
+++ b/src/shared/options/augmentOptionsWithExcludes.test.ts
@@ -34,6 +34,7 @@ const optionsBase = {
excludeTests: undefined,
funding: undefined,
logo: undefined,
+ mode: "create",
owner: "",
repository: "",
skipGitHubApi: false,
diff --git a/src/shared/options/optionsSchema.ts b/src/shared/options/optionsSchema.ts
index 02807b75e..380419061 100644
--- a/src/shared/options/optionsSchema.ts
+++ b/src/shared/options/optionsSchema.ts
@@ -41,6 +41,9 @@ export const optionsSchemaShape = {
funding: z.string().optional(),
logo: z.string().optional(),
logoAlt: z.string().optional(),
+ mode: z
+ .union([z.literal("create"), z.literal("initialize"), z.literal("migrate")])
+ .optional(),
owner: z.string().optional(),
repository: z.string().optional(),
skipGitHubApi: z.boolean().optional(),
diff --git a/src/shared/options/readOptions.test.ts b/src/shared/options/readOptions.test.ts
index b00476ca7..e1d3760b2 100644
--- a/src/shared/options/readOptions.test.ts
+++ b/src/shared/options/readOptions.test.ts
@@ -112,7 +112,7 @@ describe("readOptions", () => {
.object({ base: optionsSchemaShape.base })
.safeParse({ base: "b" });
- const actual = await readOptions(["--base", "b"]);
+ const actual = await readOptions(["--base", "b"], "create");
expect(actual).toStrictEqual({
cancelled: true,
@@ -126,7 +126,7 @@ describe("readOptions", () => {
mockDetectEmailRedundancy.mockReturnValue(error);
mockGetPrefillOrPromptedOption.mockImplementation(() => undefined);
- expect(await readOptions([])).toStrictEqual({
+ expect(await readOptions([], "create")).toStrictEqual({
cancelled: true,
error,
options: {
@@ -139,7 +139,7 @@ describe("readOptions", () => {
mockDetectEmailRedundancy.mockReturnValue(false);
mockGetPrefillOrPromptedOption.mockImplementation(() => undefined);
- expect(await readOptions([])).toStrictEqual({
+ expect(await readOptions([], "create")).toStrictEqual({
cancelled: true,
options: {
...emptyOptions,
@@ -153,7 +153,7 @@ describe("readOptions", () => {
.mockImplementationOnce(() => "MockOwner")
.mockImplementation(() => undefined);
- expect(await readOptions([])).toStrictEqual({
+ expect(await readOptions([], "create")).toStrictEqual({
cancelled: true,
options: {
...emptyOptions,
@@ -170,7 +170,7 @@ describe("readOptions", () => {
.mockImplementation(() => undefined);
mockEnsureRepositoryExists.mockResolvedValue({});
- expect(await readOptions([])).toStrictEqual({
+ expect(await readOptions([], "create")).toStrictEqual({
cancelled: true,
options: {
...emptyOptions,
@@ -191,7 +191,7 @@ describe("readOptions", () => {
repository: mockOptions.repository,
});
- expect(await readOptions([])).toStrictEqual({
+ expect(await readOptions([], "create")).toStrictEqual({
cancelled: true,
options: {
...emptyOptions,
@@ -213,7 +213,7 @@ describe("readOptions", () => {
repository: mockOptions.repository,
});
- expect(await readOptions([])).toStrictEqual({
+ expect(await readOptions([], "create")).toStrictEqual({
cancelled: true,
options: {
...emptyOptions,
@@ -236,7 +236,7 @@ describe("readOptions", () => {
repository: mockOptions.repository,
});
- expect(await readOptions(["--logo", "logo.svg"])).toStrictEqual({
+ expect(await readOptions(["--logo", "logo.svg"], "create")).toStrictEqual({
cancelled: true,
options: {
...emptyOptions,
@@ -260,7 +260,7 @@ describe("readOptions", () => {
repository: mockOptions.repository,
});
- expect(await readOptions([])).toStrictEqual({
+ expect(await readOptions([], "create")).toStrictEqual({
cancelled: true,
options: {
...emptyOptions,
@@ -286,7 +286,7 @@ describe("readOptions", () => {
});
mockAugmentOptionsWithExcludes.mockResolvedValue(undefined);
- expect(await readOptions([])).toStrictEqual({
+ expect(await readOptions([], "create")).toStrictEqual({
cancelled: true,
options: {
...emptyOptions,
@@ -305,7 +305,9 @@ describe("readOptions", () => {
});
mockGetPrefillOrPromptedOption.mockImplementation(() => "mock");
- expect(await readOptions(["--base", mockOptions.base])).toStrictEqual({
+ expect(
+ await readOptions(["--base", mockOptions.base], "create"),
+ ).toStrictEqual({
cancelled: false,
github: mockOptions.github,
options: {
diff --git a/src/shared/options/readOptions.ts b/src/shared/options/readOptions.ts
index 877d77e81..eb01a9db8 100644
--- a/src/shared/options/readOptions.ts
+++ b/src/shared/options/readOptions.ts
@@ -2,6 +2,7 @@ import { parseArgs } from "node:util";
import { titleCase } from "title-case";
import { z } from "zod";
+import { Mode } from "../../bin/mode.js";
import { withSpinner } from "../cli/spinners.js";
import { Options, OptionsLogo } from "../types.js";
import { allArgOptions } from "./args.js";
@@ -30,7 +31,10 @@ export interface OptionsParseSuccess extends GitHubAndOptions {
export type OptionsParseResult = OptionsParseCancelled | OptionsParseSuccess;
-export async function readOptions(args: string[]): Promise {
+export async function readOptions(
+ args: string[],
+ mode: Mode,
+): Promise {
const defaults = readOptionDefaults();
const { values } = parseArgs({
args,
@@ -193,6 +197,7 @@ export async function readOptions(args: string[]): Promise {
email: typeof email === "string" ? { github: email, npm: email } : email,
funding: options.funding ?? (await defaults.funding()),
logo,
+ mode,
owner: options.owner,
repository,
title: options.title,
diff --git a/src/shared/types.ts b/src/shared/types.ts
index dc550aca3..a85730e4e 100644
--- a/src/shared/types.ts
+++ b/src/shared/types.ts
@@ -1,3 +1,5 @@
+import { Mode } from "../bin/mode.js";
+
export interface AllContributorContributor {
contributions: string[];
login: string;
@@ -59,6 +61,7 @@ export interface Options {
excludeTests?: boolean;
funding?: string;
logo: OptionsLogo | undefined;
+ mode: Mode;
owner: string;
repository: string;
skipGitHubApi?: boolean;
diff --git a/src/steps/finalizeDependencies.test.ts b/src/steps/finalizeDependencies.test.ts
index bb1aebc7d..7b2714c6b 100644
--- a/src/steps/finalizeDependencies.test.ts
+++ b/src/steps/finalizeDependencies.test.ts
@@ -41,6 +41,7 @@ const options = {
excludeTests: undefined,
funding: undefined,
logo: undefined,
+ mode: "create",
owner: "StubOwner",
repository: "stub-repository",
skipGitHubApi: false,
diff --git a/src/steps/updateLocalFiles.test.ts b/src/steps/updateLocalFiles.test.ts
index 453599734..86e28e949 100644
--- a/src/steps/updateLocalFiles.test.ts
+++ b/src/steps/updateLocalFiles.test.ts
@@ -44,6 +44,7 @@ const options = {
excludeTests: undefined,
funding: undefined,
logo: undefined,
+ mode: "create",
owner: "StubOwner",
repository: "stub-repository",
skipGitHubApi: false,
@@ -62,7 +63,7 @@ describe("updateLocalFiles", () => {
mockReplaceInFile.mockRejectedValue(error);
await expect(async () => {
- await updateLocalFiles(options, "initialize");
+ await updateLocalFiles({ ...options, mode: "initialize" });
}).rejects.toThrowErrorMatchingInlineSnapshot(
'"Failed to replace /Create TypeScript App/g with Stub Title in ./.github/**/*,./*.*"',
);
@@ -72,7 +73,7 @@ describe("updateLocalFiles", () => {
mockReadFileSafeAsJson.mockResolvedValue(null);
mockReplaceInFile.mockResolvedValue([]);
- await updateLocalFiles(options, "initialize");
+ await updateLocalFiles({ ...options, mode: "initialize" });
expect(mockReplaceInFile.mock.calls).toMatchInlineSnapshot(`
[
@@ -214,7 +215,7 @@ describe("updateLocalFiles", () => {
mockReadFileSafeAsJson.mockResolvedValue({});
mockReplaceInFile.mockResolvedValue([]);
- await updateLocalFiles(options, "initialize");
+ await updateLocalFiles({ ...options, mode: "initialize" });
expect(mockReplaceInFile.mock.calls).toMatchInlineSnapshot(`
[
@@ -356,7 +357,7 @@ describe("updateLocalFiles", () => {
mockReadFileSafeAsJson.mockResolvedValue({});
mockReplaceInFile.mockResolvedValue([]);
- await updateLocalFiles(options, "initialize");
+ await updateLocalFiles({ ...options, mode: "initialize" });
expect(mockReplaceInFile).not.toHaveBeenCalledWith({
files: ["./.github/**/*", "./*.*"],
@@ -372,7 +373,7 @@ describe("updateLocalFiles", () => {
});
mockReplaceInFile.mockResolvedValue([]);
- await updateLocalFiles(options, "initialize");
+ await updateLocalFiles({ ...options, mode: "initialize" });
expect(mockReplaceInFile).toHaveBeenCalledWith({
files: ["./.github/**/*", "./*.*"],
@@ -387,7 +388,7 @@ describe("updateLocalFiles", () => {
});
mockReplaceInFile.mockResolvedValue([]);
- await updateLocalFiles(options, "initialize");
+ await updateLocalFiles({ ...options, mode: "initialize" });
expect(mockReplaceInFile).toHaveBeenCalledWith({
files: "./package.json",
@@ -402,7 +403,7 @@ describe("updateLocalFiles", () => {
});
mockReplaceInFile.mockResolvedValue([]);
- await updateLocalFiles(options, "migrate");
+ await updateLocalFiles({ ...options, mode: "migrate" });
expect(mockReplaceInFile).not.toHaveBeenCalledWith({
files: "./package.json",
@@ -417,7 +418,7 @@ describe("updateLocalFiles", () => {
});
mockReplaceInFile.mockResolvedValue([]);
- await updateLocalFiles(options, "initialize");
+ await updateLocalFiles({ ...options, mode: "initialize" });
expect(mockReplaceInFile).toHaveBeenCalledWith({
files: "./package.json",
@@ -432,7 +433,7 @@ describe("updateLocalFiles", () => {
});
mockReplaceInFile.mockResolvedValue([]);
- await updateLocalFiles(options, "migrate");
+ await updateLocalFiles({ ...options, mode: "migrate" });
expect(mockReplaceInFile).not.toHaveBeenCalledWith({
files: "./package.json",
diff --git a/src/steps/updateLocalFiles.ts b/src/steps/updateLocalFiles.ts
index 741aa7bfd..dc15fcc3b 100644
--- a/src/steps/updateLocalFiles.ts
+++ b/src/steps/updateLocalFiles.ts
@@ -1,6 +1,5 @@
import replaceInFile from "replace-in-file";
-import { Mode } from "../bin/mode.js";
import { readFileSafeAsJson } from "../shared/readFileSafeAsJson.js";
import { Options } from "../shared/types.js";
@@ -9,7 +8,7 @@ interface ExistingPackageData {
version?: string;
}
-export async function updateLocalFiles(options: Options, mode: Mode) {
+export async function updateLocalFiles(options: Options) {
const existingPackage = ((await readFileSafeAsJson("./package.json")) ??
{}) as ExistingPackageData;
@@ -19,7 +18,9 @@ export async function updateLocalFiles(options: Options, mode: Mode) {
[/create-typescript-app/g, options.repository],
[/\/\*\n.+\*\/\n\n/gs, ``, ".eslintrc.cjs"],
[/"author": ".+"/g, `"author": "${options.author}"`, "./package.json"],
- ...(mode === "migrate" ? [] : [[/"bin": ".+\n/g, ``, "./package.json"]]),
+ ...(options.mode === "migrate"
+ ? []
+ : [[/"bin": ".+\n/g, ``, "./package.json"]]),
[/"test:create": ".+\n/g, ``, "./package.json"],
[/"test:initialize": ".*/g, ``, "./package.json"],
[/"initialize": ".*/g, ``, "./package.json"],
@@ -46,7 +47,7 @@ export async function updateLocalFiles(options: Options, mode: Mode) {
replacements.push([existingPackage.description, options.description]);
}
- if (mode === "initialize" && existingPackage.version) {
+ if (options.mode === "initialize" && existingPackage.version) {
replacements.push([
new RegExp(`"version": "${existingPackage.version}"`, "g"),
`"version": "0.0.0"`,
diff --git a/src/steps/writeReadme/findIntroSectionClose.test.ts b/src/steps/writeReadme/findIntroSectionClose.test.ts
index 5dae9e73a..60da73e68 100644
--- a/src/steps/writeReadme/findIntroSectionClose.test.ts
+++ b/src/steps/writeReadme/findIntroSectionClose.test.ts
@@ -40,7 +40,7 @@ describe("findIntroSectionClose", () => {
Next line.
`,
- 13,
+ 14,
],
])("%s", (contents, expected) => {
expect(findIntroSectionClose(contents)).toEqual(expected);
diff --git a/src/steps/writeReadme/findIntroSectionClose.ts b/src/steps/writeReadme/findIntroSectionClose.ts
index 913a5843e..e910b5f3b 100644
--- a/src/steps/writeReadme/findIntroSectionClose.ts
+++ b/src/steps/writeReadme/findIntroSectionClose.ts
@@ -18,5 +18,5 @@ export function findIntroSectionClose(contents: string) {
}
// Lastly, go for the second line altogether
- return contents.indexOf("\n", 2);
+ return contents.indexOf("\n", 1) + 1;
}
diff --git a/src/steps/writeReadme/generateTopContent.test.ts b/src/steps/writeReadme/generateTopContent.test.ts
index e654015aa..030a4bb9d 100644
--- a/src/steps/writeReadme/generateTopContent.test.ts
+++ b/src/steps/writeReadme/generateTopContent.test.ts
@@ -28,6 +28,7 @@ const optionsBase = {
excludeTests: undefined,
funding: undefined,
logo: undefined,
+ mode: "create",
owner: "",
repository: "",
skipGitHubApi: false,
@@ -64,7 +65,18 @@ describe("findExistingBadges", () => {
-
"
+
+
+ ## Usage
+
+ \`\`\`shell
+ npm i
+ \`\`\`
+ \`\`\`ts
+ import { greet } from \\"\\";
+
+ greet(\\"Hello, world! π\\");
+ \`\`\`"
`);
});
@@ -97,11 +109,22 @@ describe("findExistingBadges", () => {
- "
+
+
+ ## Usage
+
+ \`\`\`shell
+ npm i
+ \`\`\`
+ \`\`\`ts
+ import { greet } from \\"\\";
+
+ greet(\\"Hello, world! π\\");
+ \`\`\`"
`);
});
- it("push existing badgesΒ to the end when there is an existing unknown badge", () => {
+ it("push existing badges to the end when there is an existing unknown badge", () => {
expect(
generateTopContent(optionsBase, [
`
`,
@@ -131,7 +154,48 @@ describe("findExistingBadges", () => {
- "
+
+
+ ## Usage
+
+ \`\`\`shell
+ npm i
+ \`\`\`
+ \`\`\`ts
+ import { greet } from \\"\\";
+
+ greet(\\"Hello, world! π\\");
+ \`\`\`"
`);
});
+
+ it("does not include a greet section when the mode is migrate", () => {
+ expect(generateTopContent({ ...optionsBase, mode: "migrate" }, []))
+ .toMatchInlineSnapshot(`
+ "
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
"
+ `);
+ });
});
diff --git a/src/steps/writeReadme/generateTopContent.ts b/src/steps/writeReadme/generateTopContent.ts
index c7f631708..7fdb5f90f 100644
--- a/src/steps/writeReadme/generateTopContent.ts
+++ b/src/steps/writeReadme/generateTopContent.ts
@@ -79,5 +79,20 @@ export function generateTopContent(options: Options, existingBadges: string[]) {
${[...badges, ...remainingExistingBadges]
.map((badge) => `\t${badge}`)
.join("\n")}
-`;
+${
+ options.mode === "migrate"
+ ? ""
+ : `
+
+## Usage
+
+\`\`\`shell
+npm i ${options.repository}
+\`\`\`
+\`\`\`ts
+import { greet } from "${options.repository}";
+
+greet("Hello, world! π");
+\`\`\``
+ }`;
}
diff --git a/src/steps/writeReadme/index.test.ts b/src/steps/writeReadme/index.test.ts
index 4c3646bf3..32aa3528e 100644
--- a/src/steps/writeReadme/index.test.ts
+++ b/src/steps/writeReadme/index.test.ts
@@ -48,6 +48,7 @@ const options = {
excludeTests: undefined,
funding: "TestFunding",
logo: undefined,
+ mode: "create",
owner: "TestOwner",
repository: "test-repository",
skipGitHubApi: false,
@@ -96,6 +97,17 @@ describe("writeReadme", () => {
+ ## Usage
+
+ \`\`\`shell
+ npm i test-repository
+ \`\`\`
+ \`\`\`ts
+ import { greet } from \\"test-repository\\";
+
+ greet(\\"Hello, world! π\\");
+ \`\`\`
+
## Contributors
@@ -120,7 +132,7 @@ describe("writeReadme", () => {
});
it("adds sections when the README.md already exists and is sparse", async () => {
- mockReadFileSafe.mockResolvedValueOnce(`# ${options.title}`);
+ mockReadFileSafe.mockResolvedValueOnce(`# ${options.title}\n`);
await writeReadme(options);
@@ -154,7 +166,18 @@ describe("writeReadme", () => {
- e
+
+
+ ## Usage
+
+ \`\`\`shell
+ npm i test-repository
+ \`\`\`
+ \`\`\`ts
+ import { greet } from \\"test-repository\\";
+
+ greet(\\"Hello, world! π\\");
+ \`\`\`
## Contributors
@@ -180,7 +203,7 @@ describe("writeReadme", () => {
});
it("adds all-contributors content when directed to and the indicator does not yet exist", async () => {
- mockReadFileSafe.mockResolvedValueOnce(`# ${options.title}`);
+ mockReadFileSafe.mockResolvedValueOnce(`# ${options.title}\n`);
await writeReadme({
...options,
@@ -217,7 +240,18 @@ describe("writeReadme", () => {
- e
+
+
+ ## Usage
+
+ \`\`\`shell
+ npm i test-repository
+ \`\`\`
+ \`\`\`ts
+ import { greet } from \\"test-repository\\";
+
+ greet(\\"Hello, world! π\\");
+ \`\`\`
## Contributors
@@ -326,6 +360,17 @@ describe("writeReadme", () => {
+ ## Usage
+
+ \`\`\`shell
+ npm i test-repository
+ \`\`\`
+ \`\`\`ts
+ import { greet } from \\"test-repository\\";
+
+ greet(\\"Hello, world! π\\");
+ \`\`\`
+
## Contributors
diff --git a/src/steps/writing/creation/createESLintRC.test.ts b/src/steps/writing/creation/createESLintRC.test.ts
index 7c4d7e52d..16c9bd716 100644
--- a/src/steps/writing/creation/createESLintRC.test.ts
+++ b/src/steps/writing/creation/createESLintRC.test.ts
@@ -38,6 +38,7 @@ function fakeOptions(getExcludeValue: (exclusionName: string) => boolean) {
].map((key) => [key, getExcludeValue(key)]),
),
logo: undefined,
+ mode: "create",
owner: "TestOwner",
repository: "test-repository",
skipGitHubApi: true,
@@ -73,6 +74,15 @@ describe("createESLintRC", () => {
\\"operator-assignment\\": \\"error\\",
},
},
+ {
+ files: \\"**/*.md/*.ts\\",
+ rules: {
+ \\"n/no-missing-import\\": [
+ \\"error\\",
+ { allowModules: [\\"create-typescript-app\\"] },
+ ],
+ },
+ },
{
extends: [\\"plugin:@typescript-eslint/recommended-type-checked\\"],
files: [\\"**/*.ts\\"],
@@ -148,6 +158,15 @@ describe("createESLintRC", () => {
\\"jsdoc/require-returns\\": \\"off\\",
},
},
+ {
+ files: \\"**/*.md/*.ts\\",
+ rules: {
+ \\"n/no-missing-import\\": [
+ \\"error\\",
+ { allowModules: [\\"create-typescript-app\\"] },
+ ],
+ },
+ },
{
excludedFiles: [\\"**/*.md/*.ts\\"],
extends: [
diff --git a/src/steps/writing/creation/createESLintRC.ts b/src/steps/writing/creation/createESLintRC.ts
index 9a5948482..33197e7d5 100644
--- a/src/steps/writing/creation/createESLintRC.ts
+++ b/src/steps/writing/creation/createESLintRC.ts
@@ -75,6 +75,15 @@ module.exports = {
}
},
},
+ {
+ files: "**/*.md/*.ts",
+ rules: {
+ "n/no-missing-import": [
+ "error",
+ { allowModules: ["create-typescript-app"] },
+ ],
+ },
+ },
{
${
options.excludeLintMd
diff --git a/src/steps/writing/creation/dotGitHub/createDevelopment.test.ts b/src/steps/writing/creation/dotGitHub/createDevelopment.test.ts
index 80163acc9..9fdbbb403 100644
--- a/src/steps/writing/creation/dotGitHub/createDevelopment.test.ts
+++ b/src/steps/writing/creation/dotGitHub/createDevelopment.test.ts
@@ -28,6 +28,7 @@ const options = {
excludeTests: undefined,
funding: undefined,
logo: undefined,
+ mode: "create",
owner: "TestOwner",
repository: "test-repository",
skipGitHubApi: false,
diff --git a/src/steps/writing/creation/index.ts b/src/steps/writing/creation/index.ts
index 4b2ac3f71..897e6b350 100644
--- a/src/steps/writing/creation/index.ts
+++ b/src/steps/writing/creation/index.ts
@@ -1,4 +1,3 @@
-import { Mode } from "../../../bin/mode.js";
import { Options } from "../../../shared/types.js";
import { Structure } from "../types.js";
import { createDotGitHub } from "./dotGitHub/index.js";
@@ -7,15 +6,12 @@ import { createDotVSCode } from "./dotVSCode.js";
import { createRootFiles } from "./rootFiles.js";
import { createSrc } from "./src.js";
-export async function createStructure(
- options: Options,
- mode: Mode,
-): Promise {
+export async function createStructure(options: Options): Promise {
return {
".github": await createDotGitHub(options),
".husky": createDotHusky(),
".vscode": await createDotVSCode(options),
- ...(mode !== "migrate" && { src: await createSrc(options) }),
+ ...(options.mode !== "migrate" && { src: await createSrc(options) }),
...(await createRootFiles(options)),
};
}
diff --git a/src/steps/writing/creation/writePackageJson.test.ts b/src/steps/writing/creation/writePackageJson.test.ts
index 90b83eed8..dc2d8fb56 100644
--- a/src/steps/writing/creation/writePackageJson.test.ts
+++ b/src/steps/writing/creation/writePackageJson.test.ts
@@ -36,6 +36,7 @@ const options = {
excludeTests: false,
funding: undefined,
logo: undefined,
+ mode: "create",
owner: "test-owner",
repository: "test-repository",
skipGitHubApi: false,
diff --git a/src/steps/writing/writeStructure.ts b/src/steps/writing/writeStructure.ts
index 71d2ab1ac..6ac110464 100644
--- a/src/steps/writing/writeStructure.ts
+++ b/src/steps/writing/writeStructure.ts
@@ -1,12 +1,11 @@
import { $ } from "execa";
-import { Mode } from "../../bin/mode.js";
import { Options } from "../../shared/types.js";
import { createStructure } from "./creation/index.js";
import { writeStructureWorker } from "./writeStructureWorker.js";
-export async function writeStructure(options: Options, mode: Mode) {
- await writeStructureWorker(await createStructure(options, mode), ".");
+export async function writeStructure(options: Options) {
+ await writeStructureWorker(await createStructure(options), ".");
// https://github.com/JoshuaKGoldberg/create-typescript-app/issues/718
await $`chmod ug+x .husky/pre-commit`;