Skip to content

Commit 71d4b96

Browse files
feat: allow blank package.json in readFileSafeAsJson (#679)
## PR Checklist - [x] Addresses an existing open issue: fixes #678 - [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 Adds unit test coverage too.
1 parent d47f5b7 commit 71d4b96

File tree

2 files changed

+321
-3
lines changed

2 files changed

+321
-3
lines changed

Diff for: src/initialize/steps/updateLocalFiles.test.ts

+318
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
import { describe, expect, it, vi } from "vitest";
2+
3+
import { updateLocalFiles } from "./updateLocalFiles.js";
4+
5+
const mockReplaceInFile = vi.fn();
6+
7+
vi.mock("replace-in-file", () => ({
8+
get default() {
9+
return mockReplaceInFile;
10+
},
11+
}));
12+
13+
const mockReadFileSafeAsJson = vi.fn();
14+
15+
vi.mock("../../shared/readFileSafeAsJson.js", () => ({
16+
get readFileSafeAsJson() {
17+
return mockReadFileSafeAsJson;
18+
},
19+
}));
20+
21+
const stubOptions = {
22+
description: "Stub description.",
23+
npmAuthor: "stub-npm-author",
24+
owner: "StubOwner",
25+
repository: "stub-repository",
26+
title: "Stub Title",
27+
};
28+
29+
describe("updateLocalFiles", () => {
30+
it("throws a wrapping error when replaceInFiles rejects", async () => {
31+
const error = new Error("Oh no!");
32+
33+
mockReadFileSafeAsJson.mockResolvedValue({});
34+
mockReplaceInFile.mockRejectedValue(error);
35+
36+
await expect(async () => {
37+
await updateLocalFiles(stubOptions);
38+
}).rejects.toThrowErrorMatchingInlineSnapshot(
39+
'"Failed to replace /Template TypeScript Node Package/g with Stub Title in ./.github/**/*,./*.*"',
40+
);
41+
});
42+
43+
it("replaces using the common replacements when the existing package data is empty", async () => {
44+
mockReadFileSafeAsJson.mockResolvedValue({});
45+
mockReplaceInFile.mockResolvedValue([]);
46+
47+
await updateLocalFiles(stubOptions);
48+
49+
expect(mockReplaceInFile.mock.calls).toMatchInlineSnapshot(`
50+
[
51+
[
52+
{
53+
"files": [
54+
"./.github/**/*",
55+
"./*.*",
56+
],
57+
"from": /Template TypeScript Node Package/g,
58+
"to": "Stub Title",
59+
},
60+
],
61+
[
62+
{
63+
"files": [
64+
"./.github/**/*",
65+
"./*.*",
66+
],
67+
"from": /JoshuaKGoldberg/g,
68+
"to": "StubOwner",
69+
},
70+
],
71+
[
72+
{
73+
"files": [
74+
"./.github/**/*",
75+
"./*.*",
76+
],
77+
"from": /template-typescript-node-package/g,
78+
"to": "stub-repository",
79+
},
80+
],
81+
[
82+
{
83+
"files": ".eslintrc.cjs",
84+
"from": /\\\\/\\\\\\*\\\\n\\.\\+\\\\\\*\\\\/\\\\n\\\\n/gs,
85+
"to": "",
86+
},
87+
],
88+
[
89+
{
90+
"files": "./package.json",
91+
"from": /"author": "\\.\\+"/g,
92+
"to": "\\"author\\": \\"stub-npm-author\\"",
93+
},
94+
],
95+
[
96+
{
97+
"files": "./package.json",
98+
"from": /"bin": "\\.\\+\\\\n/g,
99+
"to": "",
100+
},
101+
],
102+
[
103+
{
104+
"files": "./package.json",
105+
"from": /"initialize:test": "\\.\\*/g,
106+
"to": "",
107+
},
108+
],
109+
[
110+
{
111+
"files": "./package.json",
112+
"from": /"initialize": "\\.\\*/g,
113+
"to": "",
114+
},
115+
],
116+
[
117+
{
118+
"files": "./package.json",
119+
"from": /"migrate:test": "\\.\\+\\\\n/g,
120+
"to": "",
121+
},
122+
],
123+
[
124+
{
125+
"files": "./README.md",
126+
"from": /## Getting Started\\.\\*## Development/gs,
127+
"to": "## Development",
128+
},
129+
],
130+
[
131+
{
132+
"files": "./.github/DEVELOPMENT.md",
133+
"from": /\\\\n## Setup Scripts\\.\\*\\$/gs,
134+
"to": "",
135+
},
136+
],
137+
[
138+
{
139+
"files": "./knip.jsonc",
140+
"from": " \\"src/initialize/index.ts\\",
141+
",
142+
"to": "",
143+
},
144+
],
145+
[
146+
{
147+
"files": "./knip.jsonc",
148+
"from": " \\"src/migrate/index.ts\\",
149+
",
150+
"to": "",
151+
},
152+
],
153+
[
154+
{
155+
"files": "./knip.jsonc",
156+
"from": "[\\"src/index.ts!\\", \\"script/initialize*.js\\"]",
157+
"to": "\\"src/index.ts!\\"",
158+
},
159+
],
160+
[
161+
{
162+
"files": "./knip.jsonc",
163+
"from": "[\\"src/**/*.ts!\\", \\"script/**/*.js\\"]",
164+
"to": "\\"src/**/*.ts!\\"",
165+
},
166+
],
167+
]
168+
`);
169+
});
170+
171+
it("replaces using the extra replacements when the existing package data is full", async () => {
172+
mockReadFileSafeAsJson.mockResolvedValue({
173+
description: "Existing description",
174+
version: "1.2.3",
175+
});
176+
mockReplaceInFile.mockResolvedValue([]);
177+
178+
await updateLocalFiles(stubOptions);
179+
180+
expect(mockReplaceInFile.mock.calls).toMatchInlineSnapshot(`
181+
[
182+
[
183+
{
184+
"files": [
185+
"./.github/**/*",
186+
"./*.*",
187+
],
188+
"from": /Template TypeScript Node Package/g,
189+
"to": "Stub Title",
190+
},
191+
],
192+
[
193+
{
194+
"files": [
195+
"./.github/**/*",
196+
"./*.*",
197+
],
198+
"from": /JoshuaKGoldberg/g,
199+
"to": "StubOwner",
200+
},
201+
],
202+
[
203+
{
204+
"files": [
205+
"./.github/**/*",
206+
"./*.*",
207+
],
208+
"from": /template-typescript-node-package/g,
209+
"to": "stub-repository",
210+
},
211+
],
212+
[
213+
{
214+
"files": ".eslintrc.cjs",
215+
"from": /\\\\/\\\\\\*\\\\n\\.\\+\\\\\\*\\\\/\\\\n\\\\n/gs,
216+
"to": "",
217+
},
218+
],
219+
[
220+
{
221+
"files": "./package.json",
222+
"from": /"author": "\\.\\+"/g,
223+
"to": "\\"author\\": \\"stub-npm-author\\"",
224+
},
225+
],
226+
[
227+
{
228+
"files": "./package.json",
229+
"from": /"bin": "\\.\\+\\\\n/g,
230+
"to": "",
231+
},
232+
],
233+
[
234+
{
235+
"files": "./package.json",
236+
"from": /"initialize:test": "\\.\\*/g,
237+
"to": "",
238+
},
239+
],
240+
[
241+
{
242+
"files": "./package.json",
243+
"from": /"initialize": "\\.\\*/g,
244+
"to": "",
245+
},
246+
],
247+
[
248+
{
249+
"files": "./package.json",
250+
"from": /"migrate:test": "\\.\\+\\\\n/g,
251+
"to": "",
252+
},
253+
],
254+
[
255+
{
256+
"files": "./README.md",
257+
"from": /## Getting Started\\.\\*## Development/gs,
258+
"to": "## Development",
259+
},
260+
],
261+
[
262+
{
263+
"files": "./.github/DEVELOPMENT.md",
264+
"from": /\\\\n## Setup Scripts\\.\\*\\$/gs,
265+
"to": "",
266+
},
267+
],
268+
[
269+
{
270+
"files": "./knip.jsonc",
271+
"from": " \\"src/initialize/index.ts\\",
272+
",
273+
"to": "",
274+
},
275+
],
276+
[
277+
{
278+
"files": "./knip.jsonc",
279+
"from": " \\"src/migrate/index.ts\\",
280+
",
281+
"to": "",
282+
},
283+
],
284+
[
285+
{
286+
"files": "./knip.jsonc",
287+
"from": "[\\"src/index.ts!\\", \\"script/initialize*.js\\"]",
288+
"to": "\\"src/index.ts!\\"",
289+
},
290+
],
291+
[
292+
{
293+
"files": "./knip.jsonc",
294+
"from": "[\\"src/**/*.ts!\\", \\"script/**/*.js\\"]",
295+
"to": "\\"src/**/*.ts!\\"",
296+
},
297+
],
298+
[
299+
{
300+
"files": [
301+
"./.github/**/*",
302+
"./*.*",
303+
],
304+
"from": /Existing description/g,
305+
"to": "Stub description.",
306+
},
307+
],
308+
[
309+
{
310+
"files": "./package.json",
311+
"from": /"version": "1\\.2\\.3"/g,
312+
"to": "\\"version\\": \\"0.0.0\\"",
313+
},
314+
],
315+
]
316+
`);
317+
});
318+
});

Diff for: src/initialize/steps/updateLocalFiles.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import replaceInFile from "replace-in-file";
22

3-
import { readFileAsJson } from "../../shared/readFileAsJson.js";
3+
import { readFileSafeAsJson } from "../../shared/readFileSafeAsJson.js";
44

55
interface UpdateLocalFilesOptions {
66
description: string;
@@ -22,11 +22,11 @@ export async function updateLocalFiles({
2222
repository,
2323
title,
2424
}: UpdateLocalFilesOptions) {
25-
const existingPackage = (await readFileAsJson(
25+
const existingPackage = (await readFileSafeAsJson(
2626
"./package.json",
2727
)) as ExistingPackageData;
2828

29-
const replacements: [RegExp | string, string, string?][] = [
29+
const replacements = [
3030
[/Template TypeScript Node Package/g, title],
3131
[/JoshuaKGoldberg/g, owner],
3232
[/template-typescript-node-package/g, repository],

0 commit comments

Comments
 (0)