create-zudo-doc 3.3.0 → 4.0.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/dist/prompts.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as p from "@clack/prompts";
2
- import { SINGLE_SCHEMES, FEATURES, SUPPORTED_LANGS } from "./constants.js";
2
+ import { SINGLE_SCHEMES, FEATURES, SUPPORTED_LANGS, THEME_PACKS } from "./constants.js";
3
3
  import { validateProjectName } from "./utils.js";
4
4
  export async function runPrompts(prefilled = {}) {
5
5
  // 1. Project name
@@ -124,6 +124,26 @@ export async function runPrompts(prefilled = {}) {
124
124
  respectPrefersColorScheme = respect;
125
125
  }
126
126
  }
127
+ // 3.5 Theme pack (ADR #2818 Decision 7) — placed between the color-scheme
128
+ // block above and the features multiselect below (locked spec, #2823).
129
+ let themePack;
130
+ if (prefilled.themePack) {
131
+ themePack = prefilled.themePack;
132
+ }
133
+ else {
134
+ const result = await p.select({
135
+ message: "Theme pack:",
136
+ options: THEME_PACKS.map((t) => ({
137
+ value: t.slug,
138
+ label: t.label,
139
+ hint: t.hint,
140
+ })),
141
+ initialValue: "default",
142
+ });
143
+ if (p.isCancel(result))
144
+ process.exit(0);
145
+ themePack = result;
146
+ }
127
147
  // 4. Features
128
148
  let features;
129
149
  if (prefilled.features) {
@@ -196,6 +216,7 @@ export async function runPrompts(prefilled = {}) {
196
216
  darkScheme,
197
217
  respectPrefersColorScheme,
198
218
  defaultMode,
219
+ themePack,
199
220
  features,
200
221
  explicitlyDisabledFeatures: prefilled.explicitlyDisabledFeatures,
201
222
  githubUrl,
@@ -18,5 +18,5 @@ export { getSecondaryLang };
18
18
  *
19
19
  * Bumped in lockstep by scripts/release-create-zudo-doc.sh.
20
20
  */
21
- export declare const ZUDO_DOC_PIN = "^3.3.0";
21
+ export declare const ZUDO_DOC_PIN = "^4.0.0";
22
22
  export declare function scaffold(choices: UserChoices): Promise<void>;
package/dist/scaffold.js CHANGED
@@ -24,7 +24,7 @@ export { getSecondaryLang };
24
24
  *
25
25
  * Bumped in lockstep by scripts/release-create-zudo-doc.sh.
26
26
  */
27
- export const ZUDO_DOC_PIN = "^3.3.0";
27
+ export const ZUDO_DOC_PIN = "^4.0.0";
28
28
  /**
29
29
  * Files in `templates/base/**` that must not be copied by the unconditional
30
30
  * base mirror. Each entry is matched against the path relative to
@@ -415,9 +415,9 @@ function generatePackageJson(choices) {
415
415
  // disabled, reproducible CSS-Modules scoped names (project-relative paths),
416
416
  // dev-mode git-restore detection, Tailwind temp-file cleanup, and a
417
417
  // near-miss `"use client"` directive scanner.
418
- // next.33 added the opt-in hierarchical heading-ID strategy
418
+ // next.33 added hierarchical heading IDs
419
419
  // (Takazudo/zudo-front-builder#871): `markdown.features.headingIds.strategy`.
420
- // The generated config + TOC builder use it via settings.headingIdStrategy.
420
+ // zudo-doc now uses that strategy unconditionally in its package preset.
421
421
  // next.35 fixes resolve_links rewriting bare same-page `[text](#anchor)` /
422
422
  // `[text](?query)` links to `/<parent-dir>/#anchor` (zudolab/zudo-doc#1948,
423
423
  // upstream Takazudo/zudo-front-builder#875).
@@ -447,23 +447,24 @@ function generatePackageJson(choices) {
447
447
  // extraWatchPaths rebuilds, and TS-config-loader path canonicalization
448
448
  // (Takazudo/zudo-front-builder#1036–#1043). next.45: docs-only. next.46:
449
449
  // opt-in dev boot-lazy mode (#1057) + client-router timer lifecycle fixes —
450
- // dev-server-only. next.47: dual light/dark syntect themes (themeLight/
450
+ // dev-server-only. Historical dual light/dark theme support (themeLight/
451
451
  // themeDark on CodeHighlightConfig, --shiki-light/--shiki-dark, #1067) plus
452
452
  // stricter build-start validation that rejects unknown theme names. next.48:
453
453
  // re-export @takazudo/zfb/config from the zfb-shim.d.ts type shim — type-only
454
454
  // fix, additive. next.49: client-router WebKit bfcache fix —
455
455
  // re-sync the history index + originalLocation on a bfcache restore so
456
456
  // browser Back after an SPA navigation returns to the previous page instead
457
- // of skipping an entry — runtime-only bug fix, additive. next.50 (current
458
- // pin): client-router fix to commit the SPA history entry BEFORE the View
457
+ // of skipping an entry — runtime-only bug fix, additive. next.50:
458
+ // client-router fix to commit the SPA history entry BEFORE the View
459
459
  // Transition, so on WebKit/iOS a single browser Back after an SPA navigation
460
460
  // creates a distinct history entry instead of falling off the site —
461
- // runtime-only bug fix, additive. next.51 (current pin): additive public-API
461
+ // runtime-only bug fix, additive. next.51: additive public-API
462
462
  // surface — VNode/VNodeArray/VNodeObject are now exported from
463
463
  // "@takazudo/zfb" (#972) — plus removal of the no-op linkValidation.allowExternal
464
- // knob (#925); both are non-breaking for a fresh scaffold. A fresh scaffold
465
- // sets codeHighlight.themeLight/themeDark for dual-theme syntect output. No
466
- // consumer-facing / CLI breaking change. next.52: adds the
464
+ // knob (#925); both are non-breaking for a fresh scaffold. Highlight config
465
+ // is package-preset-owned: fresh scaffolds delegate to zudoDoc(), whose
466
+ // preset selects class mode without emitting theme names project-side.
467
+ // next.52 adds the
467
468
  // `ClientRouter({ preserveHtmlAttrs })` option (zfb#1104) — consumers can
468
469
  // declare runtime `<html>` attribute names to preserve across SPA swaps so
469
470
  // e.g. `data-sidebar-hidden` / `data-theme` survive (zudolab/zudo-doc#2200).
@@ -501,14 +502,22 @@ function generatePackageJson(choices) {
501
502
  // next.76: routine toolchain bump from next.75, adopted in
502
503
  // lockstep with the root package.json pins. No consumer-facing / CLI change.
503
504
  // next.77: router persistence/history fixes and runtime island remount
504
- // support, adopted in lockstep with the root package.json pins. No
505
- // scaffold API change. next.78 (current pin): production HTML minification
506
- // support via minifyHtml, adopted as a zudo-doc default through settings.
507
- "@takazudo/zfb": "0.1.0-next.78",
508
- "@takazudo/zfb-runtime": "0.1.0-next.78",
505
+ // support. next.78 added production HTML minification. next.81 added the
506
+ // package-root semantic highlight API plus island resource delivery
507
+ // (zfb#1633/#1643). next.83 completes bundle.exclude dependency staging
508
+ // for package-owned overlay routes (zfb#1645/#1649); next.84 canonicalizes
509
+ // linked package-route identity so SSR shares one framework singleton
510
+ // (zfb#1650/#1651). next.85 also remaps absolute project imports from
511
+ // virtual host modules into the staged project graph, preserving that
512
+ // singleton across host-callable wiring (zfb#1652/#1653). The zfb family
513
+ // must stay in lockstep because the WASM browser entry depends on its
514
+ // resource-aware island pipeline.
515
+ "@takazudo/zfb": "0.1.0-next.87",
516
+ "@takazudo/zfb-runtime": "0.1.0-next.87",
509
517
  // zfb-adapter-cloudflare — required for any route with `prerender = false`.
510
518
  // Pinned in lockstep with @takazudo/zfb.
511
- "@takazudo/zfb-adapter-cloudflare": "0.1.0-next.78",
519
+ "@takazudo/zfb-adapter-cloudflare": "0.1.0-next.87",
520
+ "@takazudo/zfb-md-wasm": "0.1.0-next.87",
512
521
  // @takazudo/zudo-doc — published from this monorepo via
513
522
  // .github/workflows/publish-zudo-doc.yml. The pin here is bumped in
514
523
  // lockstep by scripts/release-create-zudo-doc.sh whenever zudo-doc's
@@ -543,6 +552,7 @@ function generatePackageJson(choices) {
543
552
  // compiles. Same pin as host. Caught by W6B (#1735) consumer-build
544
553
  // verification.
545
554
  "preact-render-to-string": "^6.6.6",
555
+ // Retained until #2742 atomically moves HTML Preview to zfb-md-wasm.
546
556
  shiki: "^4.0.2",
547
557
  "@shikijs/transformers": "^4.0.0",
548
558
  "gray-matter": "^4.0.0",
@@ -587,7 +597,7 @@ function generatePackageJson(choices) {
587
597
  // same reason. This is the ACCEPTED, permanent contract per #2668 — see
588
598
  // the "@takazudo/zdtp dep implication" note in
589
599
  // packages/zudo-doc/docs/adr/route-injection-seam.md.
590
- "@takazudo/zdtp": "0.4.6",
600
+ "@takazudo/zdtp": "0.4.9",
591
601
  };
592
602
  const devDeps = {
593
603
  "@tailwindcss/vite": "^4.2.0",
@@ -608,11 +618,11 @@ function generatePackageJson(choices) {
608
618
  // required regardless of this feature flag.)
609
619
  // @takazudo/zudo-doc has @takazudo/zudo-doc-history-server as an optional
610
620
  // peer dep. When docHistory is selected the zfb plugin
611
- // (@takazudo/zudo-doc/plugins/doc-history) eagerly imports
612
- // @takazudo/zudo-doc/integrations/doc-history which in turn imports
621
+ // (@takazudo/zudo-doc/plugins/doc-history) eagerly imports its internal
622
+ // doc-history helpers, which in turn import
613
623
  // @takazudo/zudo-doc-history-server/git-history. Without this dep the
614
624
  // plugin host fails at init with ERR_MODULE_NOT_FOUND — W8A (#1739).
615
- deps["@takazudo/zudo-doc-history-server"] = "^3.3.0";
625
+ deps["@takazudo/zudo-doc-history-server"] = "^4.0.0";
616
626
  // tsx is no longer needed here: the relocated package plugin imports the
617
627
  // runner directly (no `tsx -e` spawn) since the package ships compiled
618
628
  // dist/ — package-first migration #2321 (#2337).
@@ -620,17 +630,6 @@ function generatePackageJson(choices) {
620
630
  // claudeResources: tsx is no longer needed. The relocated package plugin
621
631
  // (@takazudo/zudo-doc/plugins/claude-resources) imports the runner directly
622
632
  // since the package ships compiled dist/ — package-first migration #2321 (#2337).
623
- if (choices.features.includes("tagGovernance")) {
624
- // gray-matter is already in `deps` unconditionally (base template uses it),
625
- // so we only add the tooling deps specific to tags:audit / tags:suggest.
626
- devDeps["string-similarity"] = "^4.0.4";
627
- devDeps["@types/string-similarity"] = "^4.0.2";
628
- devDeps["pluralize"] = "^8.0.0";
629
- devDeps["@types/pluralize"] = "^0.0.33";
630
- devDeps["picocolors"] = "^1.1.1";
631
- devDeps["@inquirer/prompts"] = "^8.4.2";
632
- devDeps["tsx"] = "^4.21.0";
633
- }
634
633
  // check:html and gen:z-index/check:z-index are DROPPED from the default
635
634
  // scaffold (locked decision, epic #2651 #2660 work item 6):
636
635
  // - `.htmlvalidate.json` no longer ships — html-validate becomes an
@@ -646,11 +645,12 @@ function generatePackageJson(choices) {
646
645
  check: "zfb check",
647
646
  };
648
647
  if (choices.features.includes("tagGovernance")) {
649
- // tags-audit bin is provided by @takazudo/zudo-doc (S9b #2334);
650
- // tsx is still required as a devDep because the bin's runner imports
651
- // the project's TypeScript config files at runtime via tsx.
652
- scripts["tags:audit"] = "tags-audit";
653
- scripts["tags:suggest"] = "tsx scripts/tags-suggest.ts";
648
+ // Both package-owned bins load the same explicit project config. `--`
649
+ // supplied by pnpm/npm is preserved by the runners for forwarded options.
650
+ scripts["tags:audit"] =
651
+ "tags-audit --config src/config/tag-vocabulary.ts";
652
+ scripts["tags:suggest"] =
653
+ "tags-suggest --config src/config/tag-vocabulary.ts";
654
654
  }
655
655
  if (choices.features.includes("skillSymlinker")) {
656
656
  scripts["setup:doc-skill"] = "bash scripts/setup-doc-skill.sh";
@@ -32,6 +32,9 @@ export const DEFAULT_MIRROR = {
32
32
  darkScheme: "Default Dark",
33
33
  respectPrefersColorScheme: true,
34
34
  },
35
+ themePack: "default",
36
+ themePackSwitcher: false,
37
+ themePacks: undefined,
35
38
  siteName: "Docs",
36
39
  minifyHtml: true,
37
40
  defaultLocale: "en",
@@ -152,6 +155,12 @@ function buildDesiredConfig(choices) {
152
155
  respectPrefersColorScheme: choices.respectPrefersColorScheme ?? true,
153
156
  };
154
157
  }
158
+ // ── Theme pack (ADR #2818 Decision 7) ────────────────────────────────
159
+ // themePacks (the enabled-slugs allowlist) is intentionally NOT set here —
160
+ // it is an advanced, hand-edited-only field with no CLI/prompt surface
161
+ // (locked spec, #2823).
162
+ desired.themePack = choices.themePack ?? "default";
163
+ desired.themePackSwitcher = choices.features.includes("themePackSwitcher");
155
164
  // ── i18n ──────────────────────────────────────────────────────────────
156
165
  desired.defaultLocale = choices.defaultLang ?? "en";
157
166
  if (choices.features.includes("i18n")) {
@@ -169,7 +178,7 @@ function buildDesiredConfig(choices) {
169
178
  // ── Misc site fields ──────────────────────────────────────────────────
170
179
  desired.minifyHtml = choices.minifyHtml ?? true;
171
180
  desired.noindex = choices.features.includes("noindex");
172
- const rawGithubUrl = (choices.githubUrl ?? "").trim();
181
+ const rawGithubUrl = typeof choices.githubUrl === "string" ? choices.githubUrl.trim() : "";
173
182
  desired.githubUrl = rawGithubUrl ? rawGithubUrl : false;
174
183
  desired.cjkFriendly = choices.cjkFriendly ?? false;
175
184
  // ── Meta tags ─────────────────────────────────────────────────────────
@@ -194,12 +203,11 @@ function buildDesiredConfig(choices) {
194
203
  // ── Tags / docs ───────────────────────────────────────────────────────
195
204
  desired.docTags = choices.features.includes("docTags");
196
205
  if (choices.features.includes("tagGovernance")) {
197
- desired.tagGovernance = "warn";
198
- desired.tagVocabulary = true;
199
- // Wired to the src/config/tag-vocabulary.ts starter file the
200
- // tagGovernance feature module emits (single source of truth shared
201
- // with scripts/tags-audit.ts and scripts/tags-suggest.ts).
202
- desired.tagVocabularyEntries = raw("tagVocabulary");
206
+ // The explicit tag CLI config is also the zfb source of truth, so the
207
+ // package-owned bins and runtime settings cannot drift.
208
+ desired.tagGovernance = raw("tagCliConfig.governance");
209
+ desired.tagVocabulary = raw("tagCliConfig.vocabularyActive");
210
+ desired.tagVocabularyEntries = raw("tagCliConfig.vocabulary");
203
211
  }
204
212
  else {
205
213
  desired.tagGovernance = "off";
@@ -295,11 +303,8 @@ function buildDesiredConfig(choices) {
295
303
  }
296
304
  desired.headerNav = headerNav;
297
305
  if (choices.headerRightItems !== undefined) {
298
- // User-supplied override (including empty array) — emit verbatim, minus
299
- // a defensive strip of "design-token-panel" when the feature is off.
300
- desired.headerRightItems = choices.headerRightItems.filter((item) => !(item.type === "trigger" &&
301
- item.trigger === "design-token-panel" &&
302
- !choices.features.includes("designTokenPanel")));
306
+ // User-supplied override (including empty array) — emit verbatim.
307
+ desired.headerRightItems = choices.headerRightItems;
303
308
  }
304
309
  else {
305
310
  const items = [];
@@ -309,7 +314,9 @@ function buildDesiredConfig(choices) {
309
314
  if (choices.features.includes("versioning")) {
310
315
  items.push({ type: "component", component: "version-switcher" });
311
316
  }
312
- items.push({ type: "component", component: "github-link" });
317
+ if (rawGithubUrl) {
318
+ items.push({ type: "component", component: "github-link" });
319
+ }
313
320
  items.push({ type: "component", component: "theme-toggle" });
314
321
  if (choices.features.includes("search")) {
315
322
  items.push({ type: "component", component: "search" });
@@ -332,6 +339,9 @@ function buildDesiredConfig(choices) {
332
339
  const FIELD_ORDER = [
333
340
  "colorScheme",
334
341
  "colorMode",
342
+ "themePack",
343
+ "themePackSwitcher",
344
+ "themePacks",
335
345
  "siteName",
336
346
  "defaultLocale",
337
347
  "locales",
@@ -395,7 +405,7 @@ export function generateZfbConfig(choices) {
395
405
  lines.push(`import { defineConfig } from "zfb/config";`);
396
406
  lines.push(`import { zudoDoc } from "@takazudo/zudo-doc/config";`);
397
407
  if (choices.features.includes("tagGovernance")) {
398
- lines.push(`import { tagVocabulary } from "./src/config/tag-vocabulary";`);
408
+ lines.push(`import tagCliConfig from "./src/config/tag-vocabulary";`);
399
409
  }
400
410
  lines.push(``);
401
411
  lines.push(`export default defineConfig(`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-zudo-doc",
3
- "version": "3.3.0",
3
+ "version": "4.0.0",
4
4
  "description": "Create a new zudo-doc documentation site",
5
5
  "license": "MIT",
6
6
  "author": "Takeshi Takatsudo",
@@ -5,19 +5,23 @@
5
5
  // `zfb dev` (real pre-existing gap in zfb's dev-mode dynamic-route rendering,
6
6
  // distinct from the `/`-injection gap zfb#1227; empirically confirmed on
7
7
  // #2653). This stub reconstructs the doc route from scratch using ONLY the
8
- // three sanctioned package entrypoints — no `pages/lib`, no `@/config`:
8
+ // sanctioned package entrypoints — no `pages/lib`, no `@/config`:
9
9
  // 1. the `virtual:zudo-doc-route-context` virtual module (serializable
10
10
  // settings/translations/tagVocabulary/colorSchemes payload),
11
- // 2. `@takazudo/zudo-doc/route-context` (`createRouteContext`), and
12
- // 3. `@takazudo/zudo-doc/chrome` (`createChrome`).
11
+ // 2. `@takazudo/zudo-doc/route-context` (`createRouteContext`),
12
+ // 3. `@takazudo/zudo-doc/chrome` (`createChrome`), and
13
+ // 4. `virtual:zudo-doc-chrome-bindings` (the host-callables channel).
14
+ // The bindings import is unconditional: the routes plugin supplies an empty
15
+ // object when `chromeBindingsModule` is unset, while configured projects get
16
+ // their MDX/chrome bindings without editing this stub.
13
17
  // Makes `/docs/getting-started/` return 200 in BOTH `zfb dev` and `zfb build`
14
18
  // (see the "TM negative guard" case in route-injection-build.slow.test.ts for
15
19
  // the no-stub 404 proof this fixes).
16
20
  //
17
21
  // docHistory note: when the docHistory feature is selected, the generator
18
22
  // patches this file to statically import DocHistory from
19
- // "@takazudo/zudo-doc/doc-history" and pass it to createChrome's hostBindings
20
- // (second) argument via defineChromeBindings({ DocHistory })
23
+ // "@takazudo/zudo-doc/doc-history" and merge it over chromeBindings in
24
+ // createChrome's hostBindings (second) argument —
21
25
  // DocHistory's chrome-derive default is a no-op stub (unlike
22
26
  // DesignTokenPanelBootstrap, which the package auto-defaults), so without
23
27
  // that patch the doc-history button never hydrates on this route.
@@ -29,10 +33,11 @@ import {
29
33
  type RouteContextPayload,
30
34
  } from "@takazudo/zudo-doc/route-context";
31
35
  import { createChrome } from "@takazudo/zudo-doc/chrome";
36
+ import { chromeBindings } from "virtual:zudo-doc-chrome-bindings";
32
37
 
33
38
  const ctx = routeContext as unknown as RouteContextPayload;
34
39
  const routeCtx = createRouteContext(ctx);
35
- const { renderDocPage } = createChrome(routeCtx);
40
+ const { renderDocPage } = createChrome(routeCtx, chromeBindings);
36
41
 
37
42
  export const frontmatter = { title: "Docs" };
38
43
 
@@ -2,8 +2,11 @@
2
2
  /** @jsxImportSource preact */
3
3
  // Locked manifest (#2653 Decision 4, i18n addendum): the locale-prefixed
4
4
  // counterpart of pages/docs/[[...slug]].tsx — required for the same reason
5
- // (injected DYNAMIC routes 404 in `zfb dev`). Self-contained: only the three
6
- // sanctioned package entrypoints — no `pages/lib`, no `@/config`. Mirrors
5
+ // (injected DYNAMIC routes 404 in `zfb dev`). Self-contained: only the
6
+ // sanctioned package entrypoints — no `pages/lib`, no `@/config`. The
7
+ // `virtual:zudo-doc-chrome-bindings` import is unconditional, just like the
8
+ // default-locale stub: the routes plugin supplies `{}` when no host module is
9
+ // configured. Mirrors
7
10
  // the package's own `routes/locale-docs-slug.tsx` shape, rebuilt from the
8
11
  // route-context payload instead of the package-internal `_context.js`.
9
12
  //
@@ -28,10 +31,11 @@ import {
28
31
  type RouteContextPayload,
29
32
  } from "@takazudo/zudo-doc/route-context";
30
33
  import { createChrome } from "@takazudo/zudo-doc/chrome";
34
+ import { chromeBindings } from "virtual:zudo-doc-chrome-bindings";
31
35
 
32
36
  const ctx = routeContext as unknown as RouteContextPayload;
33
37
  const routeCtx = createRouteContext(ctx);
34
- const { renderDocPage } = createChrome(routeCtx);
38
+ const { renderDocPage } = createChrome(routeCtx, chromeBindings);
35
39
 
36
40
  export const frontmatter = { title: "Docs" };
37
41
 
@@ -59,7 +63,7 @@ export function paths(): Array<{
59
63
  result.push({
60
64
  params: { locale, slug: item.slugParams },
61
65
  props: {
62
- ...(item.props as Record<string, unknown>),
66
+ ...(item.props as unknown as Record<string, unknown>),
63
67
  // Fallback pages exist only in the default locale, so their
64
68
  // doc-history lives under the EN docsDir; translated pages read
65
69
  // their own locale dir.
@@ -1,131 +0,0 @@
1
- #!/usr/bin/env -S tsx
2
- /**
3
- * scripts/tags-audit.ts
4
- *
5
- * Thin re-export shim — the audit logic lives in @takazudo/zudo-doc/tags-audit
6
- * (S9b #2334). This file exists for backward-compat with any project-side
7
- * imports of these functions (e.g. custom scripts that import helpers from here).
8
- *
9
- * The `tags:audit` package.json script does not invoke this file directly;
10
- * it uses the `tags-audit` package bin from @takazudo/zudo-doc instead.
11
- *
12
- * CLI usage (still works via tsx for debugging):
13
- * tsx scripts/tags-audit.ts [--fix] [--ci] [--json]
14
- */
15
-
16
- import { join, resolve } from "node:path";
17
- import { fileURLToPath } from "node:url";
18
-
19
- import pc from "picocolors";
20
- import pluralize from "pluralize";
21
- import stringSimilarity from "string-similarity";
22
-
23
- import { settings } from "../src/config/settings";
24
- import { tagVocabulary } from "../src/config/tag-vocabulary";
25
-
26
- // Re-export everything from the package core so existing callers continue
27
- // to work unchanged.
28
- export {
29
- audit,
30
- applyFixes,
31
- buildIndex,
32
- collectMdxFiles,
33
- computeRewrites,
34
- formatTextReport,
35
- hasHardIssues,
36
- normalizeTags,
37
- rewriteAliasesByteStable,
38
- } from "@takazudo/zudo-doc/tags-audit";
39
- export type {
40
- AliasIssue,
41
- AuditOptions,
42
- AuditReport,
43
- DeprecatedIssue,
44
- NearDuplicatePair,
45
- NearDupHelpers,
46
- TagVocabularyEntry,
47
- UnknownIssue,
48
- } from "@takazudo/zudo-doc/tags-audit";
49
-
50
- // Backward-compat: expose findNearDuplicates with project helpers pre-bound.
51
- import { findNearDuplicates as _findNearDuplicates } from "@takazudo/zudo-doc/tags-audit";
52
- export function findNearDuplicates(
53
- tags: string[],
54
- ): import("@takazudo/zudo-doc/tags-audit").NearDuplicatePair[] {
55
- return _findNearDuplicates(tags, {
56
- singular: pluralize.singular,
57
- compareTwoStrings: stringSimilarity.compareTwoStrings,
58
- });
59
- }
60
-
61
- // ── CLI runner (legacy — package bin is preferred) ─────────────────────────
62
-
63
- const __filename = fileURLToPath(import.meta.url);
64
- const isMain =
65
- process.argv[1] !== undefined &&
66
- resolve(process.argv[1]) === resolve(__filename);
67
-
68
- if (isMain) {
69
- const { audit: coreAudit, computeRewrites: coreComputeRewrites, applyFixes: coreApplyFixes, hasHardIssues: coreHasHardIssues, formatTextReport: coreFormatReport } =
70
- await import("@takazudo/zudo-doc/tags-audit");
71
-
72
- const argv = process.argv.slice(2);
73
- const flags = {
74
- fix: argv.includes("--fix"),
75
- ci: argv.includes("--ci"),
76
- json: argv.includes("--json"),
77
- };
78
-
79
- const ROOT_DIR = resolve(__filename, "..", "..");
80
- const docsDir = join(ROOT_DIR, settings.docsDir);
81
- const localeDirs = Object.values(settings.locales ?? {}).map((l) =>
82
- join(ROOT_DIR, l.dir),
83
- );
84
- const contentDirs = [docsDir, ...localeDirs];
85
- const vocabularyActive =
86
- Boolean(settings.tagVocabulary) && settings.tagGovernance !== "off";
87
-
88
- if (flags.fix) {
89
- const rewrites = coreComputeRewrites(tagVocabulary);
90
- const touched = await coreApplyFixes(contentDirs, rewrites, ROOT_DIR);
91
- if (flags.json) {
92
- process.stdout.write(JSON.stringify({ fixed: touched }, null, 2) + "\n");
93
- } else if (touched.length === 0) {
94
- console.log(pc.green("✓ No alias rewrites needed"));
95
- } else {
96
- console.log(pc.green(`✓ Rewrote aliases in ${touched.length} file(s):`));
97
- for (const f of touched) console.log(` ${f}`);
98
- }
99
- process.exit(0);
100
- }
101
-
102
- const report = await coreAudit({
103
- rootDir: ROOT_DIR,
104
- contentDirs,
105
- vocabulary: tagVocabulary,
106
- governance: settings.tagGovernance,
107
- vocabularyActive,
108
- nearDupHelpers: {
109
- singular: pluralize.singular,
110
- compareTwoStrings: stringSimilarity.compareTwoStrings,
111
- },
112
- });
113
-
114
- if (flags.json) {
115
- process.stdout.write(JSON.stringify(report, null, 2) + "\n");
116
- } else {
117
- console.log(coreFormatReport(report, settings.tagGovernance, vocabularyActive, pc));
118
- }
119
-
120
- const hardIssues = coreHasHardIssues(report);
121
- if (hardIssues && (flags.ci || settings.tagGovernance === "strict")) {
122
- process.exit(1);
123
- }
124
- if (hardIssues) {
125
- console.error(
126
- pc.yellow(
127
- "Note: tag issues found but running in non-strict mode (exit 0). Use --ci to fail.",
128
- ),
129
- );
130
- }
131
- }