Skip to content

Commit 597e6da

Browse files
feat: allowed hydration directory to be empty (#673)
## PR Checklist - [x] Addresses an existing open issue: fixes #669 - [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 Changes split out of #668: * If `git status` fails, runs `git init` to make the directory a repo * Allows `package.json` to be empty, defaulting its contents to `{}`
1 parent ba7ea21 commit 597e6da

7 files changed

+79
-14
lines changed

src/hydrate/steps/writing/creation/writePackageJson.test.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import { describe, expect, it, vi } from "vitest";
22

33
import { writePackageJson } from "./writePackageJson.js";
44

5-
const mockReadFileAsJson = vi.fn();
5+
const mockReadFileSafeAsJson = vi.fn();
66

7-
vi.mock("../../../../shared/readFileAsJson.js", () => ({
8-
get readFileAsJson() {
9-
return mockReadFileAsJson;
7+
vi.mock("../../../../shared/readFileSafeAsJson.js", () => ({
8+
get readFileSafeAsJson() {
9+
return mockReadFileSafeAsJson;
1010
},
1111
}));
1212

@@ -23,7 +23,7 @@ const values = {
2323
describe("writePackageJson", () => {
2424
it("preserves existing dependencies when they exist", async () => {
2525
const dependencies = { abc: "1.2.3" };
26-
mockReadFileAsJson.mockResolvedValue({ dependencies });
26+
mockReadFileSafeAsJson.mockResolvedValue({ dependencies });
2727

2828
const packageJson = await writePackageJson(values);
2929

@@ -34,7 +34,7 @@ describe("writePackageJson", () => {
3434

3535
it("preserves existing devDependencies that aren't known to be unnecessary when they exist", async () => {
3636
const devDependencies = { abc: "1.2.3", jest: "4.5.6" };
37-
mockReadFileAsJson.mockResolvedValue({ devDependencies });
37+
mockReadFileSafeAsJson.mockResolvedValue({ devDependencies });
3838

3939
const packageJson = await writePackageJson(values);
4040

@@ -44,7 +44,7 @@ describe("writePackageJson", () => {
4444
});
4545

4646
it("includes a release script when releases is true", async () => {
47-
mockReadFileAsJson.mockResolvedValue({});
47+
mockReadFileSafeAsJson.mockResolvedValue({});
4848

4949
const packageJson = await writePackageJson({
5050
...values,
@@ -61,7 +61,7 @@ describe("writePackageJson", () => {
6161
});
6262

6363
it("includes a test script when unitTests is true", async () => {
64-
mockReadFileAsJson.mockResolvedValue({});
64+
mockReadFileSafeAsJson.mockResolvedValue({});
6565

6666
const packageJson = await writePackageJson({
6767
...values,

src/hydrate/steps/writing/creation/writePackageJson.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { readFileAsJson } from "../../../../shared/readFileAsJson.js";
1+
import { readFileSafeAsJson } from "../../../../shared/readFileSafeAsJson.js";
22
import { HydrationInputValues } from "../../../values/types.js";
33
import { formatJson } from "./formatters/formatJson.js";
44

@@ -41,9 +41,8 @@ export async function writePackageJson({
4141
| "repository"
4242
| "unitTests"
4343
>) {
44-
const existingPackageJson = (await readFileAsJson(
45-
"./package.json",
46-
)) as object;
44+
const existingPackageJson =
45+
((await readFileSafeAsJson("./package.json")) as null | object) ?? {};
4746

4847
return await formatJson({
4948
// To start, copy over all existing package fields (e.g. "dependencies")
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import chalk from "chalk";
2+
import { describe, expect, it, vi } from "vitest";
3+
4+
import { ensureGitRepository } from "./ensureGitRepository.js";
5+
6+
const mock$ = vi.fn();
7+
8+
vi.mock("execa", () => ({
9+
get $() {
10+
return mock$;
11+
},
12+
}));
13+
14+
const mockLogLine = vi.fn();
15+
16+
vi.mock("./cli/lines.js", () => ({
17+
get logLine() {
18+
return mockLogLine;
19+
},
20+
}));
21+
22+
describe("ensureGitRepository", () => {
23+
it("does not run git init when git status succeeds", async () => {
24+
mock$.mockResolvedValue(0);
25+
26+
await ensureGitRepository();
27+
28+
expect(mock$).toHaveBeenCalledTimes(1);
29+
expect(mockLogLine).not.toHaveBeenCalled();
30+
});
31+
32+
it("runs git init when git status fails", async () => {
33+
mock$.mockRejectedValueOnce(1);
34+
35+
await ensureGitRepository();
36+
37+
expect(mock$).toHaveBeenCalledWith(["git init"]);
38+
expect(mockLogLine).toHaveBeenCalledWith();
39+
expect(mockLogLine).toHaveBeenCalledWith(
40+
chalk.gray("Running `git init` to turn this into a Git repository."),
41+
);
42+
});
43+
});

src/shared/ensureGitRepository.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import chalk from "chalk";
2+
import { $ } from "execa";
3+
4+
import { logLine } from "./cli/lines.js";
5+
6+
export async function ensureGitRepository() {
7+
try {
8+
await $`git status`;
9+
} catch {
10+
logLine();
11+
logLine(
12+
chalk.gray("Running `git init` to turn this into a Git repository."),
13+
);
14+
15+
await $`git init`;
16+
}
17+
}

src/shared/getDefaultSettings.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export async function getDefaultSettings() {
1414
logLine();
1515
logLine(
1616
chalk.gray(
17-
"Could not populate default owner and repository. Did not detect a Git repository with an origin. ",
17+
"Could not populate default owner and repository. Did not detect an 'origin' remote.",
1818
),
1919
);
2020

src/shared/runOrRestore.test.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ describe("runOrRestore", () => {
6767

6868
expect(actual).toEqual(1);
6969

70-
expect(mock$).not.toHaveBeenCalled();
70+
expect(mock$).toHaveBeenCalledWith(["git status"]);
71+
expect(mock$).toHaveBeenCalledTimes(1);
7172
});
7273

7374
it("returns 1 and restores the repository when run rejects, skipRestore is false, and shouldRestore is confirmed", async () => {
@@ -87,6 +88,8 @@ describe("runOrRestore", () => {
8788

8889
expect(actual).toEqual(1);
8990

91+
expect(mock$).toHaveBeenCalledWith(["git status"]);
9092
expect(mock$).toHaveBeenCalledWith(["git restore ."]);
93+
expect(mock$).toHaveBeenCalledTimes(2);
9194
});
9295
});

src/shared/runOrRestore.ts

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as prompts from "@clack/prompts";
22
import chalk from "chalk";
33
import { $ } from "execa";
44

5+
import { ensureGitRepository } from "./ensureGitRepository.js";
56
import {
67
GetterDefaultInputValues,
78
InputValuesAndOctokit,
@@ -39,6 +40,8 @@ export async function runOrRestore({
3940
),
4041
);
4142

43+
await ensureGitRepository();
44+
4245
const { octokit, values } = await getInputValuesAndOctokit(args, defaults);
4346

4447
skipRestore = values.skipRestore;

0 commit comments

Comments
 (0)