@takazudo/zudo-doc 0.2.0-next.6 → 0.2.0-next.8
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/breadcrumb/breadcrumb.js +2 -1
- package/dist/html-preview-wrapper/dedent.js +1 -1
- package/dist/html-preview-wrapper/preview-base.js +1 -1
- package/dist/integrations/claude-resources/escape-for-mdx.js +1 -1
- package/dist/integrations/claude-resources/generate.js +41 -15
- package/dist/integrations/doc-history/index.d.ts +35 -10
- package/dist/integrations/doc-history/index.js +26 -3
- package/dist/integrations/doc-history/pre-build.d.ts +7 -1
- package/dist/integrations/doc-history/pre-build.js +62 -13
- package/dist/integrations/llms-txt/dev-middleware.js +1 -1
- package/dist/integrations/llms-txt/load.d.ts +3 -1
- package/dist/integrations/llms-txt/load.js +3 -1
- package/dist/integrations/llms-txt/types.d.ts +6 -0
- package/dist/integrations/search-index/content-files.d.ts +11 -1
- package/dist/integrations/search-index/content-files.js +1 -1
- package/dist/safelist.css +2 -0
- package/dist/sidebar-tree/build-tree.js +6 -4
- package/dist/sidebar-tree/types.d.ts +14 -0
- package/dist/toc/smart-break.js +1 -1
- package/package.json +9 -7
|
@@ -6,6 +6,7 @@ function buildBreadcrumbItems(tree, currentId, homeHref) {
|
|
|
6
6
|
const path = findPath(tree, currentId);
|
|
7
7
|
for (let i = 0; i < path.length; i++) {
|
|
8
8
|
const node = path[i];
|
|
9
|
+
if (!node) continue;
|
|
9
10
|
const isLast = i === path.length - 1;
|
|
10
11
|
items.push({
|
|
11
12
|
label: node.label,
|
|
@@ -20,7 +21,7 @@ function SmartLabel({ label }) {
|
|
|
20
21
|
const nodes = [];
|
|
21
22
|
for (let i = 0; i < parts.length; i++) {
|
|
22
23
|
const part = parts[i];
|
|
23
|
-
if (part
|
|
24
|
+
if (!part) continue;
|
|
24
25
|
nodes.push(part);
|
|
25
26
|
if (i % 2 === 1) nodes.push(/* @__PURE__ */ jsx("wbr", {}, `wbr-${i}`));
|
|
26
27
|
}
|
|
@@ -3,7 +3,7 @@ function dedent(text) {
|
|
|
3
3
|
let minIndent = Infinity;
|
|
4
4
|
for (const line of lines) {
|
|
5
5
|
if (line.trim().length === 0) continue;
|
|
6
|
-
const indent = line.match(/^(\s*)/)?.[1]
|
|
6
|
+
const indent = line.match(/^(\s*)/)?.[1]?.length ?? 0;
|
|
7
7
|
if (indent < minIndent) minIndent = indent;
|
|
8
8
|
}
|
|
9
9
|
if (minIndent === 0 || minIndent === Infinity) {
|
|
@@ -53,7 +53,7 @@ function PreviewBase({
|
|
|
53
53
|
const id = setTimeout(syncHeight, 150);
|
|
54
54
|
return () => clearTimeout(id);
|
|
55
55
|
}, [activeViewport, syncHeight, height]);
|
|
56
|
-
const containerWidth = VIEWPORTS[activeViewport]
|
|
56
|
+
const containerWidth = VIEWPORTS[activeViewport]?.width ?? "100%";
|
|
57
57
|
return /* @__PURE__ */ jsxs("div", { class: "border border-muted rounded-lg overflow-hidden my-vsp-md", children: [
|
|
58
58
|
/* @__PURE__ */ jsxs("div", { class: "flex items-center justify-between px-hsp-md py-hsp-sm bg-surface border-b border-muted gap-hsp-sm flex-wrap", children: [
|
|
59
59
|
title && /* @__PURE__ */ jsx("span", { class: "text-caption font-semibold text-fg", children: title }),
|
|
@@ -121,7 +121,7 @@ function escapeForMdx(content) {
|
|
|
121
121
|
).replace(/<(-+|=+)/g, "<$1").replace(/<(\d)/g, "<$1").replace(/\{/g, "{").replace(/\}/g, "}");
|
|
122
122
|
escaped = escaped.replace(
|
|
123
123
|
new RegExp(`${inlinePlaceholder}(\\d+)\0`, "g"),
|
|
124
|
-
(_, idx) => inlineCodes[Number(idx)]
|
|
124
|
+
(_, idx) => inlineCodes[Number(idx)] ?? ""
|
|
125
125
|
);
|
|
126
126
|
return escaped;
|
|
127
127
|
}).join("");
|
|
@@ -25,13 +25,16 @@ function listFiles(dir) {
|
|
|
25
25
|
if (!fs.existsSync(dir)) return [];
|
|
26
26
|
return fs.readdirSync(dir, { withFileTypes: true }).filter((d) => d.isFile()).map((d) => d.name).sort();
|
|
27
27
|
}
|
|
28
|
-
function
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
function writeCategoryIndex(outputDir, label, position, description) {
|
|
29
|
+
const mdx = `---
|
|
30
|
+
title: "${escapeTitle(label)}"
|
|
31
|
+
description: "${escapeTitle(description)}"
|
|
32
|
+
sidebar_position: ${position}
|
|
33
|
+
category_no_page: true
|
|
34
|
+
generated: true
|
|
35
|
+
---
|
|
36
|
+
`;
|
|
37
|
+
fs.writeFileSync(path.join(outputDir, "index.mdx"), mdx);
|
|
35
38
|
}
|
|
36
39
|
function findClaudeMdFiles(dir, excludeDirs) {
|
|
37
40
|
const results = [];
|
|
@@ -89,6 +92,11 @@ function generateClaudemdDocs(config) {
|
|
|
89
92
|
});
|
|
90
93
|
const emittedSlugs = /* @__PURE__ */ new Map();
|
|
91
94
|
items.forEach((item, index) => {
|
|
95
|
+
if (item.slug === "index") {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`claude-resources: "${item.relPath}" maps to the reserved slug "index", which is used for the category metadata file. Rename the directory to resolve the conflict.`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
92
100
|
const previous = emittedSlugs.get(item.slug);
|
|
93
101
|
if (previous !== void 0) {
|
|
94
102
|
throw new Error(
|
|
@@ -111,7 +119,7 @@ ${escapeForMdx(content.trim())}
|
|
|
111
119
|
`;
|
|
112
120
|
fs.writeFileSync(path.join(outputDir, `${item.slug}.mdx`), mdx);
|
|
113
121
|
});
|
|
114
|
-
|
|
122
|
+
writeCategoryIndex(outputDir, "CLAUDE.md", 900, "Project-specific instructions");
|
|
115
123
|
return items;
|
|
116
124
|
}
|
|
117
125
|
function generateCommandsDocs(config) {
|
|
@@ -128,6 +136,11 @@ function generateCommandsDocs(config) {
|
|
|
128
136
|
const parsed = parseFrontmatter(content);
|
|
129
137
|
if (!parsed) continue;
|
|
130
138
|
const name = file.replace(/\.md$/, "");
|
|
139
|
+
if (name === "index") {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`claude-resources: ".claude/commands/index.md" uses the reserved name "index", which is used for the category metadata file. Rename the command file to resolve the conflict.`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
131
144
|
const description = parsed.data.description || "";
|
|
132
145
|
items.push({ name, description });
|
|
133
146
|
const mdx = `---
|
|
@@ -142,7 +155,7 @@ ${escapeForMdx(parsed.content.trim())}
|
|
|
142
155
|
fs.writeFileSync(path.join(outputDir, `${name}.mdx`), mdx);
|
|
143
156
|
}
|
|
144
157
|
items.sort((a, b) => a.name.localeCompare(b.name));
|
|
145
|
-
|
|
158
|
+
writeCategoryIndex(outputDir, "Commands", 901, "Custom slash commands");
|
|
146
159
|
return items;
|
|
147
160
|
}
|
|
148
161
|
function getSkillFileTree(skillDir, subDirs) {
|
|
@@ -153,6 +166,7 @@ function getSkillFileTree(skillDir, subDirs) {
|
|
|
153
166
|
}
|
|
154
167
|
for (let i = 0; i < entries.length; i++) {
|
|
155
168
|
const entry = entries[i];
|
|
169
|
+
if (!entry) continue;
|
|
156
170
|
const isLast = i === entries.length - 1;
|
|
157
171
|
const prefix = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
158
172
|
if (!entry.isDir) {
|
|
@@ -161,6 +175,7 @@ function getSkillFileTree(skillDir, subDirs) {
|
|
|
161
175
|
lines.push(`${prefix}${entry.name}/`);
|
|
162
176
|
for (let j = 0; j < entry.children.length; j++) {
|
|
163
177
|
const child = entry.children[j];
|
|
178
|
+
if (!child) continue;
|
|
164
179
|
const childIsLast = j === entry.children.length - 1;
|
|
165
180
|
const continuation = isLast ? " " : "\u2502 ";
|
|
166
181
|
const childPrefix = childIsLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
@@ -173,7 +188,8 @@ function getSkillFileTree(skillDir, subDirs) {
|
|
|
173
188
|
function getScriptDescription(filePath) {
|
|
174
189
|
try {
|
|
175
190
|
const topLines = fs.readFileSync(filePath, "utf8").split("\n", 2);
|
|
176
|
-
const
|
|
191
|
+
const firstLine = topLines[0] ?? "";
|
|
192
|
+
const commentLine = firstLine.startsWith("#!") ? topLines[1] ?? "" : firstLine;
|
|
177
193
|
const match = commentLine.match(/^(?:#|\/\/)\s*(.+)/);
|
|
178
194
|
return match ? ` \u2014 ${match[1]}` : "";
|
|
179
195
|
} catch {
|
|
@@ -187,7 +203,7 @@ function getSkillReferences(skillsDir, skillDir) {
|
|
|
187
203
|
const content = fs.readFileSync(path.join(refsDir, f), "utf8");
|
|
188
204
|
const name = f.replace(/\.md$/, "");
|
|
189
205
|
const h1Match = content.match(/^#\s+(.+)$/m);
|
|
190
|
-
const title = h1Match
|
|
206
|
+
const title = h1Match?.[1] ?? name;
|
|
191
207
|
return { name, title, content };
|
|
192
208
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
193
209
|
}
|
|
@@ -204,6 +220,11 @@ function generateSkillsDocs(config) {
|
|
|
204
220
|
ensureDir(outputDir);
|
|
205
221
|
const items = [];
|
|
206
222
|
for (const dir of dirs) {
|
|
223
|
+
if (dir === "index") {
|
|
224
|
+
throw new Error(
|
|
225
|
+
`claude-resources: skill directory ".claude/skills/index/" uses the reserved name "index", which is used for the category metadata file. Rename the skill directory to resolve the conflict.`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
207
228
|
const content = fs.readFileSync(
|
|
208
229
|
path.join(skillsDir, dir, "SKILL.md"),
|
|
209
230
|
"utf8"
|
|
@@ -283,7 +304,7 @@ ${escapeForMdx(ref.content.trim())}
|
|
|
283
304
|
"utf8"
|
|
284
305
|
);
|
|
285
306
|
const h1Match = raw.match(/^#\s+(.+)$/m);
|
|
286
|
-
const title = h1Match
|
|
307
|
+
const title = h1Match?.[1] ?? slug;
|
|
287
308
|
fs.writeFileSync(
|
|
288
309
|
path.join(outputDir, `${dir}--script-${slug}.mdx`),
|
|
289
310
|
`---
|
|
@@ -305,7 +326,7 @@ ${escapeForMdx(raw.trim())}
|
|
|
305
326
|
"utf8"
|
|
306
327
|
);
|
|
307
328
|
const h1Match = raw.match(/^#\s+(.+)$/m);
|
|
308
|
-
const title = h1Match
|
|
329
|
+
const title = h1Match?.[1] ?? slug;
|
|
309
330
|
fs.writeFileSync(
|
|
310
331
|
path.join(outputDir, `${dir}--asset-${slug}.mdx`),
|
|
311
332
|
`---
|
|
@@ -321,7 +342,7 @@ ${escapeForMdx(raw.trim())}
|
|
|
321
342
|
}
|
|
322
343
|
}
|
|
323
344
|
items.sort((a, b) => a.name.localeCompare(b.name));
|
|
324
|
-
|
|
345
|
+
writeCategoryIndex(outputDir, "Skills", 902, "Skill packages");
|
|
325
346
|
return items;
|
|
326
347
|
}
|
|
327
348
|
function generateAgentsDocs(config) {
|
|
@@ -341,6 +362,11 @@ function generateAgentsDocs(config) {
|
|
|
341
362
|
const description = parsed.data.description || "";
|
|
342
363
|
const model = parsed.data.model || "";
|
|
343
364
|
const fileSlug = file.replace(/\.md$/, "");
|
|
365
|
+
if (fileSlug === "index") {
|
|
366
|
+
throw new Error(
|
|
367
|
+
`claude-resources: ".claude/agents/index.md" uses the reserved name "index", which is used for the category metadata file. Rename the agent file to resolve the conflict.`
|
|
368
|
+
);
|
|
369
|
+
}
|
|
344
370
|
items.push({ name, file: fileSlug, description, model });
|
|
345
371
|
const modelBadge = model ? `**Model:** \`${model}\`
|
|
346
372
|
` : "";
|
|
@@ -357,7 +383,7 @@ ${escapeForMdx(parsed.content.trim())}
|
|
|
357
383
|
fs.writeFileSync(path.join(outputDir, `${fileSlug}.mdx`), mdx);
|
|
358
384
|
}
|
|
359
385
|
items.sort((a, b) => a.name.localeCompare(b.name));
|
|
360
|
-
|
|
386
|
+
writeCategoryIndex(outputDir, "Agents", 903, "Custom subagents");
|
|
361
387
|
return items;
|
|
362
388
|
}
|
|
363
389
|
function generateOverviewIndex(config, {
|
|
@@ -87,21 +87,46 @@ interface PostBuildContext {
|
|
|
87
87
|
/** Optional logger; falls back to silent no-ops when omitted. */
|
|
88
88
|
logger?: PostBuildLogger;
|
|
89
89
|
}
|
|
90
|
+
/** Env var that opts a LOCAL build back into postBuild per-page JSON generation. */
|
|
91
|
+
declare const DOC_HISTORY_GEN_ENV = "GEN_DOC_HISTORY";
|
|
92
|
+
/**
|
|
93
|
+
* Decide whether the postBuild hook should generate per-page doc-history JSON.
|
|
94
|
+
*
|
|
95
|
+
* The per-page JSON is redundant on the normal paths: dev reads it live from
|
|
96
|
+
* the standalone `:4322` server, and CI generates it in the dedicated parallel
|
|
97
|
+
* `build-history` job (the CLI directly, NOT this hook). Running it inline
|
|
98
|
+
* during a plain local `pnpm build` issues ~3 serial `git log --follow` calls
|
|
99
|
+
* per content file, which on a large corpus exceeds zfb's 120s postBuild
|
|
100
|
+
* lifecycle-hook budget (#1986). So the default flips to opt-in:
|
|
101
|
+
*
|
|
102
|
+
* - `SKIP_DOC_HISTORY=1` → never generate (highest priority; back-compat).
|
|
103
|
+
* - `GEN_DOC_HISTORY=1` → always generate (explicit local opt-in).
|
|
104
|
+
* - CI → generate (keeps the CI build-site artifact
|
|
105
|
+
* byte-identical to before; D1's async generator
|
|
106
|
+
* keeps it within budget).
|
|
107
|
+
* - otherwise (local) → skip (the #1986 fix).
|
|
108
|
+
*
|
|
109
|
+
* This gates ONLY the postBuild per-page dropdown JSON. The preBuild meta step
|
|
110
|
+
* (the visible Created/Updated/Author block) is unaffected — it keys off
|
|
111
|
+
* `SKIP_DOC_HISTORY` alone — so a plain local build still shows real page
|
|
112
|
+
* metadata; only the per-page history dropdown JSON is absent until opt-in.
|
|
113
|
+
*/
|
|
114
|
+
declare function shouldGeneratePostBuild(env?: NodeJS.ProcessEnv): {
|
|
115
|
+
generate: boolean;
|
|
116
|
+
reason: string;
|
|
117
|
+
};
|
|
90
118
|
/**
|
|
91
119
|
* Post-build hook. Spawns the `doc-history-generate` CLI from
|
|
92
120
|
* `@takazudo/zudo-doc-history-server` to write per-page git history JSON
|
|
93
121
|
* files into `<outDir>/doc-history/`.
|
|
94
122
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* runs leave the flag unset so the inline path is taken.
|
|
123
|
+
* Generation is gated by `shouldGeneratePostBuild` (see its docs): skipped by
|
|
124
|
+
* default on local builds (opt in with `GEN_DOC_HISTORY=1`), run in CI and
|
|
125
|
+
* when explicitly opted in, and always suppressed by `SKIP_DOC_HISTORY=1`.
|
|
99
126
|
*
|
|
100
|
-
* The CLI is spawned
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* progress and warnings surface in the user's terminal exactly as
|
|
104
|
-
* they do in CI.
|
|
127
|
+
* The CLI is spawned as `node <cli> <args>` (shell: false) so option-derived
|
|
128
|
+
* paths are never interpolated into a command line. Output is inherited so
|
|
129
|
+
* progress and warnings surface in the user's terminal exactly as in CI.
|
|
105
130
|
*/
|
|
106
131
|
declare function runDocHistoryPostBuild(options: DocHistoryOptions, ctx: PostBuildContext): Promise<void>;
|
|
107
132
|
/**
|
|
@@ -134,4 +159,4 @@ declare function buildGenerateCliArgs(options: DocHistoryOptions, outDir: string
|
|
|
134
159
|
*/
|
|
135
160
|
declare function resolveDocHistoryGenerateBin(): string;
|
|
136
161
|
|
|
137
|
-
export { type ConnectMiddleware, DEFAULT_MAX_ENTRIES, DEFAULT_SERVER_PORT, DOC_HISTORY_GENERATE_BIN, DOC_HISTORY_OUTPUT_DIRNAME, DOC_HISTORY_ROUTE_PREFIX, type DocHistoryLocaleConfig, type DocHistoryOptions, type DocHistoryPluginDescriptor, type MiddlewareLogger, type PostBuildContext, type PostBuildLogger, buildGenerateCliArgs, createDocHistoryDevMiddleware, docHistoryPlugin, resolveDocHistoryGenerateBin, runDocHistoryPostBuild };
|
|
162
|
+
export { type ConnectMiddleware, DEFAULT_MAX_ENTRIES, DEFAULT_SERVER_PORT, DOC_HISTORY_GENERATE_BIN, DOC_HISTORY_GEN_ENV, DOC_HISTORY_OUTPUT_DIRNAME, DOC_HISTORY_ROUTE_PREFIX, type DocHistoryLocaleConfig, type DocHistoryOptions, type DocHistoryPluginDescriptor, type MiddlewareLogger, type PostBuildContext, type PostBuildLogger, buildGenerateCliArgs, createDocHistoryDevMiddleware, docHistoryPlugin, resolveDocHistoryGenerateBin, runDocHistoryPostBuild, shouldGeneratePostBuild };
|
|
@@ -46,11 +46,32 @@ function createDocHistoryDevMiddleware(options, logger) {
|
|
|
46
46
|
});
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
+
const DOC_HISTORY_GEN_ENV = "GEN_DOC_HISTORY";
|
|
50
|
+
function shouldGeneratePostBuild(env = process.env) {
|
|
51
|
+
if (env.SKIP_DOC_HISTORY === "1") {
|
|
52
|
+
return { generate: false, reason: "SKIP_DOC_HISTORY=1" };
|
|
53
|
+
}
|
|
54
|
+
if (env[DOC_HISTORY_GEN_ENV] === "1") {
|
|
55
|
+
return { generate: true, reason: `${DOC_HISTORY_GEN_ENV}=1` };
|
|
56
|
+
}
|
|
57
|
+
if (isCiEnv(env)) {
|
|
58
|
+
return { generate: true, reason: "CI" };
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
generate: false,
|
|
62
|
+
reason: `local default \u2014 set ${DOC_HISTORY_GEN_ENV}=1 to generate`
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function isCiEnv(env) {
|
|
66
|
+
return env.CI === "true" || env.CI === "1" || env.GITHUB_ACTIONS === "true";
|
|
67
|
+
}
|
|
49
68
|
async function runDocHistoryPostBuild(options, ctx) {
|
|
50
|
-
|
|
51
|
-
|
|
69
|
+
const { generate, reason } = shouldGeneratePostBuild();
|
|
70
|
+
if (!generate) {
|
|
71
|
+
ctx.logger?.info(`Skipping doc history generation (${reason})`);
|
|
52
72
|
return;
|
|
53
73
|
}
|
|
74
|
+
ctx.logger?.info(`Generating doc history JSON (${reason})`);
|
|
54
75
|
const args = buildGenerateCliArgs(options, ctx.outDir);
|
|
55
76
|
await spawnDocHistoryGenerate(args, ctx.logger);
|
|
56
77
|
}
|
|
@@ -127,6 +148,7 @@ export {
|
|
|
127
148
|
DEFAULT_MAX_ENTRIES,
|
|
128
149
|
DEFAULT_SERVER_PORT,
|
|
129
150
|
DOC_HISTORY_GENERATE_BIN,
|
|
151
|
+
DOC_HISTORY_GEN_ENV,
|
|
130
152
|
DOC_HISTORY_OUTPUT_DIRNAME,
|
|
131
153
|
DOC_HISTORY_ROUTE_PREFIX,
|
|
132
154
|
buildGenerateCliArgs,
|
|
@@ -134,5 +156,6 @@ export {
|
|
|
134
156
|
docHistoryPlugin,
|
|
135
157
|
resolveDocHistoryGenerateBin,
|
|
136
158
|
runDocHistoryMetaStep,
|
|
137
|
-
runDocHistoryPostBuild
|
|
159
|
+
runDocHistoryPostBuild,
|
|
160
|
+
shouldGeneratePostBuild
|
|
138
161
|
};
|
|
@@ -42,6 +42,12 @@ interface DocHistoryMetaEntry {
|
|
|
42
42
|
}
|
|
43
43
|
/** Manifest shape — keyed by composedSlug. */
|
|
44
44
|
type DocHistoryMetaManifest = Record<string, DocHistoryMetaEntry>;
|
|
45
|
+
/**
|
|
46
|
+
* Tiny in-file semaphore for bounded parallelism — avoids a p-limit dependency.
|
|
47
|
+
* Limits concurrent async tasks to `concurrency` at a time.
|
|
48
|
+
* Ported from packages/doc-history-server/src/cli.ts.
|
|
49
|
+
*/
|
|
50
|
+
declare function makeSemaphore(concurrency: number): () => Promise<() => void>;
|
|
45
51
|
/**
|
|
46
52
|
* Emit `<projectRoot>/.zfb/doc-history-meta.json` from git history.
|
|
47
53
|
*
|
|
@@ -55,4 +61,4 @@ type DocHistoryMetaManifest = Record<string, DocHistoryMetaEntry>;
|
|
|
55
61
|
*/
|
|
56
62
|
declare function runDocHistoryMetaStep(options: RunDocHistoryMetaOptions): Promise<void>;
|
|
57
63
|
|
|
58
|
-
export { type DocHistoryMetaEntry, type DocHistoryMetaLocaleConfig, type DocHistoryMetaManifest, type DocHistoryMetaVersionConfig, type RunDocHistoryMetaOptions, runDocHistoryMetaStep };
|
|
64
|
+
export { type DocHistoryMetaEntry, type DocHistoryMetaLocaleConfig, type DocHistoryMetaManifest, type DocHistoryMetaVersionConfig, type RunDocHistoryMetaOptions, makeSemaphore, runDocHistoryMetaStep };
|
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { cpus } from "node:os";
|
|
3
4
|
import {
|
|
4
5
|
collectContentFiles,
|
|
5
|
-
|
|
6
|
+
getFileCommitsMetaAsync
|
|
6
7
|
} from "@takazudo/zudo-doc-history-server/git-history";
|
|
7
8
|
const META_OUT_RELATIVE_DIR = ".zfb";
|
|
8
9
|
const META_OUT_FILENAME = "doc-history-meta.json";
|
|
10
|
+
function makeSemaphore(concurrency) {
|
|
11
|
+
let running = 0;
|
|
12
|
+
const queue = [];
|
|
13
|
+
function next() {
|
|
14
|
+
if (queue.length > 0 && running < concurrency) {
|
|
15
|
+
queue.shift()();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return function acquire() {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
function tryRun() {
|
|
21
|
+
if (running < concurrency) {
|
|
22
|
+
running++;
|
|
23
|
+
resolve(() => {
|
|
24
|
+
running--;
|
|
25
|
+
next();
|
|
26
|
+
});
|
|
27
|
+
} else {
|
|
28
|
+
queue.push(tryRun);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
tryRun();
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
}
|
|
9
35
|
async function runDocHistoryMetaStep(options) {
|
|
10
36
|
const projectRoot = path.resolve(options.projectRoot);
|
|
11
37
|
const zfbDir = path.join(projectRoot, META_OUT_RELATIVE_DIR);
|
|
@@ -27,24 +53,46 @@ async function runDocHistoryMetaStep(options) {
|
|
|
27
53
|
dirEntries.push([code, path.resolve(projectRoot, locale.dir)]);
|
|
28
54
|
}
|
|
29
55
|
}
|
|
30
|
-
const
|
|
56
|
+
const concurrency = Math.min(8, Math.max(2, cpus().length));
|
|
57
|
+
const acquire = makeSemaphore(concurrency);
|
|
58
|
+
const jobs = [];
|
|
31
59
|
for (const [localeKey, contentDir] of dirEntries) {
|
|
32
60
|
const files = collectContentFiles(contentDir);
|
|
33
61
|
for (const { filePath, slug } of files) {
|
|
34
|
-
const allCommits = getFileCommitsMeta(filePath);
|
|
35
|
-
if (allCommits.length === 0) continue;
|
|
36
|
-
const newestInfo = allCommits[0];
|
|
37
|
-
const oldestInfo = allCommits[allCommits.length - 1] ?? newestInfo;
|
|
38
62
|
const composedSlug = localeKey ? `${localeKey}/${slug}` : slug;
|
|
39
|
-
|
|
40
|
-
// Author comes from the FIRST (oldest) commit.
|
|
41
|
-
author: oldestInfo.author,
|
|
42
|
-
// createdDate = oldest commit; updatedDate = newest commit.
|
|
43
|
-
createdDate: oldestInfo.date,
|
|
44
|
-
updatedDate: newestInfo.date
|
|
45
|
-
};
|
|
63
|
+
jobs.push({ composedSlug, filePath });
|
|
46
64
|
}
|
|
47
65
|
}
|
|
66
|
+
const results = new Array(jobs.length).fill(null);
|
|
67
|
+
await Promise.all(
|
|
68
|
+
jobs.map(async ({ filePath }, i) => {
|
|
69
|
+
const release = await acquire();
|
|
70
|
+
try {
|
|
71
|
+
const allCommits = await getFileCommitsMetaAsync(filePath);
|
|
72
|
+
if (allCommits.length === 0) return;
|
|
73
|
+
const newestInfo = allCommits[0];
|
|
74
|
+
const oldestInfo = allCommits[allCommits.length - 1] ?? newestInfo;
|
|
75
|
+
results[i] = {
|
|
76
|
+
newest: { author: newestInfo.author, date: newestInfo.date },
|
|
77
|
+
oldest: { author: oldestInfo.author, date: oldestInfo.date }
|
|
78
|
+
};
|
|
79
|
+
} finally {
|
|
80
|
+
release();
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
);
|
|
84
|
+
const meta = {};
|
|
85
|
+
for (let i = 0; i < jobs.length; i++) {
|
|
86
|
+
const result = results[i];
|
|
87
|
+
if (result == null) continue;
|
|
88
|
+
meta[jobs[i].composedSlug] = {
|
|
89
|
+
// Author comes from the FIRST (oldest) commit.
|
|
90
|
+
author: result.oldest.author,
|
|
91
|
+
// createdDate = oldest commit; updatedDate = newest commit.
|
|
92
|
+
createdDate: result.oldest.date,
|
|
93
|
+
updatedDate: result.newest.date
|
|
94
|
+
};
|
|
95
|
+
}
|
|
48
96
|
fs.mkdirSync(zfbDir, { recursive: true });
|
|
49
97
|
fs.writeFileSync(outPath, JSON.stringify(meta, null, 2) + "\n", "utf-8");
|
|
50
98
|
logger.info(
|
|
@@ -60,5 +108,6 @@ const defaultLogger = {
|
|
|
60
108
|
}
|
|
61
109
|
};
|
|
62
110
|
export {
|
|
111
|
+
makeSemaphore,
|
|
63
112
|
runDocHistoryMetaStep
|
|
64
113
|
};
|
|
@@ -53,7 +53,7 @@ function createLlmsTxtDevMiddleware(options, logger) {
|
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
function matchLlmsRoute(url, localeCodes) {
|
|
56
|
-
const pathname = url.split("?")[0];
|
|
56
|
+
const pathname = url.split("?")[0] ?? url;
|
|
57
57
|
const m = pathname.match(LLMS_KIND_PATTERN);
|
|
58
58
|
if (!m) return null;
|
|
59
59
|
const prefix = m[1];
|
|
@@ -28,7 +28,9 @@ declare function collectMdFiles(dir: string): Array<{
|
|
|
28
28
|
* URL is produced.
|
|
29
29
|
*/
|
|
30
30
|
declare function slugToUrl(slug: string, locale: string | null, base: string, siteUrl?: string): string;
|
|
31
|
-
/** Whether a given doc should be excluded from the llms.txt index.
|
|
31
|
+
/** Whether a given doc should be excluded from the llms.txt index.
|
|
32
|
+
* `category_no_page` index files are metadata-only with no built route, so
|
|
33
|
+
* they are excluded alongside search_exclude / draft / unlisted. */
|
|
32
34
|
declare function isExcluded(data: LlmsTxtFrontmatter): boolean;
|
|
33
35
|
/**
|
|
34
36
|
* Parse a markdown file into frontmatter + body. Returns `null` when
|
|
@@ -34,7 +34,9 @@ function slugToUrl(slug, locale, base, siteUrl) {
|
|
|
34
34
|
return path;
|
|
35
35
|
}
|
|
36
36
|
function isExcluded(data) {
|
|
37
|
-
return Boolean(
|
|
37
|
+
return Boolean(
|
|
38
|
+
data.search_exclude || data.draft || data.unlisted || data.category_no_page
|
|
39
|
+
);
|
|
38
40
|
}
|
|
39
41
|
function parseMarkdownFile(filePath) {
|
|
40
42
|
try {
|
|
@@ -22,6 +22,12 @@ interface LlmsTxtFrontmatter {
|
|
|
22
22
|
* Mirrors the `search_exclude` flag used by the existing project.
|
|
23
23
|
*/
|
|
24
24
|
search_exclude?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* A `category_no_page` index.mdx carries category metadata only and builds
|
|
27
|
+
* no route — so it must NOT appear in llms.txt (the link would 404). Mirrors
|
|
28
|
+
* the route/sitemap/search exclusion in the host project.
|
|
29
|
+
*/
|
|
30
|
+
category_no_page?: boolean;
|
|
25
31
|
[key: string]: unknown;
|
|
26
32
|
}
|
|
27
33
|
/**
|
|
@@ -19,6 +19,12 @@ interface DocFrontmatter {
|
|
|
19
19
|
draft?: boolean;
|
|
20
20
|
unlisted?: boolean;
|
|
21
21
|
search_exclude?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* A `category_no_page` index.mdx carries category metadata only and builds
|
|
24
|
+
* no route — so its slug must NOT enter the search index (the result link
|
|
25
|
+
* would 404). Mirrors the route/sitemap exclusion in the host project.
|
|
26
|
+
*/
|
|
27
|
+
category_no_page?: boolean;
|
|
22
28
|
[key: string]: unknown;
|
|
23
29
|
}
|
|
24
30
|
/** Parse a markdown file. Returns null when the file cannot be read. */
|
|
@@ -26,7 +32,11 @@ declare function parseMarkdownFile(filePath: string): {
|
|
|
26
32
|
data: DocFrontmatter;
|
|
27
33
|
content: string;
|
|
28
34
|
} | null;
|
|
29
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* A page is excluded from indexing when explicitly opted out, drafted,
|
|
37
|
+
* unlisted, or a `category_no_page` metadata-only category index (no built
|
|
38
|
+
* route to link to).
|
|
39
|
+
*/
|
|
30
40
|
declare function isExcluded(data: DocFrontmatter): boolean;
|
|
31
41
|
|
|
32
42
|
export { type DocFrontmatter, collectMdFiles, isExcluded, parseMarkdownFile, slugToUrl, stripMarkdown };
|
|
@@ -40,7 +40,7 @@ function parseMarkdownFile(filePath) {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
function isExcluded(data) {
|
|
43
|
-
return !!(data.search_exclude || data.draft || data.unlisted);
|
|
43
|
+
return !!(data.search_exclude || data.draft || data.unlisted || data.category_no_page);
|
|
44
44
|
}
|
|
45
45
|
export {
|
|
46
46
|
collectMdFiles,
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/* generated by gen-safelist.mjs — do not edit by hand */
|
|
2
|
+
@source inline("-mb-px [&::-webkit-details-marker]:hidden [&_a]:text-accent [&_a]:underline [&_nav]:mb-0 [doc-history-meta] [doc-layout] a a2 abbr above absolute active actual after after-breadcrumb after-content after-sidebar after-title against agent agents ai-chat ai-chat-trigger align-top all allow-same-origin allow-scripts already-executed an anchor anchored and announce antialiased any application/json applies apply-css-vars are area arg aria-controls aria-current aria-disabled aria-expanded aria-haspopup aria-hidden aria-label aria-live aria-orientation aria-pressed aria-selected aria-valuemax aria-valuemin aria-valuenow article as asc aside assets assigning assistant async at attach attribute attributes available await away b back backdrop:bg-bg/80 background backtick backticks baked banner bare based be because before below between bg bg-[#fff] bg-accent bg-bg bg-code-bg bg-fg bg-info/10 bg-muted bg-surface bg-transparent bg-warning/10 bg-warning/5 bi bigint bin blob block blockquote blocks blur body body-end-components body-end-scripts boolean bootstrap border border-accent border-b border-b-2 border-b-[5px] border-collapse border-fg border-l border-l-[3px] border-muted border-none border-r border-t border-t-[2px] border-t-[3px] border-transparent border-warning/30 both br breadcrumb:end breadcrumb:start break-words browser browsers btn bundler but button buttons by caller can cancellation cannot canonical caption cases cat-nav- catch category catppuccin-latte checkbox child ci circle cite class class-less claude claude-agents claude-commands claude-md claude-skills cleaned clear clear-css-vars click client clip close code code-block-sr-announce col col-resize colgroup collision color color-scheme color-scheme-changed color-scheme-provider color-tweak colors command commands commit component component:github-link component:language-switcher component:search component:theme-toggle component:version-switcher concrete configuration configure configured consumer container containers content content-type content-wrapper:end content-wrapper:start contents controller converts copy correct covered covers crashes crumb- css cursor cursor-not-allowed cursor-pointer custom dark data-active data-group-id data-header data-header-logo data-header-nav data-header-right data-mermaid-rendered data-nav-item data-nav-item-dropdown data-nav-more data-nav-more-menu data-nav-more-toggle data-sidebar-resizer data-tab-btn data-tab-default data-tab-label data-tab-value data-tabs data-taglist-group data-testid data-theme data-variant data-version-banner data-version-menu data-version-switcher data-version-toggle data-zfb-transition-persist dd decimal declare decoration-muted default defaults del desc description design design-token-panel design-token-trigger desktop desktop-sidebar detached details deterministic dfn diagram diagrams dialog dieser directories directory disc display:none dist div dl doc doc-card- doc-history doc-history-generate does drag draggable dropdown dropdowns dt duration-150 duration-200 during dynamically e2e earlier edge el element elements els else em emit emitting empty en entries entry error escape even eventually every exactly exit expected failed fall fallback falls false fast feed fg fieldset figcaption figure file fill fills finally fire fires first fixed fixtures flex flex-1 flex-col flex-wrap flip flipping flips focus focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-2 focus-visible:underline focus:underline font font-bold font-medium font-mono font-semibold footer for form free from frontmatter frontmatter-preview full function further g gap-[clamp(1.5rem,3vw,4rem)] gap-hsp-2xs gap-hsp-sm gap-hsp-xs gap-vsp-lg gap-vsp-xs gap-x-hsp-2xs gap-x-hsp-lg gap-x-hsp-md gap-x-hsp-sm gap-x-hsp-xs gap-y-vsp-2xs gap-y-vsp-lg gap-y-vsp-md gap-y-vsp-xs gaps generate generation geometry get github github-link go got gray-matter grid grid-cols-1 group group-focus-visible:underline group-focus-within:block group-hover:bg-fg group-hover:block group-hover:text-bg group-hover:underline group-open:rotate-90 guard h-[0.5rem] h-[0.875rem] h-[1.575rem] h-[1lh] h-[3.5rem] h-[calc(100vh-3.5rem)] h-icon-sm h-icon-xs h1 h2 h3 h4 h5 h6 hand handle handled handlers has hash-link have head head-links head-scripts header header-call:end header-call:start height here hex hidden highlighter history hit horizontal host hover:bg-[color-mix(in_srgb,var(--color-surface)_80%,var(--color-fg)_20%)] hover:bg-accent-hover hover:bg-accent/10 hover:border-accent hover:border-accent-hover hover:border-fg hover:text-accent hover:text-accent-hover hover:text-fg hover:underline hr html i i2 i3 i4 identical if iframe img import imports in index inherit initial initialised inline inline-block inline-flex input ins inset-0 inside instanceof instructions intent into inverse invoke is it italic item- items items-center items-start itself javascript justify-between justify-center justify-end kbd keep keeps kept keydown khroma label landing language-switcher last:border-b-0 launch leading-relaxed leading-snug leading-tight leaf- leaving left left-0 left:calc legend lg lg:block lg:flex lg:grid-cols-[repeat(auto-fit,minmax(12rem,1fr))] lg:ml-[var(--zd-sidebar-w)] lg:pt-vsp-2xl lg:px-hsp-2xl lg:py-vsp-2xl li li2 light like line link link- list-disc list-none listener literal lives llms llms-txt load local lostpointercapture luminance m m-0 main make maps mark marks matching max-h-[80vh] max-w-[46rem] max-w-[80rem] max-w-[clamp(50rem,75vw,90rem)] max-w-full max-w-none mb-0 mb-vsp-2xs mb-vsp-lg mb-vsp-md mb-vsp-sm mb-vsp-xl mb-vsp-xs menu merged mermaid message meta metadata min-h-[calc(100vh-3.5rem)] min-h-screen min-w-0 min-w-[10rem] min-w-[8rem] mirror missing ml-auto ml-hsp-2xl ml-hsp-lg ml-hsp-sm ml-hsp-xl mod mode mounted mouseenter mouseleave mt-0 mt-vsp-2xs mt-vsp-3xs mt-vsp-lg mt-vsp-sm mt-vsp-xl must mutates mx-auto my-vsp-lg my-vsp-md name named native nav nav-card- navigating navigation navigations near needs new next no-underline node:fs node:module node:os node:path nodes nofollow noindex non-empty non-string none noopener noreferrer normal not not-object note now null number object observe observer of og:description og:image og:title og:type og:url ol older on once one only opacity-60 option or other others out overflow overflow-auto overflow-hidden overflow-x-auto overflow-y-auto overflow-y:auto own p p-0 p-hsp-lg p-hsp-md p-hsp-sm p-hsp-xl packages padding page page-loading-overlay page-loading-spinner page-navigate-end pages paint panel panels parent parse parsed pass path pb-vsp-xl per pi pick picked picks pins pipelines pl-[1.25rem] pl-hsp-lg pl-hsp-sm pl-hsp-xl place pointer-events-none pointercancel pointerdown pointermove pointerup polite polyline populates port position position:fixed pr-hsp-lg pr-hsp-md pr-hsp-xl pre preact preact/hooks preact/jsx-runtime preload pres produced produces production propagating properties property proxy pt-vsp-3xs pt-vsp-md pt-vsp-sm pt-vsp-xl pt-vsp-xs ptag- public purely px px-hsp-lg px-hsp-md px-hsp-sm px-hsp-xl px-hsp-xs py-0 py-hsp-2xs py-hsp-sm py-hsp-xs py-vsp-2xs py-vsp-3xs py-vsp-md py-vsp-sm py-vsp-xl py-vsp-xs q r raw re-render re-run reach reaches reading ready real references regenerates reinit relative remains render rendered renders reorder replaced repopulate requires reserved resize resize-x resolve resolved resolves return returns right- right-0 ro robots role root rotate-180 rotate-90 round round-trip rounded rounded-full rounded-lg running runs runtime s safer samp schema-mismatch schema-missing script script-evaluation scripts scroll scrollbar scrolled scrollend search section section- see sel-bg sel-fg select select-none self-start separator server server-rendered set shadow-[0_1px_3px_color-mix(in_srgb,var(--color-fg)_8%,transparent)] shadow-lg shared shiki ships should show shown shrink-0 sidebar signal single sitemap- size skill skills slash slug sm:flex-row sm:grid-cols-2 sm:items-center sm:justify-between small so solid some source space-y-vsp-2xs spacing span spans spec specifiers sr-only stale state stay sticky still stored stray string strip stroke-linecap stroke-linejoin stroke-width strong style styles stylesheet sub subagents subsequent success summary sup support survives svg synchronous synchronously syntactically syntect tab tab-panel tabindex table tablist tabpanel tabs tabs-container tabs-content tabs-nav tag- tag-item- tbody td temp temp-element template temporary temporary-element test-results text text-accent text-bg text-body text-caption text-center text-code-fg text-fg text-heading text-info text-left text-micro text-muted text-muted/50 text-small text-title text-warning textarea tfoot th that the thead them theme theme-color theme-toggle then these they this through time title to toggle toggle-ai-chat toggle-design-token-panel token tokens tolerates too top-0 top-[3.5rem] top-full total tr tracking-wider trade-off transition-[background,color,border-color] transition-colors transition-transform tree-child- tree-item- tree-top- trick trigger trigger:ai-chat trigger:design-token-panel triggers true try twitter:card twitter:creator twitter:description twitter:image twitter:site twitter:title two u ul unavailable undefined under underline understand unknown unmaintained unobserve unreadable unreleased unreliable unset unsupported up uppercase use used uses utf-8 utf8 utilities v val value values var variable version- version-menu version-switcher vertical via video viewport visible vitesse-dark w w-[0.5rem] w-[0.875rem] w-[1.575rem] w-[16px] w-[280px] w-[var(--zd-sidebar-w)] w-full w-icon-sm w-icon-xs was watching wbr wbr- we when where whereas which while whitespace-nowrap whitespace-pre whole will with without word worktrees would wrap wrapper wrappers written wrote xl:flex xl:hidden y-scrollbar yet z-10 z-30 z-50 zd-content zd-html-preview-code zfb zfb:after-swap zfb:before-preparation zudo-doc-design-tokens/v1 zudo-doc-sidebar-width zudo-doc-theme zudo-doc-theme-bridge zudo-doc-tweak-state zudo-doc-tweak-state-v2");
|
|
@@ -59,7 +59,8 @@ function toSidebarNodes(parent, locale, buildHref, categoryMeta, parentSortOrder
|
|
|
59
59
|
for (const child of parent.children.values()) {
|
|
60
60
|
const doc = child.doc;
|
|
61
61
|
const meta = categoryMeta?.get(child.fullPath);
|
|
62
|
-
const
|
|
62
|
+
const noPage = doc?.data.category_no_page ?? meta?.noPage;
|
|
63
|
+
const sortOrder = doc?.data.category_sort_order ?? meta?.sortOrder ?? "asc";
|
|
63
64
|
const children = toSidebarNodes(
|
|
64
65
|
child,
|
|
65
66
|
locale,
|
|
@@ -67,13 +68,14 @@ function toSidebarNodes(parent, locale, buildHref, categoryMeta, parentSortOrder
|
|
|
67
68
|
categoryMeta,
|
|
68
69
|
sortOrder
|
|
69
70
|
);
|
|
70
|
-
const hasPage = !!doc;
|
|
71
|
+
const hasPage = !!doc && noPage !== true;
|
|
71
72
|
const isCategory = !hasPage && children.length > 0;
|
|
72
73
|
const label = doc?.data.sidebar_label ?? doc?.data.title ?? meta?.label ?? toTitleCase(child.segment);
|
|
73
74
|
const description = doc?.data.description ?? meta?.description;
|
|
74
75
|
const positionRaw = doc?.data.sidebar_position ?? meta?.position;
|
|
75
76
|
const position = positionRaw ?? 999;
|
|
76
|
-
const href =
|
|
77
|
+
const href = noPage ? void 0 : doc || children.length > 0 ? buildHref(child.fullPath, locale) : void 0;
|
|
78
|
+
const hasSortOrder = doc?.data.category_sort_order !== void 0 || meta?.sortOrder !== void 0;
|
|
77
79
|
nodes.push({
|
|
78
80
|
type: isCategory ? "category" : "doc",
|
|
79
81
|
id: child.fullPath,
|
|
@@ -82,7 +84,7 @@ function toSidebarNodes(parent, locale, buildHref, categoryMeta, parentSortOrder
|
|
|
82
84
|
...positionRaw !== void 0 ? { sidebar_position: positionRaw } : {},
|
|
83
85
|
...href !== void 0 ? { href } : {},
|
|
84
86
|
hasPage,
|
|
85
|
-
...
|
|
87
|
+
...hasSortOrder ? { sortOrder } : {},
|
|
86
88
|
children
|
|
87
89
|
});
|
|
88
90
|
Object.defineProperty(nodes[nodes.length - 1], "__sortPosition", {
|
|
@@ -27,6 +27,20 @@ interface SidebarFrontmatter {
|
|
|
27
27
|
* the slug from the entry's id (stripping a trailing `/index`).
|
|
28
28
|
*/
|
|
29
29
|
slug?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Category metadata carried on a directory's `index.mdx` frontmatter — the
|
|
32
|
+
* frontmatter equivalent of `_category_.json`'s `noPage`. When `true`, this
|
|
33
|
+
* index file exists only to label/position the category: it renders as a
|
|
34
|
+
* non-linked sidebar header and is excluded from routes, the sitemap, and
|
|
35
|
+
* the search index. Frontmatter wins over the sidecar when both are present.
|
|
36
|
+
*/
|
|
37
|
+
category_no_page?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Frontmatter equivalent of `_category_.json`'s `sortOrder`. Controls the
|
|
40
|
+
* sort direction of this category's children. Frontmatter wins over the
|
|
41
|
+
* sidecar when both are present.
|
|
42
|
+
*/
|
|
43
|
+
category_sort_order?: "asc" | "desc";
|
|
30
44
|
}
|
|
31
45
|
/**
|
|
32
46
|
* Minimum shape required of a content collection entry. Designed to be a
|
package/dist/toc/smart-break.js
CHANGED
|
@@ -20,7 +20,7 @@ function smartBreak(text) {
|
|
|
20
20
|
const nodes = [];
|
|
21
21
|
for (let i = 0; i < parts.length; i++) {
|
|
22
22
|
const part = parts[i];
|
|
23
|
-
if (part
|
|
23
|
+
if (!part) continue;
|
|
24
24
|
nodes.push(part);
|
|
25
25
|
if (i % 2 === 1) nodes.push(/* @__PURE__ */ jsx("wbr", {}, `wbr-${i}`));
|
|
26
26
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takazudo/zudo-doc",
|
|
3
|
-
"version": "0.2.0-next.
|
|
3
|
+
"version": "0.2.0-next.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "zudo-doc framework primitives layer that sits on top of zfb's engine — sidebar, theme, TOC, breadcrumb, layouts, head injection, View Transitions, SSR-skip wrappers (per ADR-003).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -150,7 +150,9 @@
|
|
|
150
150
|
"./icons": {
|
|
151
151
|
"types": "./dist/icons/index.d.ts",
|
|
152
152
|
"default": "./dist/icons/index.js"
|
|
153
|
-
}
|
|
153
|
+
},
|
|
154
|
+
"./safelist.css": "./dist/safelist.css",
|
|
155
|
+
"./safelist": "./dist/safelist.css"
|
|
154
156
|
},
|
|
155
157
|
"files": [
|
|
156
158
|
"dist",
|
|
@@ -158,10 +160,10 @@
|
|
|
158
160
|
],
|
|
159
161
|
"peerDependencies": {
|
|
160
162
|
"preact": "^10.29.1",
|
|
161
|
-
"@takazudo/zfb": "^0.1.0-next.
|
|
162
|
-
"@takazudo/zfb-runtime": "^0.1.0-next.
|
|
163
|
+
"@takazudo/zfb": "^0.1.0-next.35",
|
|
164
|
+
"@takazudo/zfb-runtime": "^0.1.0-next.35",
|
|
163
165
|
"@takazudo/zdtp": "^0.2.0-next.2",
|
|
164
|
-
"@takazudo/zudo-doc-history-server": "^0.2.0-next.
|
|
166
|
+
"@takazudo/zudo-doc-history-server": "^0.2.0-next.8"
|
|
165
167
|
},
|
|
166
168
|
"peerDependenciesMeta": {
|
|
167
169
|
"@takazudo/zudo-doc-history-server": {
|
|
@@ -182,8 +184,8 @@
|
|
|
182
184
|
"tsup": "^8.0.0",
|
|
183
185
|
"typescript": "^5.0.0",
|
|
184
186
|
"vitest": "^3.0.0",
|
|
185
|
-
"@takazudo/zfb": "0.1.0-next.
|
|
186
|
-
"@takazudo/zfb-runtime": "0.1.0-next.
|
|
187
|
+
"@takazudo/zfb": "0.1.0-next.35",
|
|
188
|
+
"@takazudo/zfb-runtime": "0.1.0-next.35"
|
|
187
189
|
},
|
|
188
190
|
"scripts": {
|
|
189
191
|
"build": "cross-env NODE_OPTIONS=--max-old-space-size=4096 tsup",
|