Skip to content

Commit cf524e6

Browse files
feat: run setup after hydration, with unified code (#489)
## PR Checklist - [x] Addresses an existing open issue: fixes #475; fixes #476 - [x] That issue was marked as [`status: accepting prs`](https://github.com/JoshuaKGoldberg/template-typescript-node-package/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22) - [x] Steps in [CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/template-typescript-node-package/blob/main/.github/CONTRIBUTING.md) were taken ## Overview A sweeping refactor, hooray! This unifies much of the the _hydration_ and _setup_ infrastructure: * Reuses the spinner blocks already used in _setup_ for the steps in _hydration_ * Extracts the `try`/`catch` logic already used in _setup_ to a new `runOrRestore` function, and uses it in _hydration_ * `runOrRestore` also includes args parsing for the shared values between the two * Standardizes the names of settings/values as `values` Test coverage has dipped a bit, even with the end-to-end scripts. This'll be a good followup issue - the _setup_ script's end-to-end test doesn't upload coverage...
1 parent 4ef5ec5 commit cf524e6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+832
-688
lines changed

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ pnpm run setup --repository "testing-repository" --title "Testing Title" --owner
9191
9292
## Repository Hydration
9393

94+
> **Warning**
95+
> Hydration will override many files in your repository.
96+
> You'll want to review each of the changes and make sure nothing important is removed.
97+
9498
Alternately, if you have an existing repository that you'd like to give the files from this repository, you can run `template-typescript-node-package` in a repository to "hydrate" it.
9599

96100
```shell
@@ -123,9 +127,14 @@ You can disable some of them on the command-line:
123127
npx template-typescript-node-package --releases false --unitTests false
124128
```
125129

126-
> **Warning**
127-
> This will override many files in your repository.
128-
> You'll want to review each of the changes and make sure nothing important is removed.
130+
You can prevent the hydration script from making network-based changes using either or both of the following CLI flags:
131+
132+
- `--skip-install` _(`boolean`)_: Skips installing all the new template packages with `pnpm`
133+
- `--skip-setup` _(`boolean`)_: Skips running the setup script at the end of hydration
134+
135+
```shell
136+
npx template-typescript-node-package --skip-install --skip-setup
137+
```
129138

130139
## Usage
131140

knip.jsonc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"script/*e2e.js"
88
],
99
"ignoreBinaries": ["dedupe", "gh"],
10-
"ignoreDependencies": ["c8"],
10+
"ignoreDependencies": ["all-contributors-cli", "c8"],
1111
"ignoreExportsUsedInFile": {
1212
"interface": true,
1313
"type": true

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
"url": "https://github.com/JoshuaKGoldberg/template-typescript-node-package"
88
},
99
"license": "MIT",
10-
"author": "Josh Goldberg <[email protected]>",
10+
"author": {
11+
"name": "Josh Goldberg",
12+
"email": "[email protected]"
13+
},
1114
"type": "module",
1215
"main": "./lib/index.js",
1316
"bin": "./lib/hydrate/index.js",

script/hydrate-test-e2e.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import chalk from "chalk";
2-
import { $ } from "execa";
2+
import { $, execaCommand } from "execa";
3+
4+
import packageData from "../package.json" assert { type: "json" };
5+
6+
const { description, name: repository } = packageData;
7+
const owner = "JoshuaKGoldberg";
8+
const title = "Template TypeScript Node Package";
39

410
await $({
511
stdio: "inherit",
6-
})`c8 -o ./coverage-hydrate -r html -r lcov node ./lib/hydrate/index.js`;
12+
})`c8 -o ./coverage-hydrate -r html -r lcov node ./lib/hydrate/index.js --description ${description} --owner ${owner} --title ${title} --repository ${repository} --skip-api --skip-install --skip-setup`;
713

814
const { stdout: gitStatus } = await $`git status`;
915
console.log(`Stdout from running \`git status\`:\n${gitStatus}`);
@@ -46,7 +52,7 @@ if (unstagedModifiedFiles.length) {
4652
const gitDiffCommand = `git diff HEAD -- ${unstagedModifiedFiles.join(" ")}`;
4753
console.log(
4854
`Stdout from running \`${gitDiffCommand}\`:\n${
49-
(await $(gitDiffCommand)).stdout
55+
(await execaCommand(gitDiffCommand)).stdout
5056
}`
5157
);
5258
console.error(

src/hydrate/hydrate.test.ts

Lines changed: 0 additions & 83 deletions
This file was deleted.

src/hydrate/hydrate.ts

Lines changed: 52 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,68 @@
11
import { parseArgs } from "node:util";
22

3-
import chalk from "chalk";
4-
import { $ } from "execa";
5-
6-
import { clearUnnecessaryFiles } from "./clearUnnecessaryFiles.js";
7-
import { createStructure } from "./creation/index.js";
8-
import { finalize } from "./finalize.js";
9-
import { readFileSafe } from "./readFileSafe.js";
10-
import { readFundingIfExists } from "./readFundingIfExists.js";
11-
import { ensureSettingsAreFilledOut } from "./repositorySettings.js";
12-
import { writeReadme } from "./writeReadme.js";
13-
import { writeStructure } from "./writing.js";
14-
15-
interface PartialPackageData {
16-
author?: string | { email: string; name: string };
17-
description?: string;
18-
email?: string;
19-
name?: string;
20-
repository?: string;
21-
}
3+
import { setupWithInformation } from "../setup/setupWithInformation.js";
4+
import {
5+
skipSpinnerBlock,
6+
successSpinnerBlock,
7+
withSpinner,
8+
} from "../shared/cli/spinners.js";
9+
import { runOrRestore } from "../shared/runOrRestore.js";
10+
import { clearUnnecessaryFiles } from "./steps/clearUnnecessaryFiles.js";
11+
import { finalizeDependencies as finalizeDependencies } from "./steps/finalizeDependencies.js";
12+
import { writeReadme } from "./steps/writeReadme.js";
13+
import { writeStructure } from "./steps/writing/writeStructure.js";
14+
import { getHydrationDefaults } from "./values/getHydrationDefaults.js";
15+
import { ensureHydrationInputValues } from "./values/hydrationInputValues.js";
2216

2317
export async function hydrate(args: string[]) {
24-
const { values } = parseArgs({
18+
const { values: hydrationValues } = parseArgs({
2519
args,
2620
options: {
27-
author: { type: "string" },
28-
description: { type: "string" },
29-
email: { type: "string" },
30-
funding: { type: "string" },
31-
owner: { type: "string" },
32-
releases: { type: "boolean" },
33-
repository: { type: "string" },
34-
unitTests: { type: "boolean" },
35-
title: { type: "string" },
21+
"skip-install": { type: "boolean" },
22+
"skip-setup": { type: "boolean" },
3623
},
3724
tokens: true,
3825
strict: false,
3926
});
4027

41-
const existingReadme = await readFileSafe("./README.md", "");
42-
const existingPackage = JSON.parse(
43-
await readFileSafe("./package.json", "{}")
44-
) as PartialPackageData;
28+
return await runOrRestore({
29+
args,
30+
defaults: await getHydrationDefaults(),
31+
label: "hydration",
32+
run: async ({ octokit, values }) => {
33+
ensureHydrationInputValues(values);
34+
35+
await withSpinner(clearUnnecessaryFiles, "clearing unnecessary files");
36+
37+
await withSpinner(
38+
() => writeStructure(values),
39+
"writing new repository structure"
40+
);
4541

46-
const settings = {
47-
author:
48-
(values.author as string | undefined) ??
49-
(typeof existingPackage.author === "string"
50-
? existingPackage.author.split("<")[0].trim()
51-
: existingPackage.author?.name),
52-
description:
53-
(values.description as string | undefined) ?? existingPackage.description,
54-
email:
55-
(values.email as string | undefined) ??
56-
(typeof existingPackage.author === "string"
57-
? existingPackage.author.split(/<|>/)[1]
58-
: existingPackage.author?.email),
59-
funding: await readFundingIfExists(),
60-
owner:
61-
(values.owner as string | undefined) ??
62-
(await $`git remote -v`).stdout.match(
63-
/origin\s+https:\/\/\S+\.\w+\/([^/]+)/
64-
)?.[1],
65-
releases: (values.releases as boolean | undefined) ?? true,
66-
repository:
67-
(values.repository as string | undefined) ?? existingPackage.name,
68-
unitTests: (values.unitTests as boolean | undefined) ?? true,
69-
title:
70-
(values.title as string | undefined) ??
71-
existingReadme.match(/^(?:# |<h1\s+align="center">)(\S+)/)?.[1],
72-
};
42+
await withSpinner(() => writeReadme(values), "writing README.md");
7343

74-
ensureSettingsAreFilledOut(settings);
44+
if (hydrationValues["skip-install"]) {
45+
skipSpinnerBlock(`Skipping package installations.`);
46+
} else {
47+
await withSpinner(
48+
() => finalizeDependencies(values),
49+
"finalizing dependencies"
50+
);
51+
}
7552

76-
await clearUnnecessaryFiles();
77-
await writeStructure(createStructure(settings));
78-
await writeReadme(settings);
79-
await finalize(settings);
53+
if (hydrationValues["skip-setup"]) {
54+
skipSpinnerBlock(`Done hydrating, and skipping setup command.`);
55+
} else {
56+
successSpinnerBlock("Done hydrating. Starting setup command...");
8057

81-
console.log(chalk.green("Done!"));
58+
await setupWithInformation({
59+
octokit,
60+
values: {
61+
...values,
62+
skipUninstalls: true,
63+
},
64+
});
65+
}
66+
},
67+
});
8268
}

src/hydrate/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
import { hydrate } from "./hydrate.js";
22

3-
await hydrate(process.argv.slice(2));
3+
process.exitCode = await hydrate(process.argv.slice(2));
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import chalk from "chalk";
2-
import { beforeEach, describe, expect, it, vi } from "vitest";
1+
import { describe, expect, it, vi } from "vitest";
32

43
import { clearUnnecessaryFiles } from "./clearUnnecessaryFiles.js";
54

@@ -12,16 +11,9 @@ vi.mock("execa", () => ({
1211
}));
1312

1413
describe("clearUnnecessaryFiles", () => {
15-
beforeEach(() => {
16-
console.log = vi.fn();
17-
});
18-
1914
it("runs all commands even when they fail", async () => {
2015
await clearUnnecessaryFiles();
2116

22-
expect(console.log).toHaveBeenCalledWith(
23-
chalk.gray(`rm -rf ./src/**/*.js`)
24-
);
2517
expect(mockExecaCommand).toHaveBeenCalledWith(`rm -rf ./src/**/*.js`);
2618
});
2719
});

src/hydrate/clearUnnecessaryFiles.ts renamed to src/hydrate/steps/clearUnnecessaryFiles.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import chalk from "chalk";
21
import { execaCommand } from "execa";
32

43
export async function clearUnnecessaryFiles() {
@@ -8,7 +7,6 @@ export async function clearUnnecessaryFiles() {
87
"./src/**/*.js",
98
]) {
109
try {
11-
console.log(chalk.gray(`rm -rf ${glob}`));
1210
await execaCommand(`rm -rf ${glob}`);
1311
} catch {
1412
// (we ignore failures if nothing matched)

src/hydrate/finalize.test.ts renamed to src/hydrate/steps/finalizeDependencies.test.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { beforeEach, describe, expect, it, vi } from "vitest";
1+
import { describe, expect, it, vi } from "vitest";
22

3-
import { finalize } from "./finalize.js";
3+
import { finalizeDependencies } from "./finalizeDependencies.js";
44

55
const mockExecaCommand = vi.fn();
66

@@ -11,12 +11,8 @@ vi.mock("execa", () => ({
1111
}));
1212

1313
describe("finalize", () => {
14-
beforeEach(() => {
15-
console.log = vi.fn();
16-
});
17-
1814
it("installs the base list of commands when no options are enabled", async () => {
19-
await finalize({ releases: false, unitTests: false });
15+
await finalizeDependencies({ releases: false, unitTests: false });
2016

2117
expect(mockExecaCommand).toHaveBeenCalledWith(
2218
[
@@ -53,13 +49,12 @@ describe("finalize", () => {
5349
"typescript",
5450
"yaml-eslint-parser",
5551
"-D",
56-
].join(" "),
57-
{ stdio: "inherit" }
52+
].join(" ")
5853
);
5954
});
6055

6156
it("installs the full list of commands when both options are enabled", async () => {
62-
await finalize({ releases: true, unitTests: true });
57+
await finalizeDependencies({ releases: true, unitTests: true });
6358

6459
expect(mockExecaCommand).toHaveBeenCalledWith(
6560
[
@@ -102,8 +97,7 @@ describe("finalize", () => {
10297
"eslint-plugin-vitest",
10398
"vitest",
10499
"-D",
105-
].join(" "),
106-
{ stdio: "inherit" }
100+
].join(" ")
107101
);
108102
});
109103
});

0 commit comments

Comments
 (0)