@knitli/astro-docs-template 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knitli/astro-docs-template",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Opinionated Astro + Starlight docs site template with Knitli branding",
5
5
  "keywords": [
6
6
  "knitli",
@@ -64,10 +64,8 @@
64
64
  "starlight-scroll-to-top": ">=0.4.0",
65
65
  "starlight-sidebar-topics": ">=0.7.1",
66
66
  "starlight-tags": ">=0.4.0",
67
- "vite-tsconfig-paths": "6.1.1"
68
- },
69
- "devDependencies": {
70
- "@astrojs/check": "^0.9.6",
67
+ "vite-tsconfig-paths": "6.1.1",
68
+ "vite": "^7.0.0",
71
69
  "@astrojs/compiler-rs": "^0.1.4",
72
70
  "@biomejs/biome": "^2.4.2",
73
71
  "@knitli/tsconfig": "*",
@@ -77,6 +75,9 @@
77
75
  "lightningcss": "^1.30.2",
78
76
  "sharp": "^0.34.5",
79
77
  "svgo": "^4.0.0",
78
+ "@astrojs/check": "^0.9.6"
79
+ },
80
+ "devDependencies": {
80
81
  "typescript": "^5.9.3",
81
82
  "vitest": "^4.0.18"
82
83
  },
@@ -11,7 +11,7 @@ tools.bun = "latest"
11
11
 
12
12
  [tasks.gentypes]
13
13
  description = "Generate Cloudflare types for the project"
14
- run = "bunx wrangler types"
14
+ run = "bunx wrangler types && bunx astro sync"
15
15
  tools.bun = "latest"
16
16
  tools.wrangler = "latest"
17
17
  depends = ["installdeps"]
@@ -63,3 +63,7 @@ tools.biome = "latest"
63
63
  description = "Fix linting issues using Biome"
64
64
  run = "biome check . --write --changed"
65
65
  tools.biome = "latest"
66
+
67
+ [tasks.ufix]
68
+ description = "Use unsafe fixes with Biome"
69
+ run = "biome check . --write --changed --unsafe"
@@ -7,6 +7,7 @@
7
7
  "astro": "bunx astro",
8
8
  "prebuild": "bunx wrangler types; bunx astro sync",
9
9
  "build": "bunx astro build",
10
+ "predeploy": "bun run build",
10
11
  "deploy": "bun run build && bunx wrangler deploy",
11
12
  "dev": "bunx astro dev",
12
13
  "prepare": "test -f ./node_modules/bun/install.js && node ./node_modules/bun/install.js || true",
@@ -14,8 +15,10 @@
14
15
  "wrangler": "bunx wrangler"
15
16
  },
16
17
  "dependencies": {
18
+ "@astrojs/starlight": "^0.38.1",
17
19
  "@knitli/astro-docs-template": "latest",
18
- "@knitli/docs-components": "latest"
20
+ "@knitli/docs-components": "latest",
21
+ "astro": "^6.1.1"
19
22
  },
20
23
  "devDependencies": {
21
24
  "@astrojs/check": "latest",
@@ -1,7 +1,20 @@
1
1
  import { defineCollection } from "astro:content";
2
2
  import { docsLoader } from "@astrojs/starlight/loaders";
3
3
  import { docsSchema } from "@astrojs/starlight/schema";
4
+ import { changelogsLoader } from "starlight-changelogs/loader";
4
5
 
5
6
  export const collections = {
6
7
  docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
8
+ changelogs: defineCollection({
9
+ loader: changelogsLoader([
10
+ {
11
+ provider: "keep-a-changelog",
12
+ base: "changelog",
13
+ changelog: "../../CHANGELOG.md",
14
+ pageSize: 10,
15
+ title: "Changelog",
16
+ pagefind: true,
17
+ },
18
+ ]),
19
+ }),
7
20
  };
@@ -9,7 +9,7 @@
9
9
  "directory": "./dist",
10
10
  "binding": "ASSETS",
11
11
  "html_handling": "auto-trailing-slash",
12
- "not_found_handling": "404-errors",
12
+ "not_found_handling": "404-page",
13
13
  "run_worker_first": false
14
14
  },
15
15
  "observability": {
package/src/config.ts CHANGED
@@ -52,8 +52,10 @@ export const {
52
52
  headlineLogoLight,
53
53
  variables,
54
54
  docsStyle,
55
- faviconIco,
56
- faviconSvg,
55
+ knitliFaviconIco,
56
+ knitliFaviconSvg,
57
+ codeweaverPrimary,
58
+ codeweaverReverse,
57
59
  } = DocsAssets;
58
60
 
59
61
  const shikiCfg = {
@@ -197,11 +199,12 @@ const getIntegrations = (options: IntegrationOptions) => {
197
199
  markdoc(),
198
200
  llmEnhancements({ llmsTxt: false }),
199
201
  mdx(),
202
+ starlightIconsIntegration(),
200
203
  favicons({
201
204
  name: `${appName} Docs by Knitli`,
202
205
  short_name: `${appName} Docs`,
203
206
  input: {
204
- favicons: [faviconSvg],
207
+ favicons: [knitliFaviconSvg],
205
208
  },
206
209
  }),
207
210
  cloudflarePagesHeaders({}),
@@ -257,7 +260,6 @@ const get_plugins = (options: PluginOptions) => {
257
260
  starlightAnnouncement(),
258
261
  starlightChangelogs(),
259
262
  starlightHeadingBadges(),
260
- starlightIconsIntegration(),
261
263
  starlightIconsPlugin(),
262
264
  starlightLinksValidator(),
263
265
  starlightPageActions({
@@ -317,6 +319,16 @@ const get_plugins = (options: PluginOptions) => {
317
319
  return [...filtered, ...overrides];
318
320
  };
319
321
 
322
+ const defaultHeadersConfig: OutgoingHttpHeaders = {
323
+ "X-Frame-Options": "DENY",
324
+ "X-Content-Type-Options": "nosniff",
325
+ "Referrer-Policy": "strict-origin-when-cross-origin",
326
+ "Permissions-Policy":
327
+ "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()",
328
+ "content-security-policy":
329
+ "default-src 'self'; script-src 'self' 'unsafe-inline' static.cloudflareinsights.com zaraz.cloudflare.com; connect-src 'self' cloudflareinsights.com *.cloudflareinsights.com; img-src 'self' media.knitli.com data: avatars.githubusercontent.com ui-avatars.com media.knitli.com knitli.com; style-src 'self' 'unsafe-inline' fonts.googleapis.com; font-src 'self' fonts.gstatic.com; frame-ancestors 'none'",
330
+ };
331
+
320
332
  export default function createConfig(options: DocsTemplateOptions) {
321
333
  const {
322
334
  appName,
@@ -334,7 +346,7 @@ export default function createConfig(options: DocsTemplateOptions) {
334
346
  integrationConfigs,
335
347
  unwantedIntegrations,
336
348
  unwantedPlugins,
337
- headersConfig,
349
+ headersConfig = defaultHeadersConfig,
338
350
  } = options;
339
351
 
340
352
  // https://astro.build/config
@@ -378,7 +390,7 @@ export default function createConfig(options: DocsTemplateOptions) {
378
390
  ],
379
391
  },
380
392
  server: {
381
- headers: headersConfig,
393
+ headers: { ...defaultHeadersConfig, ...headersConfig },
382
394
  },
383
395
  trailingSlash: "always",
384
396
  // Vite configuration for better bundling
@@ -478,10 +490,10 @@ export default function createConfig(options: DocsTemplateOptions) {
478
490
  pagefind: true,
479
491
  description: description,
480
492
  logo: {
481
- dark: logoDark,
482
- light: logoLight,
493
+ dark: is_codeweaver ? codeweaverReverse : logoDark,
494
+ light: is_codeweaver ? codeweaverPrimary : logoLight,
483
495
  alt: is_codeweaver ? "CodeWeaver Logo" : "Knitli Logo",
484
- replacesTitle: true,
496
+ replacesTitle: !is_codeweaver,
485
497
  },
486
498
  editLink: {
487
499
  baseUrl: `https://github.com/knitli/${appName.toLowerCase()}/edit/main/docs-site/src`,
package/src/index.test.ts CHANGED
@@ -2,18 +2,24 @@
2
2
  //
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
 
5
- import { existsSync, mkdtempSync, readFileSync, writeFileSync, rmSync } from "node:fs";
6
- import { join } from "node:path";
5
+ import {
6
+ existsSync,
7
+ mkdtempSync,
8
+ readFileSync,
9
+ rmSync,
10
+ writeFileSync,
11
+ } from "node:fs";
7
12
  import { tmpdir } from "node:os";
13
+ import { join } from "node:path";
8
14
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
9
15
  import {
10
16
  addPieces,
17
+ type InitOptions,
11
18
  initDocsTemplate,
12
19
  mergePackageJsonDeps,
13
- normaliseDependencyVersion,
20
+ normalizeDependencyVersion,
14
21
  PIECE_NAMES,
15
22
  PIECES,
16
- type InitOptions,
17
23
  } from "./index";
18
24
 
19
25
  // ── helpers ──────────────────────────────────────────────────────────────────
@@ -29,32 +35,32 @@ const BASE_OPTIONS: InitOptions = {
29
35
  workerName: "test-app-docs",
30
36
  };
31
37
 
32
- // ── normaliseDependencyVersion ────────────────────────────────────────────────
38
+ // ── normalizeDependencyVersion ────────────────────────────────────────────────
33
39
 
34
- describe("normaliseDependencyVersion", () => {
40
+ describe("normalizeDependencyVersion", () => {
35
41
  it("converts workspace:* to *", () => {
36
- expect(normaliseDependencyVersion("workspace:*")).toBe("*");
42
+ expect(normalizeDependencyVersion("workspace:*")).toBe("*");
37
43
  });
38
44
 
39
45
  it("strips workspace: prefix from semver ranges, preserving the range", () => {
40
- expect(normaliseDependencyVersion("workspace:^1.2.3")).toBe("^1.2.3");
41
- expect(normaliseDependencyVersion("workspace:~1.0.0")).toBe("~1.0.0");
42
- expect(normaliseDependencyVersion("workspace:>=2.0.0")).toBe(">=2.0.0");
46
+ expect(normalizeDependencyVersion("workspace:^1.2.3")).toBe("^1.2.3");
47
+ expect(normalizeDependencyVersion("workspace:~1.0.0")).toBe("~1.0.0");
48
+ expect(normalizeDependencyVersion("workspace:>=2.0.0")).toBe(">=2.0.0");
43
49
  });
44
50
 
45
51
  it("converts catalog:dev-common to latest", () => {
46
- expect(normaliseDependencyVersion("catalog:dev-common")).toBe("latest");
52
+ expect(normalizeDependencyVersion("catalog:dev-common")).toBe("latest");
47
53
  });
48
54
 
49
55
  it("converts catalog:types to latest", () => {
50
- expect(normaliseDependencyVersion("catalog:types")).toBe("latest");
56
+ expect(normalizeDependencyVersion("catalog:types")).toBe("latest");
51
57
  });
52
58
 
53
59
  it("leaves regular semver ranges unchanged", () => {
54
- expect(normaliseDependencyVersion("^1.2.3")).toBe("^1.2.3");
55
- expect(normaliseDependencyVersion("~2.0.0")).toBe("~2.0.0");
56
- expect(normaliseDependencyVersion("latest")).toBe("latest");
57
- expect(normaliseDependencyVersion("*")).toBe("*");
60
+ expect(normalizeDependencyVersion("^1.2.3")).toBe("^1.2.3");
61
+ expect(normalizeDependencyVersion("~2.0.0")).toBe("~2.0.0");
62
+ expect(normalizeDependencyVersion("latest")).toBe("latest");
63
+ expect(normalizeDependencyVersion("*")).toBe("*");
58
64
  });
59
65
  });
60
66
 
@@ -75,14 +81,16 @@ describe("mergePackageJsonDeps", () => {
75
81
  expect(result.dependencies?.astro).toBe("^5.0.0");
76
82
  });
77
83
 
78
- it("normalises workspace:* in template deps", () => {
84
+ it("normalizes workspace:* in template deps", () => {
79
85
  const existing = { name: "x" };
80
- const template = { dependencies: { "@knitli/astro-docs-template": "workspace:*" } };
86
+ const template = {
87
+ dependencies: { "@knitli/astro-docs-template": "workspace:*" },
88
+ };
81
89
  const result = mergePackageJsonDeps(existing, template);
82
90
  expect(result.dependencies?.["@knitli/astro-docs-template"]).toBe("*");
83
91
  });
84
92
 
85
- it("normalises catalog:* in template devDependencies", () => {
93
+ it("normalizes catalog:* in template devDependencies", () => {
86
94
  const existing = { name: "x" };
87
95
  const template = { devDependencies: { typescript: "catalog:dev-common" } };
88
96
  const result = mergePackageJsonDeps(existing, template);
@@ -91,7 +99,12 @@ describe("mergePackageJsonDeps", () => {
91
99
 
92
100
  it("merges devDependencies", () => {
93
101
  const existing = { name: "x", devDependencies: { vitest: "^4.0.0" } };
94
- const template = { devDependencies: { typescript: "catalog:dev-common", "@types/node": "catalog:types" } };
102
+ const template = {
103
+ devDependencies: {
104
+ typescript: "catalog:dev-common",
105
+ "@types/node": "catalog:types",
106
+ },
107
+ };
95
108
  const result = mergePackageJsonDeps(existing, template);
96
109
  expect(result.devDependencies).toEqual({
97
110
  vitest: "^4.0.0",
@@ -108,8 +121,18 @@ describe("mergePackageJsonDeps", () => {
108
121
  });
109
122
 
110
123
  it("does not touch name, version, scripts, or other fields", () => {
111
- const existing = { name: "my-app", version: "1.2.3", scripts: { build: "tsc" }, private: true };
112
- const template = { name: "template", version: "0.0.1", scripts: { dev: "astro dev" }, dependencies: { astro: "*" } };
124
+ const existing = {
125
+ name: "my-app",
126
+ version: "1.2.3",
127
+ scripts: { build: "tsc" },
128
+ private: true,
129
+ };
130
+ const template = {
131
+ name: "template",
132
+ version: "0.0.1",
133
+ scripts: { dev: "astro dev" },
134
+ dependencies: { astro: "*" },
135
+ };
113
136
  const result = mergePackageJsonDeps(existing, template);
114
137
  expect(result.name).toBe("my-app");
115
138
  expect(result.version).toBe("1.2.3");
@@ -159,7 +182,11 @@ describe("initDocsTemplate", () => {
159
182
 
160
183
  it("always clobbers existing files (init always overwrites, no flag needed)", () => {
161
184
  // Write an existing file that will be overwritten
162
- writeFileSync(join(tmpDir, "tsconfig.json"), '{"compilerOptions":{}}', "utf-8");
185
+ writeFileSync(
186
+ join(tmpDir, "tsconfig.json"),
187
+ '{"compilerOptions":{}}',
188
+ "utf-8",
189
+ );
163
190
  const created = initDocsTemplate(tmpDir, BASE_OPTIONS);
164
191
  // tsconfig.json should have been re-created
165
192
  expect(created.some((f) => f.includes("tsconfig.json"))).toBe(true);
@@ -192,7 +219,10 @@ describe("addPieces", () => {
192
219
  });
193
220
 
194
221
  it("creates the requested file when it does not exist", () => {
195
- const created = addPieces(tmpDir, { ...BASE_OPTIONS, pieces: ["tsconfig"] });
222
+ const created = addPieces(tmpDir, {
223
+ ...BASE_OPTIONS,
224
+ pieces: ["tsconfig"],
225
+ });
196
226
  expect(created.length).toBe(1);
197
227
  expect(existsSync(join(tmpDir, "tsconfig.json"))).toBe(true);
198
228
  });
@@ -200,18 +230,29 @@ describe("addPieces", () => {
200
230
  it("skips an existing non-package.json file without --force", () => {
201
231
  // Pre-populate the tsconfig so it already exists
202
232
  writeFileSync(join(tmpDir, "tsconfig.json"), '{"existing":true}', "utf-8");
203
- const created = addPieces(tmpDir, { ...BASE_OPTIONS, pieces: ["tsconfig"] });
233
+ const created = addPieces(tmpDir, {
234
+ ...BASE_OPTIONS,
235
+ pieces: ["tsconfig"],
236
+ });
204
237
  expect(created.length).toBe(0);
205
238
  // Original content should be intact
206
- const content = JSON.parse(readFileSync(join(tmpDir, "tsconfig.json"), "utf-8"));
239
+ const content = JSON.parse(
240
+ readFileSync(join(tmpDir, "tsconfig.json"), "utf-8"),
241
+ );
207
242
  expect(content.existing).toBe(true);
208
243
  });
209
244
 
210
245
  it("overwrites an existing non-package.json file with --force", () => {
211
246
  writeFileSync(join(tmpDir, "tsconfig.json"), '{"existing":true}', "utf-8");
212
- const created = addPieces(tmpDir, { ...BASE_OPTIONS, pieces: ["tsconfig"], force: true });
247
+ const created = addPieces(tmpDir, {
248
+ ...BASE_OPTIONS,
249
+ pieces: ["tsconfig"],
250
+ force: true,
251
+ });
213
252
  expect(created.length).toBe(1);
214
- const content = JSON.parse(readFileSync(join(tmpDir, "tsconfig.json"), "utf-8"));
253
+ const content = JSON.parse(
254
+ readFileSync(join(tmpDir, "tsconfig.json"), "utf-8"),
255
+ );
215
256
  expect(content.existing).toBeUndefined();
216
257
  });
217
258
 
@@ -238,13 +279,19 @@ describe("addPieces", () => {
238
279
  private: true,
239
280
  dependencies: { express: "^4.0.0" },
240
281
  };
241
- writeFileSync(join(tmpDir, "package.json"), JSON.stringify(existingPkg, null, 2), "utf-8");
282
+ writeFileSync(
283
+ join(tmpDir, "package.json"),
284
+ JSON.stringify(existingPkg, null, 2),
285
+ "utf-8",
286
+ );
242
287
 
243
288
  const created = addPieces(tmpDir, { ...BASE_OPTIONS, pieces: ["deps"] });
244
289
  // Should be modified (not 0 files)
245
290
  expect(created.length).toBe(1);
246
291
 
247
- const merged = JSON.parse(readFileSync(join(tmpDir, "package.json"), "utf-8"));
292
+ const merged = JSON.parse(
293
+ readFileSync(join(tmpDir, "package.json"), "utf-8"),
294
+ );
248
295
  // Existing non-dep fields are preserved
249
296
  expect(merged.name).toBe("my-other-project");
250
297
  expect(merged.version).toBe("3.0.0");
@@ -256,11 +303,17 @@ describe("addPieces", () => {
256
303
  expect(merged.devDependencies?.["@astrojs/check"]).toBeDefined();
257
304
  });
258
305
 
259
- it("normalises workspace:* references when merging", () => {
260
- writeFileSync(join(tmpDir, "package.json"), '{"name":"x","dependencies":{}}', "utf-8");
306
+ it("normalizes workspace:* references when merging", () => {
307
+ writeFileSync(
308
+ join(tmpDir, "package.json"),
309
+ '{"name":"x","dependencies":{}}',
310
+ "utf-8",
311
+ );
261
312
  addPieces(tmpDir, { ...BASE_OPTIONS, pieces: ["deps"] });
262
- const merged = JSON.parse(readFileSync(join(tmpDir, "package.json"), "utf-8"));
263
- // workspace:* should be normalised away
313
+ const merged = JSON.parse(
314
+ readFileSync(join(tmpDir, "package.json"), "utf-8"),
315
+ );
316
+ // workspace:* should be normalized away
264
317
  const allVersions = Object.values({
265
318
  ...merged.dependencies,
266
319
  ...merged.devDependencies,
@@ -270,18 +323,32 @@ describe("addPieces", () => {
270
323
  });
271
324
 
272
325
  it("throws a helpful error when existing package.json is not valid JSON", () => {
273
- writeFileSync(join(tmpDir, "package.json"), "{ this is not valid json", "utf-8");
326
+ writeFileSync(
327
+ join(tmpDir, "package.json"),
328
+ "{ this is not valid json",
329
+ "utf-8",
330
+ );
274
331
  expect(() =>
275
332
  addPieces(tmpDir, { ...BASE_OPTIONS, pieces: ["deps"] }),
276
333
  ).toThrow(/Failed to parse existing package\.json/);
277
334
  });
278
335
 
279
336
  it("replaces existing package.json wholesale with --force", () => {
280
- const existingPkg = { name: "my-other-project", version: "3.0.0", scripts: { start: "node server.js" } };
281
- writeFileSync(join(tmpDir, "package.json"), JSON.stringify(existingPkg, null, 2), "utf-8");
337
+ const existingPkg = {
338
+ name: "my-other-project",
339
+ version: "3.0.0",
340
+ scripts: { start: "node server.js" },
341
+ };
342
+ writeFileSync(
343
+ join(tmpDir, "package.json"),
344
+ JSON.stringify(existingPkg, null, 2),
345
+ "utf-8",
346
+ );
282
347
 
283
348
  addPieces(tmpDir, { ...BASE_OPTIONS, pieces: ["deps"], force: true });
284
- const result = JSON.parse(readFileSync(join(tmpDir, "package.json"), "utf-8"));
349
+ const result = JSON.parse(
350
+ readFileSync(join(tmpDir, "package.json"), "utf-8"),
351
+ );
285
352
  // Should be the template's package.json content (with substitution applied)
286
353
  expect(result.name).toBe(BASE_OPTIONS.name);
287
354
  });
@@ -290,7 +357,10 @@ describe("addPieces", () => {
290
357
  // ── content piece ─────────────────────────────────────────────────────────
291
358
 
292
359
  it("copies directory pieces (starter-content)", () => {
293
- const created = addPieces(tmpDir, { ...BASE_OPTIONS, pieces: ["starter-content"] });
360
+ const created = addPieces(tmpDir, {
361
+ ...BASE_OPTIONS,
362
+ pieces: ["starter-content"],
363
+ });
294
364
  expect(created.length).toBeGreaterThan(0);
295
365
  expect(existsSync(join(tmpDir, "src/content/docs"))).toBe(true);
296
366
  });
@@ -298,7 +368,10 @@ describe("addPieces", () => {
298
368
  // ── multiple pieces ───────────────────────────────────────────────────────
299
369
 
300
370
  it("can add multiple pieces at once", () => {
301
- const created = addPieces(tmpDir, { ...BASE_OPTIONS, pieces: ["tsconfig", "styles"] });
371
+ const created = addPieces(tmpDir, {
372
+ ...BASE_OPTIONS,
373
+ pieces: ["tsconfig", "styles"],
374
+ });
302
375
  expect(existsSync(join(tmpDir, "tsconfig.json"))).toBe(true);
303
376
  expect(existsSync(join(tmpDir, "src/styles/custom.css"))).toBe(true);
304
377
  expect(created.length).toBe(2);
package/src/index.ts CHANGED
@@ -166,7 +166,7 @@ function copyDirRecursive(
166
166
  }
167
167
 
168
168
  /**
169
- * Normalise a dependency version specifier so that workspace-protocol and
169
+ * Normalize a dependency version specifier so that workspace-protocol and
170
170
  * catalog entries from the Knitli monorepo become portable version strings
171
171
  * usable in any project.
172
172
  *
@@ -175,7 +175,7 @@ function copyDirRecursive(
175
175
  * "catalog:something" → "latest"
176
176
  * anything else → unchanged
177
177
  */
178
- export function normaliseDependencyVersion(version: string): string {
178
+ export function normalizeDependencyVersion(version: string): string {
179
179
  if (version.startsWith("workspace:")) {
180
180
  const spec = version.slice("workspace:".length).trim();
181
181
  if (spec === "" || spec === "*") return "*";
@@ -195,7 +195,7 @@ type PackageJsonLike = {
195
195
 
196
196
  /**
197
197
  * Merge the `dependencies`, `devDependencies`, and `peerDependencies` from
198
- * `templatePkg` into `existingPkg`, normalising any monorepo-specific version
198
+ * `templatePkg` into `existingPkg`, normalizing any monorepo-specific version
199
199
  * specifiers so the result is portable. Template entries take precedence over
200
200
  * existing entries for the same package name.
201
201
  *
@@ -215,11 +215,11 @@ export function mergePackageJsonDeps(
215
215
  for (const field of depFields) {
216
216
  const templateDeps = templatePkg[field];
217
217
  if (!templateDeps || Object.keys(templateDeps).length === 0) continue;
218
- const normalised: PackageJsonDeps = {};
218
+ const normalized: PackageJsonDeps = {};
219
219
  for (const [pkg, version] of Object.entries(templateDeps)) {
220
- normalised[pkg] = normaliseDependencyVersion(version);
220
+ normalized[pkg] = normalizeDependencyVersion(version);
221
221
  }
222
- result[field] = { ...(existingPkg[field] ?? {}), ...normalised };
222
+ result[field] = { ...(existingPkg[field] ?? {}), ...normalized };
223
223
  }
224
224
  return result;
225
225
  }
@@ -231,10 +231,10 @@ export function mergePackageJsonDeps(
231
231
  * (with placeholder substitution).
232
232
  * - If `force` is set, the file is overwritten wholesale.
233
233
  * - Otherwise the `dependencies`, `devDependencies`, and `peerDependencies`
234
- * from the template are merged into the existing file, normalising any
234
+ * from the template are merged into the existing file, normalizing any
235
235
  * workspace-protocol / catalog version specifiers to portable equivalents.
236
236
  *
237
- * **Note**: the merged file is re-serialised with 2-space indentation using
237
+ * **Note**: the merged file is re-serialized with 2-space indentation using
238
238
  * `JSON.stringify`. Any bespoke formatting or key ordering in the original
239
239
  * file will not be preserved.
240
240
  */
@@ -258,7 +258,9 @@ function mergeOrCopyPackageJson(
258
258
 
259
259
  let existingPkg: PackageJsonLike;
260
260
  try {
261
- existingPkg = JSON.parse(readFileSync(destPath, "utf-8")) as PackageJsonLike;
261
+ existingPkg = JSON.parse(
262
+ readFileSync(destPath, "utf-8"),
263
+ ) as PackageJsonLike;
262
264
  } catch (err) {
263
265
  const msg = err instanceof Error ? err.message : String(err);
264
266
  throw new Error(