create-zudo-doc 5.17.1 → 5.18.0

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/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@ All notable changes to `create-zudo-doc` are documented in this file.
4
4
 
5
5
  The format is based on Keep a Changelog, and release notes are generated from the changelog MDX pages.
6
6
 
7
+ ## [5.18.0] - 2026-09-06
8
+
9
+ ### Other Changes
10
+
11
+ - A generated project now pins `@takazudo/zdtp` at `0.5.0` (was `0.4.14`), matching the peer requirement of the `@takazudo/zudo-doc` released alongside it (`a55182f9c`).
12
+
13
+ ## [5.17.2] - 2026-09-05
14
+
15
+ ### Bug Fixes
16
+
17
+ - Prevent generated documentation-skill setup scripts from overwriting real files or directories when creating symlinks (`559e7a1`)
18
+ - Detect locale settings inside spread `zudoDoc(...)` configurations when generating documentation skills (`c1658c9`)
19
+
7
20
  ## [5.17.1] - 2026-09-04
8
21
 
9
22
  ### Other Changes
@@ -31,5 +31,5 @@ export declare function deriveDocSkillName(projectName: string): string;
31
31
  *
32
32
  * Bumped in lockstep by scripts/release-create-zudo-doc.sh.
33
33
  */
34
- export declare const ZUDO_DOC_PIN = "^5.17.1";
34
+ export declare const ZUDO_DOC_PIN = "^5.18.0";
35
35
  export declare function scaffold(choices: UserChoices): Promise<void>;
package/dist/scaffold.js CHANGED
@@ -47,7 +47,7 @@ export function deriveDocSkillName(projectName) {
47
47
  *
48
48
  * Bumped in lockstep by scripts/release-create-zudo-doc.sh.
49
49
  */
50
- export const ZUDO_DOC_PIN = "^5.17.1";
50
+ export const ZUDO_DOC_PIN = "^5.18.0";
51
51
  /**
52
52
  * Files in `templates/base/**` that must not be copied by the unconditional
53
53
  * base mirror. Each entry is matched against the path relative to
@@ -840,7 +840,7 @@ function generatePackageJson(choices, localePlan) {
840
840
  // same reason. This is the ACCEPTED, permanent contract per #2668 — see
841
841
  // the "@takazudo/zdtp dep implication" note in
842
842
  // packages/zudo-doc/docs/adr/route-injection-seam.md.
843
- "@takazudo/zdtp": "0.4.14",
843
+ "@takazudo/zdtp": "0.5.0",
844
844
  // (@takazudo/zudo-doc-history-server is NOT here — it is gated on the
845
845
  // docHistory or assetViewer features, see the block below. It was briefly unconditional
846
846
  // (#3080) to work around doc-history-area importing its `/exclude` subpath
@@ -889,7 +889,7 @@ function generatePackageJson(choices, localePlan) {
889
889
  // `/exclude` at module scope from the always-bundled chrome graph; #3110
890
890
  // moved compileExclude into @takazudo/zudo-doc, so projects with both
891
891
  // docHistory and assetViewer off no longer need the package at all.
892
- deps["@takazudo/zudo-doc-history-server"] = "^5.17.1";
892
+ deps["@takazudo/zudo-doc-history-server"] = "^5.18.0";
893
893
  // tsx is no longer needed here: the relocated package plugin imports the
894
894
  // runner directly (no `tsx -e` spawn) since the package ships compiled
895
895
  // dist/ — package-first migration #2321 (#2337).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-zudo-doc",
3
- "version": "5.17.1",
3
+ "version": "5.18.0",
4
4
  "description": "Create a new zudo-doc documentation site",
5
5
  "license": "MIT",
6
6
  "author": "Takeshi Takatsudo",
@@ -298,15 +298,38 @@ function matchingBrace(masked, open) {
298
298
  return -1;
299
299
  }
300
300
 
301
+ function spreadZudoDocObjectRanges(masked, depths) {
302
+ const ranges = [];
303
+ const spreadPattern = /\.\.\.\s*zudoDoc\s*\(\s*/g;
304
+ let match;
305
+ while ((match = spreadPattern.exec(masked)) !== null) {
306
+ const open = match.index + match[0].length;
307
+ if (masked[open] !== "{") continue;
308
+ const close = matchingBrace(masked, open);
309
+ if (close < 0) continue;
310
+ ranges.push({ open, close, propertyDepth: depths[open] + 1 });
311
+ }
312
+ return ranges;
313
+ }
314
+
315
+ function isTopLevelSetting(index, depths, spreadRanges) {
316
+ if (depths[index] === 1) return true;
317
+ return spreadRanges.some(
318
+ ({ open, close, propertyDepth }) =>
319
+ index > open && index < close && depths[index] === propertyDepth,
320
+ );
321
+ }
322
+
301
323
  function topLevelObject(source, property) {
302
324
  const masked = maskSource(source);
303
325
  const depths = braceDepths(masked);
326
+ const spreadRanges = spreadZudoDocObjectRanges(masked, depths);
304
327
  const propertyPattern = new RegExp(`\\b${property}\\s*:`, "g");
305
328
  let match;
306
329
  while ((match = propertyPattern.exec(masked)) !== null) {
307
- // Direct fields of the settings/zudoDoc object are at depth 1. Nested
308
- // `versions[].locales` and nav-label locales are intentionally ignored.
309
- if (depths[match.index] !== 1) continue;
330
+ // Direct settings objects use depth 1; spread zudoDoc settings use their
331
+ // explicitly identified object depth. Nested locale maps stay ignored.
332
+ if (!isTopLevelSetting(match.index, depths, spreadRanges)) continue;
310
333
  let open = match.index + match[0].length;
311
334
  while (/\s/.test(masked[open] ?? "")) open += 1;
312
335
  if (masked[open] !== "{") continue;
@@ -320,10 +343,11 @@ function topLevelObject(source, property) {
320
343
  function topLevelString(source, property) {
321
344
  const masked = maskSource(source);
322
345
  const depths = braceDepths(masked);
346
+ const spreadRanges = spreadZudoDocObjectRanges(masked, depths);
323
347
  const propertyPattern = new RegExp(`\\b${property}\\s*:`, "g");
324
348
  let match;
325
349
  while ((match = propertyPattern.exec(masked)) !== null) {
326
- if (depths[match.index] !== 1) continue;
350
+ if (!isTopLevelSetting(match.index, depths, spreadRanges)) continue;
327
351
  const tail = source.slice(match.index + match[0].length).trimStart();
328
352
  const quote = tail[0];
329
353
  if (quote !== "\"" && quote !== "'") continue;
@@ -407,6 +431,12 @@ for (const { source } of sources) {
407
431
  if (localeObject === null) localeObject = topLevelObject(source, "locales");
408
432
  }
409
433
 
434
+ if (defaultLocale === null && docsDir === null && localeObject === null) {
435
+ console.error(
436
+ "no explicit locale settings found in zfb.config.ts; assuming defaults (en, src/content/docs, no additional locales)",
437
+ );
438
+ }
439
+
410
440
  console.log(`default\t${defaultLocale ?? "en"}`);
411
441
  console.log(`docs\t${docsDir ?? "src/content/docs"}`);
412
442
  if (localeObject) {
@@ -470,12 +500,15 @@ physical_dir() {
470
500
  fi
471
501
  }
472
502
 
473
- # Helper: replace a symlink or file at the given path
503
+ # Helper: replace a symlink at the given path; refuse to remove real files or directories
474
504
  ensure_symlink() {
475
505
  local link_path="$1"
476
506
  local target="$2"
477
- if [ -L "$link_path" ] || [ -e "$link_path" ]; then
478
- rm -rf "$link_path"
507
+ if [ -L "$link_path" ]; then
508
+ rm "$link_path"
509
+ elif [ -e "$link_path" ]; then
510
+ echo "Error: '$link_path' already exists and is not a symlink. Move or remove it and rerun setup:doc-skill." >&2
511
+ exit 1
479
512
  fi
480
513
  ln -s "$target" "$link_path"
481
514
  }