create-zudo-doc 3.1.0 → 3.3.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/claude-md-gen.d.ts +8 -0
- package/dist/claude-md-gen.js +43 -26
- package/dist/compose.d.ts +14 -20
- package/dist/compose.js +15 -25
- package/dist/features/body-foot-util.d.ts +8 -4
- package/dist/features/body-foot-util.js +8 -4
- package/dist/features/claude-resources.d.ts +12 -0
- package/dist/features/claude-resources.js +13 -6
- package/dist/features/design-token-panel.d.ts +0 -12
- package/dist/features/design-token-panel.js +32 -93
- package/dist/features/doc-history.d.ts +18 -3
- package/dist/features/doc-history.js +45 -60
- package/dist/features/doc-tags.d.ts +9 -13
- package/dist/features/doc-tags.js +10 -26
- package/dist/features/dynamic-page-transition.d.ts +19 -30
- package/dist/features/dynamic-page-transition.js +21 -209
- package/dist/features/footer-taglist.d.ts +1 -1
- package/dist/features/footer-taglist.js +1 -1
- package/dist/features/footer.d.ts +3 -2
- package/dist/features/footer.js +3 -2
- package/dist/features/i18n.d.ts +13 -8
- package/dist/features/i18n.js +14 -9
- package/dist/features/image-enlarge.d.ts +7 -26
- package/dist/features/image-enlarge.js +7 -26
- package/dist/features/llms-txt.d.ts +3 -5
- package/dist/features/llms-txt.js +3 -5
- package/dist/features/search.d.ts +7 -3
- package/dist/features/search.js +7 -3
- package/dist/features/sidebar-resizer.d.ts +4 -8
- package/dist/features/sidebar-resizer.js +4 -8
- package/dist/features/sidebar-toggle.d.ts +7 -7
- package/dist/features/sidebar-toggle.js +7 -7
- package/dist/features/tag-governance.d.ts +14 -7
- package/dist/features/tag-governance.js +52 -8
- package/dist/features/tauri.d.ts +13 -10
- package/dist/features/tauri.js +16 -52
- package/dist/features/versioning.d.ts +12 -24
- package/dist/features/versioning.js +13 -39
- package/dist/index.js +2 -3
- package/dist/preset.d.ts +1 -0
- package/dist/preset.js +5 -0
- package/dist/prompts.d.ts +2 -0
- package/dist/prompts.js +1 -0
- package/dist/scaffold.d.ts +13 -6
- package/dist/scaffold.js +78 -59
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +14 -0
- package/dist/zfb-config-gen.d.ts +32 -20
- package/dist/zfb-config-gen.js +400 -53
- package/package.json +2 -2
- package/templates/base/pages/docs/[[...slug]].tsx +59 -0
- package/templates/base/pages/index.tsx +6 -41
- package/templates/base/scripts/setup-doc-skill.sh +261 -0
- package/templates/base/src/styles/global.css +15 -340
- package/templates/base/tsconfig.json +3 -26
- package/templates/features/i18n/files/pages/[locale]/docs/[[...slug]].tsx +88 -0
- package/templates/features/tagGovernance/files/scripts/tags-suggest.ts +3 -1
- package/dist/settings-gen.d.ts +0 -2
- package/dist/settings-gen.js +0 -319
- package/templates/base/.htmlvalidate.json +0 -5
- package/templates/base/.zfb/doc-history-meta.json +0 -1
- package/templates/base/pages/_data.ts +0 -160
- package/templates/base/pages/lib/_body-end-islands.tsx +0 -165
- package/templates/base/pages/lib/_chrome.ts +0 -167
- package/templates/base/pages/lib/_details.tsx +0 -29
- package/templates/base/pages/lib/_doc-route-entries.ts +0 -10
- package/templates/base/pages/lib/_extract-headings.ts +0 -37
- package/templates/base/pages/lib/_frontmatter-preview-data.ts +0 -27
- package/templates/base/pages/lib/_nav-source-cache.ts +0 -100
- package/templates/base/pages/lib/_nav-source-docs.ts +0 -17
- package/templates/base/pages/lib/_preset-generator.tsx +0 -81
- package/templates/base/pages/lib/_route-context.ts +0 -32
- package/templates/base/pages/lib/_search-widget.tsx +0 -17
- package/templates/base/pages/lib/doc-page-props.ts +0 -30
- package/templates/base/pages/lib/locale-merge.ts +0 -59
- package/templates/base/scripts/run-b4push.sh +0 -102
- package/templates/base/src/components/ai-chat-modal.tsx +0 -18
- package/templates/base/src/components/content/code-group.tsx +0 -3
- package/templates/base/src/components/content/content-admonition.tsx +0 -4
- package/templates/base/src/components/desktop-sidebar-toggle.tsx +0 -15
- package/templates/base/src/components/doc-history.tsx +0 -21
- package/templates/base/src/components/image-enlarge.tsx +0 -24
- package/templates/base/src/components/preset-generator.tsx +0 -14
- package/templates/base/src/components/sidebar-toggle.tsx +0 -6
- package/templates/base/src/components/sidebar-tree.tsx +0 -6
- package/templates/base/src/config/color-scheme-utils.ts +0 -69
- package/templates/base/src/config/color-schemes.ts +0 -165
- package/templates/base/src/config/docs-schema.ts +0 -95
- package/templates/base/src/config/frontmatter-preview-defaults.ts +0 -27
- package/templates/base/src/config/frontmatter-preview-renderers.tsx +0 -46
- package/templates/base/src/config/i18n.ts +0 -239
- package/templates/base/src/config/settings-types.ts +0 -45
- package/templates/base/src/config/sidebars.ts +0 -66
- package/templates/base/src/config/tag-vocabulary-types.ts +0 -4
- package/templates/base/src/config/tag-vocabulary.ts +0 -20
- package/templates/base/src/config/z-index-tokens.ts +0 -128
- package/templates/base/src/types/docs-entry.ts +0 -28
- package/templates/base/src/types/heading.ts +0 -5
- package/templates/base/src/types/locale.ts +0 -10
- package/templates/base/src/utils/base.ts +0 -164
- package/templates/base/src/utils/docs.ts +0 -446
- package/templates/base/src/utils/git-info.ts +0 -70
- package/templates/base/src/utils/github.ts +0 -22
- package/templates/base/src/utils/nav-scope.ts +0 -34
- package/templates/base/src/utils/sidebar.ts +0 -36
- package/templates/base/src/utils/slug.ts +0 -10
- package/templates/base/src/utils/smart-break.tsx +0 -12
- package/templates/base/src/utils/tags.ts +0 -83
- package/templates/base/zfb-shim.d.ts +0 -178
- package/templates/features/bodyFootUtil/files/src/utils/github.ts +0 -22
- package/templates/features/claudeResources/files/src/integrations/claude-resources/__tests__/escape-for-mdx.test.ts +0 -42
- package/templates/features/claudeResources/files/src/integrations/claude-resources/__tests__/generate.test.ts +0 -752
- package/templates/features/claudeResources/files/src/integrations/claude-resources/escape-for-mdx.ts +0 -97
- package/templates/features/claudeResources/files/src/integrations/claude-resources/generate.ts +0 -735
- package/templates/features/designTokenPanel/files/src/components/design-token-panel-bootstrap.tsx +0 -15
- package/templates/features/designTokenPanel/files/src/config/design-token-panel-config.ts +0 -435
- package/templates/features/designTokenPanel/files/src/config/design-tokens-manifest.ts +0 -174
- package/templates/features/designTokenPanel/files/src/lib/design-token-panel-bootstrap.ts +0 -30
- package/templates/features/docHistory/files/src/components/doc-history.tsx +0 -10
- package/templates/features/docHistory/files/src/types/doc-history.ts +0 -7
- package/templates/features/dynamicPageTransition/files/src/components/client-router-bootstrap.tsx +0 -72
- package/templates/features/i18n/files/pages/[locale]/index.tsx +0 -72
- package/templates/features/imageEnlarge/files/src/components/image-enlarge.tsx +0 -11
- package/templates/features/sidebarToggle/files/src/components/desktop-sidebar-toggle.tsx +0 -6
- package/templates/features/tauri/files/src/components/find-bar.tsx +0 -122
- package/templates/features/tauri/files/src/components/find-in-page-init.tsx +0 -59
- package/templates/features/tauri/files/src/utils/find-in-page.ts +0 -175
|
@@ -1,752 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import os from "node:os";
|
|
5
|
-
import matter from "gray-matter";
|
|
6
|
-
import { generateClaudeResourcesDocs } from "../generate";
|
|
7
|
-
|
|
8
|
-
let tmpDir: string;
|
|
9
|
-
let claudeDir: string;
|
|
10
|
-
let docsDir: string;
|
|
11
|
-
|
|
12
|
-
function createFixture() {
|
|
13
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "claude-res-test-"));
|
|
14
|
-
|
|
15
|
-
// .claude/ directory
|
|
16
|
-
claudeDir = path.join(tmpDir, ".claude");
|
|
17
|
-
docsDir = path.join(tmpDir, "docs");
|
|
18
|
-
fs.mkdirSync(docsDir, { recursive: true });
|
|
19
|
-
|
|
20
|
-
// Commands
|
|
21
|
-
const commandsDir = path.join(claudeDir, "commands");
|
|
22
|
-
fs.mkdirSync(commandsDir, { recursive: true });
|
|
23
|
-
fs.writeFileSync(
|
|
24
|
-
path.join(commandsDir, "test-cmd.md"),
|
|
25
|
-
'---\ndescription: "A test command"\n---\n\nThis is a test command body.',
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
// Skills
|
|
29
|
-
const skillDir = path.join(claudeDir, "skills", "test-skill");
|
|
30
|
-
fs.mkdirSync(path.join(skillDir, "references"), { recursive: true });
|
|
31
|
-
fs.mkdirSync(path.join(skillDir, "scripts"), { recursive: true });
|
|
32
|
-
fs.mkdirSync(path.join(skillDir, "assets"), { recursive: true });
|
|
33
|
-
|
|
34
|
-
fs.writeFileSync(
|
|
35
|
-
path.join(skillDir, "SKILL.md"),
|
|
36
|
-
'---\nname: test-skill\ndescription: "A test skill"\n---\n\nSkill instructions here.\n\nSee [references/guide.md](references/guide.md) for details.',
|
|
37
|
-
);
|
|
38
|
-
fs.writeFileSync(
|
|
39
|
-
path.join(skillDir, "references", "guide.md"),
|
|
40
|
-
"# Guide\n\nSome guide content",
|
|
41
|
-
);
|
|
42
|
-
fs.writeFileSync(
|
|
43
|
-
path.join(skillDir, "scripts", "run.sh"),
|
|
44
|
-
"#!/bin/bash\n# Run the test",
|
|
45
|
-
);
|
|
46
|
-
fs.writeFileSync(
|
|
47
|
-
path.join(skillDir, "assets", "template.md"),
|
|
48
|
-
"# Template\n\nA template",
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
// Agents
|
|
52
|
-
const agentsDir = path.join(claudeDir, "agents");
|
|
53
|
-
fs.mkdirSync(agentsDir, { recursive: true });
|
|
54
|
-
fs.writeFileSync(
|
|
55
|
-
path.join(agentsDir, "test-agent.md"),
|
|
56
|
-
'---\nname: test-agent\ndescription: "A test agent"\nmodel: sonnet\n---\n\nAgent instructions here.',
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
// Root CLAUDE.md
|
|
60
|
-
fs.writeFileSync(
|
|
61
|
-
path.join(tmpDir, "CLAUDE.md"),
|
|
62
|
-
"# Project\n\nProject instructions",
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
describe("generateClaudeResourcesDocs", () => {
|
|
67
|
-
beforeEach(() => {
|
|
68
|
-
createFixture();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
afterEach(() => {
|
|
72
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
// ---------------------------------------------------------------------------
|
|
76
|
-
// File structure tests
|
|
77
|
-
// ---------------------------------------------------------------------------
|
|
78
|
-
|
|
79
|
-
describe("file structure", () => {
|
|
80
|
-
it("generates correct directory structure", () => {
|
|
81
|
-
generateClaudeResourcesDocs({
|
|
82
|
-
claudeDir,
|
|
83
|
-
projectRoot: tmpDir,
|
|
84
|
-
docsDir,
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
expect(fs.existsSync(path.join(docsDir, "claude"))).toBe(true);
|
|
88
|
-
expect(fs.existsSync(path.join(docsDir, "claude-md"))).toBe(true);
|
|
89
|
-
expect(fs.existsSync(path.join(docsDir, "claude-commands"))).toBe(true);
|
|
90
|
-
expect(fs.existsSync(path.join(docsDir, "claude-skills"))).toBe(true);
|
|
91
|
-
expect(fs.existsSync(path.join(docsDir, "claude-agents"))).toBe(true);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("generates index.mdx with category_no_page for sub-categories", () => {
|
|
95
|
-
generateClaudeResourcesDocs({
|
|
96
|
-
claudeDir,
|
|
97
|
-
projectRoot: tmpDir,
|
|
98
|
-
docsDir,
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
const dirs = ["claude-md", "claude-commands", "claude-skills", "claude-agents"];
|
|
102
|
-
for (const dir of dirs) {
|
|
103
|
-
const indexPath = path.join(docsDir, dir, "index.mdx");
|
|
104
|
-
expect(fs.existsSync(indexPath)).toBe(true);
|
|
105
|
-
|
|
106
|
-
const parsed = matter(fs.readFileSync(indexPath, "utf8"));
|
|
107
|
-
expect(parsed.data).toHaveProperty("title");
|
|
108
|
-
expect(parsed.data).toHaveProperty("sidebar_position");
|
|
109
|
-
expect(parsed.data).toHaveProperty("description");
|
|
110
|
-
expect(parsed.data.category_no_page).toBe(true);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("generates skill page as <dir>/index.mdx", () => {
|
|
115
|
-
generateClaudeResourcesDocs({
|
|
116
|
-
claudeDir,
|
|
117
|
-
projectRoot: tmpDir,
|
|
118
|
-
docsDir,
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// The skill page is the directory index so `./ref-<name>` links resolve
|
|
122
|
-
// against the source file path (#2411) — NOT a flat `<dir>.mdx`.
|
|
123
|
-
const indexPath = path.join(docsDir, "claude-skills", "test-skill", "index.mdx");
|
|
124
|
-
expect(fs.existsSync(indexPath)).toBe(true);
|
|
125
|
-
|
|
126
|
-
const flatPath = path.join(docsDir, "claude-skills", "test-skill.mdx");
|
|
127
|
-
expect(fs.existsSync(flatPath)).toBe(false);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// ---------------------------------------------------------------------------
|
|
132
|
-
// Content tests
|
|
133
|
-
// ---------------------------------------------------------------------------
|
|
134
|
-
|
|
135
|
-
describe("content", () => {
|
|
136
|
-
it("generates overview page with CategoryNav", () => {
|
|
137
|
-
generateClaudeResourcesDocs({
|
|
138
|
-
claudeDir,
|
|
139
|
-
projectRoot: tmpDir,
|
|
140
|
-
docsDir,
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
const overview = fs.readFileSync(
|
|
144
|
-
path.join(docsDir, "claude", "index.mdx"),
|
|
145
|
-
"utf8",
|
|
146
|
-
);
|
|
147
|
-
expect(overview).toContain('<CategoryNav categories={');
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("overview body has no prose line duplicating the frontmatter description", () => {
|
|
151
|
-
// Regression test for #2568: the body used to repeat the frontmatter
|
|
152
|
-
// `description` verbatim as a standalone prose line right below `---`.
|
|
153
|
-
generateClaudeResourcesDocs({
|
|
154
|
-
claudeDir,
|
|
155
|
-
projectRoot: tmpDir,
|
|
156
|
-
docsDir,
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
const overview = fs.readFileSync(
|
|
160
|
-
path.join(docsDir, "claude", "index.mdx"),
|
|
161
|
-
"utf8",
|
|
162
|
-
);
|
|
163
|
-
const parsed = matter(overview);
|
|
164
|
-
const description = parsed.data.description as string;
|
|
165
|
-
const bodyLines = parsed.content.split("\n");
|
|
166
|
-
|
|
167
|
-
expect(bodyLines).not.toContain(description);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it("skill page has correct frontmatter", () => {
|
|
171
|
-
generateClaudeResourcesDocs({
|
|
172
|
-
claudeDir,
|
|
173
|
-
projectRoot: tmpDir,
|
|
174
|
-
docsDir,
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
const skillPage = fs.readFileSync(
|
|
178
|
-
path.join(docsDir, "claude-skills", "test-skill", "index.mdx"),
|
|
179
|
-
"utf8",
|
|
180
|
-
);
|
|
181
|
-
const parsed = matter(skillPage);
|
|
182
|
-
|
|
183
|
-
expect(parsed.data.title).toBe("test-skill");
|
|
184
|
-
expect(parsed.data.description).toBe("A test skill");
|
|
185
|
-
expect(parsed.data.sidebar_label).toBe("test-skill");
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it("skill page has file tree", () => {
|
|
189
|
-
generateClaudeResourcesDocs({
|
|
190
|
-
claudeDir,
|
|
191
|
-
projectRoot: tmpDir,
|
|
192
|
-
docsDir,
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
const skillPage = fs.readFileSync(
|
|
196
|
-
path.join(docsDir, "claude-skills", "test-skill", "index.mdx"),
|
|
197
|
-
"utf8",
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
// Should contain tree-drawing characters
|
|
201
|
-
expect(skillPage).toContain("├── ");
|
|
202
|
-
expect(skillPage).toContain("└── ");
|
|
203
|
-
expect(skillPage).toContain("test-skill/");
|
|
204
|
-
expect(skillPage).toContain("SKILL.md");
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it("skill page has links to sub-files that resolve to sibling nested files", () => {
|
|
208
|
-
generateClaudeResourcesDocs({
|
|
209
|
-
claudeDir,
|
|
210
|
-
projectRoot: tmpDir,
|
|
211
|
-
docsDir,
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
const skillPage = fs.readFileSync(
|
|
215
|
-
path.join(docsDir, "claude-skills", "test-skill", "index.mdx"),
|
|
216
|
-
"utf8",
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
// Links use ./<subpage>; the skill page is <dir>/index.mdx so these
|
|
220
|
-
// resolve against the file path to the sibling <dir>/<subpage>.mdx (#2411).
|
|
221
|
-
expect(skillPage).toContain("./ref-guide");
|
|
222
|
-
expect(skillPage).toContain("./asset-template");
|
|
223
|
-
|
|
224
|
-
// Must NOT contain the double-dir pattern ./<dir>/<subpage>
|
|
225
|
-
expect(skillPage).not.toContain("./test-skill/ref-guide");
|
|
226
|
-
expect(skillPage).not.toContain("./test-skill/asset-template");
|
|
227
|
-
|
|
228
|
-
// Each linked sub-page must exist as a sibling nested file inside <dir>/,
|
|
229
|
-
// i.e. claude-skills/test-skill/<subpage>.mdx — which is exactly where the
|
|
230
|
-
// ./<subpage> relative link resolves to.
|
|
231
|
-
const linkPattern = /\]\(\.\/([\w-]+)\)/g;
|
|
232
|
-
let match;
|
|
233
|
-
while ((match = linkPattern.exec(skillPage)) !== null) {
|
|
234
|
-
const subPage = match[1];
|
|
235
|
-
const targetFile = path.join(
|
|
236
|
-
docsDir,
|
|
237
|
-
"claude-skills",
|
|
238
|
-
"test-skill",
|
|
239
|
-
`${subPage}.mdx`,
|
|
240
|
-
);
|
|
241
|
-
expect(
|
|
242
|
-
fs.existsSync(targetFile),
|
|
243
|
-
`Link target "test-skill/${subPage}.mdx" should exist`,
|
|
244
|
-
).toBe(true);
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it("skill body references/scripts/assets links are rewritten to doc site format", () => {
|
|
249
|
-
generateClaudeResourcesDocs({
|
|
250
|
-
claudeDir,
|
|
251
|
-
projectRoot: tmpDir,
|
|
252
|
-
docsDir,
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
const skillPage = fs.readFileSync(
|
|
256
|
-
path.join(docsDir, "claude-skills", "test-skill", "index.mdx"),
|
|
257
|
-
"utf8",
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
// Body links like (references/guide.md) should be rewritten to (./ref-guide)
|
|
261
|
-
expect(skillPage).toContain("](./ref-guide)");
|
|
262
|
-
expect(skillPage).not.toContain("](references/guide.md)");
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it("agent page has model badge", () => {
|
|
266
|
-
generateClaudeResourcesDocs({
|
|
267
|
-
claudeDir,
|
|
268
|
-
projectRoot: tmpDir,
|
|
269
|
-
docsDir,
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
const agentPage = fs.readFileSync(
|
|
273
|
-
path.join(docsDir, "claude-agents", "test-agent.mdx"),
|
|
274
|
-
"utf8",
|
|
275
|
-
);
|
|
276
|
-
expect(agentPage).toContain("**Model:** `sonnet`");
|
|
277
|
-
});
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
// ---------------------------------------------------------------------------
|
|
281
|
-
// Sub-file page tests
|
|
282
|
-
// ---------------------------------------------------------------------------
|
|
283
|
-
|
|
284
|
-
describe("sub-file pages", () => {
|
|
285
|
-
it("generates unlisted reference page as a nested file", () => {
|
|
286
|
-
generateClaudeResourcesDocs({
|
|
287
|
-
claudeDir,
|
|
288
|
-
projectRoot: tmpDir,
|
|
289
|
-
docsDir,
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
const refPage = path.join(docsDir, "claude-skills", "test-skill", "ref-guide.mdx");
|
|
293
|
-
expect(fs.existsSync(refPage)).toBe(true);
|
|
294
|
-
|
|
295
|
-
const parsed = matter(fs.readFileSync(refPage, "utf8"));
|
|
296
|
-
expect(parsed.data.unlisted).toBe(true);
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
it("generates unlisted asset page for .md files as a nested file", () => {
|
|
300
|
-
generateClaudeResourcesDocs({
|
|
301
|
-
claudeDir,
|
|
302
|
-
projectRoot: tmpDir,
|
|
303
|
-
docsDir,
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
const assetPage = path.join(docsDir, "claude-skills", "test-skill", "asset-template.mdx");
|
|
307
|
-
expect(fs.existsSync(assetPage)).toBe(true);
|
|
308
|
-
|
|
309
|
-
const parsed = matter(fs.readFileSync(assetPage, "utf8"));
|
|
310
|
-
expect(parsed.data.unlisted).toBe(true);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
it("does NOT generate page for non-.md scripts", () => {
|
|
314
|
-
generateClaudeResourcesDocs({
|
|
315
|
-
claudeDir,
|
|
316
|
-
projectRoot: tmpDir,
|
|
317
|
-
docsDir,
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
const scriptPage = path.join(docsDir, "claude-skills", "test-skill", "script-run.mdx");
|
|
321
|
-
expect(fs.existsSync(scriptPage)).toBe(false);
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
it("sub-pages derive their route from the file path (no explicit slug)", () => {
|
|
325
|
-
generateClaudeResourcesDocs({
|
|
326
|
-
claudeDir,
|
|
327
|
-
projectRoot: tmpDir,
|
|
328
|
-
docsDir,
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
// The page lives at claude-skills/test-skill/ref-guide.mdx, so its route
|
|
332
|
-
// is claude-skills/test-skill/ref-guide — nested breadcrumbs come from the
|
|
333
|
-
// file path, not an explicit slug (which zfb's link resolver ignores, #2411).
|
|
334
|
-
const refPage = fs.readFileSync(
|
|
335
|
-
path.join(docsDir, "claude-skills", "test-skill", "ref-guide.mdx"),
|
|
336
|
-
"utf8",
|
|
337
|
-
);
|
|
338
|
-
const parsed = matter(refPage);
|
|
339
|
-
expect(parsed.data.slug).toBeUndefined();
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
it("reference page content is correct", () => {
|
|
343
|
-
generateClaudeResourcesDocs({
|
|
344
|
-
claudeDir,
|
|
345
|
-
projectRoot: tmpDir,
|
|
346
|
-
docsDir,
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
const refPage = fs.readFileSync(
|
|
350
|
-
path.join(docsDir, "claude-skills", "test-skill", "ref-guide.mdx"),
|
|
351
|
-
"utf8",
|
|
352
|
-
);
|
|
353
|
-
const parsed = matter(refPage);
|
|
354
|
-
|
|
355
|
-
expect(parsed.data.title).toBe("Guide");
|
|
356
|
-
expect(parsed.content).toContain("Some guide content");
|
|
357
|
-
});
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
// ---------------------------------------------------------------------------
|
|
361
|
-
// Category metadata tests
|
|
362
|
-
// ---------------------------------------------------------------------------
|
|
363
|
-
|
|
364
|
-
describe("category metadata", () => {
|
|
365
|
-
it("index.mdx sidebar_position values are ordered correctly", () => {
|
|
366
|
-
generateClaudeResourcesDocs({
|
|
367
|
-
claudeDir,
|
|
368
|
-
projectRoot: tmpDir,
|
|
369
|
-
docsDir,
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
const readPos = (dir: string) => {
|
|
373
|
-
const parsed = matter(
|
|
374
|
-
fs.readFileSync(path.join(docsDir, dir, "index.mdx"), "utf8"),
|
|
375
|
-
);
|
|
376
|
-
return parsed.data.sidebar_position;
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
expect(readPos("claude-md")).toBe(900);
|
|
380
|
-
expect(readPos("claude-commands")).toBe(901);
|
|
381
|
-
expect(readPos("claude-skills")).toBe(902);
|
|
382
|
-
expect(readPos("claude-agents")).toBe(903);
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
it("index.mdx has correct label as title for each sub-category", () => {
|
|
386
|
-
generateClaudeResourcesDocs({
|
|
387
|
-
claudeDir,
|
|
388
|
-
projectRoot: tmpDir,
|
|
389
|
-
docsDir,
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
const readTitle = (dir: string) => {
|
|
393
|
-
const parsed = matter(
|
|
394
|
-
fs.readFileSync(path.join(docsDir, dir, "index.mdx"), "utf8"),
|
|
395
|
-
);
|
|
396
|
-
return parsed.data.title;
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
expect(readTitle("claude-md")).toBe("CLAUDE.md");
|
|
400
|
-
expect(readTitle("claude-commands")).toBe("Commands");
|
|
401
|
-
expect(readTitle("claude-skills")).toBe("Skills");
|
|
402
|
-
expect(readTitle("claude-agents")).toBe("Agents");
|
|
403
|
-
});
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
// ---------------------------------------------------------------------------
|
|
407
|
-
// Return value test
|
|
408
|
-
// ---------------------------------------------------------------------------
|
|
409
|
-
|
|
410
|
-
describe("return value", () => {
|
|
411
|
-
it("returns correct counts", () => {
|
|
412
|
-
const result = generateClaudeResourcesDocs({
|
|
413
|
-
claudeDir,
|
|
414
|
-
projectRoot: tmpDir,
|
|
415
|
-
docsDir,
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
expect(result).toEqual({
|
|
419
|
-
claudemd: 1,
|
|
420
|
-
commands: 1,
|
|
421
|
-
skills: 1,
|
|
422
|
-
agents: 1,
|
|
423
|
-
});
|
|
424
|
-
});
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
// ---------------------------------------------------------------------------
|
|
428
|
-
// Slug collision detection tests
|
|
429
|
-
// ---------------------------------------------------------------------------
|
|
430
|
-
|
|
431
|
-
describe("slug collision detection", () => {
|
|
432
|
-
it("throws when two CLAUDE.md paths produce the same slug", () => {
|
|
433
|
-
// foo/bar/CLAUDE.md → slug "foo--bar"
|
|
434
|
-
// foo--bar/CLAUDE.md → slug "foo--bar" (collision)
|
|
435
|
-
fs.mkdirSync(path.join(tmpDir, "foo", "bar"), { recursive: true });
|
|
436
|
-
fs.writeFileSync(
|
|
437
|
-
path.join(tmpDir, "foo", "bar", "CLAUDE.md"),
|
|
438
|
-
"# foo/bar instructions",
|
|
439
|
-
);
|
|
440
|
-
fs.mkdirSync(path.join(tmpDir, "foo--bar"), { recursive: true });
|
|
441
|
-
fs.writeFileSync(
|
|
442
|
-
path.join(tmpDir, "foo--bar", "CLAUDE.md"),
|
|
443
|
-
"# foo--bar instructions",
|
|
444
|
-
);
|
|
445
|
-
|
|
446
|
-
expect(() =>
|
|
447
|
-
generateClaudeResourcesDocs({
|
|
448
|
-
claudeDir,
|
|
449
|
-
projectRoot: tmpDir,
|
|
450
|
-
docsDir,
|
|
451
|
-
}),
|
|
452
|
-
).toThrow(/slug collision/);
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
it("names both colliding source paths in the error", () => {
|
|
456
|
-
fs.mkdirSync(path.join(tmpDir, "foo", "bar"), { recursive: true });
|
|
457
|
-
fs.writeFileSync(
|
|
458
|
-
path.join(tmpDir, "foo", "bar", "CLAUDE.md"),
|
|
459
|
-
"# foo/bar instructions",
|
|
460
|
-
);
|
|
461
|
-
fs.mkdirSync(path.join(tmpDir, "foo--bar"), { recursive: true });
|
|
462
|
-
fs.writeFileSync(
|
|
463
|
-
path.join(tmpDir, "foo--bar", "CLAUDE.md"),
|
|
464
|
-
"# foo--bar instructions",
|
|
465
|
-
);
|
|
466
|
-
|
|
467
|
-
let caughtMessage = "";
|
|
468
|
-
try {
|
|
469
|
-
generateClaudeResourcesDocs({
|
|
470
|
-
claudeDir,
|
|
471
|
-
projectRoot: tmpDir,
|
|
472
|
-
docsDir,
|
|
473
|
-
});
|
|
474
|
-
} catch (e) {
|
|
475
|
-
caughtMessage = (e as Error).message;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
expect(caughtMessage).toContain("foo--bar");
|
|
479
|
-
// Both source paths must appear in the message
|
|
480
|
-
expect(caughtMessage).toMatch(/foo.bar.CLAUDE\.md/);
|
|
481
|
-
expect(caughtMessage).toMatch(/foo--bar.CLAUDE\.md/);
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
it("does not throw for a clean tree (no collisions)", () => {
|
|
485
|
-
// The default fixture has only root/CLAUDE.md — no collision
|
|
486
|
-
expect(() =>
|
|
487
|
-
generateClaudeResourcesDocs({
|
|
488
|
-
claudeDir,
|
|
489
|
-
projectRoot: tmpDir,
|
|
490
|
-
docsDir,
|
|
491
|
-
}),
|
|
492
|
-
).not.toThrow();
|
|
493
|
-
});
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
// ---------------------------------------------------------------------------
|
|
497
|
-
// Reserved "index" slug guard tests
|
|
498
|
-
// ---------------------------------------------------------------------------
|
|
499
|
-
|
|
500
|
-
describe("reserved index slug guard", () => {
|
|
501
|
-
it("throws when a CLAUDE.md directory maps to the reserved index slug", () => {
|
|
502
|
-
// index/CLAUDE.md → slug "index" — reserved for category index.mdx
|
|
503
|
-
fs.mkdirSync(path.join(tmpDir, "index"), { recursive: true });
|
|
504
|
-
fs.writeFileSync(
|
|
505
|
-
path.join(tmpDir, "index", "CLAUDE.md"),
|
|
506
|
-
"# index dir instructions",
|
|
507
|
-
);
|
|
508
|
-
|
|
509
|
-
expect(() =>
|
|
510
|
-
generateClaudeResourcesDocs({
|
|
511
|
-
claudeDir,
|
|
512
|
-
projectRoot: tmpDir,
|
|
513
|
-
docsDir,
|
|
514
|
-
}),
|
|
515
|
-
).toThrow(/reserved slug "index"/);
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
it("throws when a command file is named index.md", () => {
|
|
519
|
-
fs.writeFileSync(
|
|
520
|
-
path.join(claudeDir, "commands", "index.md"),
|
|
521
|
-
'---\ndescription: "Index command"\n---\n\nIndex body.',
|
|
522
|
-
);
|
|
523
|
-
|
|
524
|
-
expect(() =>
|
|
525
|
-
generateClaudeResourcesDocs({
|
|
526
|
-
claudeDir,
|
|
527
|
-
projectRoot: tmpDir,
|
|
528
|
-
docsDir,
|
|
529
|
-
}),
|
|
530
|
-
).toThrow(/reserved name "index"/);
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
it("throws when a skill directory is named index", () => {
|
|
534
|
-
const indexSkillDir = path.join(claudeDir, "skills", "index");
|
|
535
|
-
fs.mkdirSync(indexSkillDir, { recursive: true });
|
|
536
|
-
fs.writeFileSync(
|
|
537
|
-
path.join(indexSkillDir, "SKILL.md"),
|
|
538
|
-
'---\nname: index\ndescription: "Index skill"\n---\n\nIndex skill body.',
|
|
539
|
-
);
|
|
540
|
-
|
|
541
|
-
expect(() =>
|
|
542
|
-
generateClaudeResourcesDocs({
|
|
543
|
-
claudeDir,
|
|
544
|
-
projectRoot: tmpDir,
|
|
545
|
-
docsDir,
|
|
546
|
-
}),
|
|
547
|
-
).toThrow(/reserved name "index"/);
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
it("throws when an agent file is named index.md", () => {
|
|
551
|
-
fs.writeFileSync(
|
|
552
|
-
path.join(claudeDir, "agents", "index.md"),
|
|
553
|
-
'---\nname: index-agent\ndescription: "Index agent"\nmodel: sonnet\n---\n\nIndex agent body.',
|
|
554
|
-
);
|
|
555
|
-
|
|
556
|
-
expect(() =>
|
|
557
|
-
generateClaudeResourcesDocs({
|
|
558
|
-
claudeDir,
|
|
559
|
-
projectRoot: tmpDir,
|
|
560
|
-
docsDir,
|
|
561
|
-
}),
|
|
562
|
-
).toThrow(/reserved name "index"/);
|
|
563
|
-
});
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
// ---------------------------------------------------------------------------
|
|
567
|
-
// projectRoot default scope regression test
|
|
568
|
-
// ---------------------------------------------------------------------------
|
|
569
|
-
|
|
570
|
-
describe("projectRoot default scope", () => {
|
|
571
|
-
it("walks claudeDir (not its parent) when projectRoot is omitted", () => {
|
|
572
|
-
// The fixture already writes tmpDir/CLAUDE.md (outside claudeDir).
|
|
573
|
-
// With the old bug (projectRoot = path.dirname(claudeDir) = tmpDir), the
|
|
574
|
-
// walk starts at tmpDir and picks up tmpDir/CLAUDE.md. With the fix
|
|
575
|
-
// (projectRoot = claudeDir), the walk starts inside claudeDir and does NOT
|
|
576
|
-
// reach tmpDir/CLAUDE.md (parent dirs are never walked upward).
|
|
577
|
-
//
|
|
578
|
-
// To distinguish, we write a CLAUDE.md inside claudeDir with unique content
|
|
579
|
-
// and verify the generated page body matches IT, not the outside one.
|
|
580
|
-
fs.writeFileSync(
|
|
581
|
-
path.join(claudeDir, "CLAUDE.md"),
|
|
582
|
-
"# Claude dir unique content xyz123",
|
|
583
|
-
);
|
|
584
|
-
|
|
585
|
-
// Call with NO projectRoot.
|
|
586
|
-
generateClaudeResourcesDocs({ claudeDir, docsDir });
|
|
587
|
-
|
|
588
|
-
// The generated root.mdx body must contain the inside-claudeDir content.
|
|
589
|
-
const claudeMdDir = path.join(docsDir, "claude-md");
|
|
590
|
-
const rootMdx = fs.readFileSync(path.join(claudeMdDir, "root.mdx"), "utf8");
|
|
591
|
-
expect(rootMdx).toContain("xyz123");
|
|
592
|
-
|
|
593
|
-
// And must NOT contain the outside content (from tmpDir/CLAUDE.md).
|
|
594
|
-
expect(rootMdx).not.toContain("Project instructions");
|
|
595
|
-
});
|
|
596
|
-
});
|
|
597
|
-
|
|
598
|
-
// ---------------------------------------------------------------------------
|
|
599
|
-
// excludeDirs boundary matching (#2561)
|
|
600
|
-
// ---------------------------------------------------------------------------
|
|
601
|
-
|
|
602
|
-
describe("excludeDirs boundary matching", () => {
|
|
603
|
-
it("discovers CLAUDE.md in a prefix-colliding sibling of an excluded dir (dist-extra vs dist)", () => {
|
|
604
|
-
fs.mkdirSync(path.join(tmpDir, "dist-extra"), { recursive: true });
|
|
605
|
-
fs.writeFileSync(
|
|
606
|
-
path.join(tmpDir, "dist-extra", "CLAUDE.md"),
|
|
607
|
-
"# dist-extra instructions",
|
|
608
|
-
);
|
|
609
|
-
|
|
610
|
-
const result = generateClaudeResourcesDocs({
|
|
611
|
-
claudeDir,
|
|
612
|
-
projectRoot: tmpDir,
|
|
613
|
-
docsDir,
|
|
614
|
-
});
|
|
615
|
-
|
|
616
|
-
// root/CLAUDE.md (from the fixture) + dist-extra/CLAUDE.md
|
|
617
|
-
expect(result.claudemd).toBe(2);
|
|
618
|
-
const decoyPage = path.join(docsDir, "claude-md", "dist-extra.mdx");
|
|
619
|
-
expect(fs.existsSync(decoyPage)).toBe(true);
|
|
620
|
-
expect(fs.readFileSync(decoyPage, "utf8")).toContain("dist-extra instructions");
|
|
621
|
-
});
|
|
622
|
-
|
|
623
|
-
it("still excludes CLAUDE.md directly inside the excluded dist directory", () => {
|
|
624
|
-
fs.mkdirSync(path.join(tmpDir, "dist"), { recursive: true });
|
|
625
|
-
fs.writeFileSync(
|
|
626
|
-
path.join(tmpDir, "dist", "CLAUDE.md"),
|
|
627
|
-
"# dist instructions — should be excluded",
|
|
628
|
-
);
|
|
629
|
-
|
|
630
|
-
const result = generateClaudeResourcesDocs({
|
|
631
|
-
claudeDir,
|
|
632
|
-
projectRoot: tmpDir,
|
|
633
|
-
docsDir,
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
// Only root/CLAUDE.md (from the fixture) — dist/CLAUDE.md is excluded.
|
|
637
|
-
expect(result.claudemd).toBe(1);
|
|
638
|
-
expect(fs.existsSync(path.join(docsDir, "claude-md", "dist.mdx"))).toBe(false);
|
|
639
|
-
});
|
|
640
|
-
|
|
641
|
-
it("still excludes nested CLAUDE.md files under the excluded dist directory (exact-match boundary holds)", () => {
|
|
642
|
-
fs.mkdirSync(path.join(tmpDir, "dist", "nested"), { recursive: true });
|
|
643
|
-
fs.writeFileSync(
|
|
644
|
-
path.join(tmpDir, "dist", "nested", "CLAUDE.md"),
|
|
645
|
-
"# nested dist instructions — should be excluded",
|
|
646
|
-
);
|
|
647
|
-
|
|
648
|
-
const result = generateClaudeResourcesDocs({
|
|
649
|
-
claudeDir,
|
|
650
|
-
projectRoot: tmpDir,
|
|
651
|
-
docsDir,
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
// Only root/CLAUDE.md (from the fixture) — the whole dist/ subtree,
|
|
655
|
-
// including nested dirs, is excluded once the top-level dist match hits.
|
|
656
|
-
expect(result.claudemd).toBe(1);
|
|
657
|
-
expect(fs.existsSync(path.join(docsDir, "claude-md", "dist--nested.mdx"))).toBe(false);
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
it("still excludes docsDir contents when docsDir carries a trailing separator", () => {
|
|
661
|
-
// path.join preserves a trailing separator on a single argument, so the
|
|
662
|
-
// docsDir exclude entry keeps it — the boundary compare must not break.
|
|
663
|
-
fs.writeFileSync(
|
|
664
|
-
path.join(docsDir, "CLAUDE.md"),
|
|
665
|
-
"# decoy inside docsDir — should be excluded",
|
|
666
|
-
);
|
|
667
|
-
|
|
668
|
-
const result = generateClaudeResourcesDocs({
|
|
669
|
-
claudeDir,
|
|
670
|
-
projectRoot: tmpDir,
|
|
671
|
-
docsDir: docsDir + path.sep,
|
|
672
|
-
});
|
|
673
|
-
|
|
674
|
-
// Only root/CLAUDE.md (from the fixture) — docs/CLAUDE.md is excluded.
|
|
675
|
-
expect(result.claudemd).toBe(1);
|
|
676
|
-
expect(fs.existsSync(path.join(docsDir, "claude-md", "docs.mdx"))).toBe(false);
|
|
677
|
-
});
|
|
678
|
-
});
|
|
679
|
-
|
|
680
|
-
// ---------------------------------------------------------------------------
|
|
681
|
-
// CLAUDE.md repo-relative link downgrade (#2411, Class B)
|
|
682
|
-
// ---------------------------------------------------------------------------
|
|
683
|
-
|
|
684
|
-
describe("CLAUDE.md repo-relative link downgrade", () => {
|
|
685
|
-
const claudeMdBody = [
|
|
686
|
-
"# Project",
|
|
687
|
-
"",
|
|
688
|
-
"See [wrangler.toml](./wrangler.toml) and [schema](../../schema/photos.sql).",
|
|
689
|
-
"Also a [workflow](../../.github/workflows/deploy.yml) and a [bare](docs/guide.md) link.",
|
|
690
|
-
"External [site](https://example.com), [anchor](#section), [site-abs](/docs/getting-started), [mail](mailto:x@y.com).",
|
|
691
|
-
"Image: ",
|
|
692
|
-
"",
|
|
693
|
-
"Inline code stays: `[x](./y)`",
|
|
694
|
-
"",
|
|
695
|
-
"```",
|
|
696
|
-
"[code](./should-not-change.md)",
|
|
697
|
-
"```",
|
|
698
|
-
"",
|
|
699
|
-
"~~~",
|
|
700
|
-
"[tilde-code](./also-should-not-change.md)",
|
|
701
|
-
"~~~",
|
|
702
|
-
"",
|
|
703
|
-
].join("\n");
|
|
704
|
-
|
|
705
|
-
function genRoot() {
|
|
706
|
-
fs.writeFileSync(path.join(tmpDir, "CLAUDE.md"), claudeMdBody);
|
|
707
|
-
generateClaudeResourcesDocs({ claudeDir, projectRoot: tmpDir, docsDir });
|
|
708
|
-
return fs.readFileSync(
|
|
709
|
-
path.join(docsDir, "claude-md", "root.mdx"),
|
|
710
|
-
"utf8",
|
|
711
|
-
);
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
it("downgrades repo-relative file links to inline code", () => {
|
|
715
|
-
const rootMdx = genRoot();
|
|
716
|
-
expect(rootMdx).toContain("`wrangler.toml`");
|
|
717
|
-
expect(rootMdx).toContain("`schema`");
|
|
718
|
-
expect(rootMdx).toContain("`workflow`");
|
|
719
|
-
expect(rootMdx).toContain("`bare`");
|
|
720
|
-
// The dangling hrefs must be gone.
|
|
721
|
-
expect(rootMdx).not.toContain("](./wrangler.toml)");
|
|
722
|
-
expect(rootMdx).not.toContain("](../../schema/photos.sql)");
|
|
723
|
-
expect(rootMdx).not.toContain("](../../.github/workflows/deploy.yml)");
|
|
724
|
-
expect(rootMdx).not.toContain("](docs/guide.md)");
|
|
725
|
-
});
|
|
726
|
-
|
|
727
|
-
it("preserves resolvable links (external, anchor, site-absolute, scheme)", () => {
|
|
728
|
-
const rootMdx = genRoot();
|
|
729
|
-
expect(rootMdx).toContain("](https://example.com)");
|
|
730
|
-
expect(rootMdx).toContain("](#section)");
|
|
731
|
-
expect(rootMdx).toContain("](/docs/getting-started)");
|
|
732
|
-
expect(rootMdx).toContain("](mailto:x@y.com)");
|
|
733
|
-
});
|
|
734
|
-
|
|
735
|
-
it("downgrades repo-relative image links without leaving a stray '!'", () => {
|
|
736
|
-
const rootMdx = genRoot();
|
|
737
|
-
expect(rootMdx).toContain("`diagram`");
|
|
738
|
-
expect(rootMdx).not.toContain("](./diagram.png)");
|
|
739
|
-
expect(rootMdx).not.toContain("!`diagram`");
|
|
740
|
-
});
|
|
741
|
-
|
|
742
|
-
it("does not rewrite links inside inline code or fenced code blocks", () => {
|
|
743
|
-
const rootMdx = genRoot();
|
|
744
|
-
// Inline-code span is preserved verbatim.
|
|
745
|
-
expect(rootMdx).toContain("`[x](./y)`");
|
|
746
|
-
// Backtick-fenced code block content is preserved verbatim.
|
|
747
|
-
expect(rootMdx).toContain("[code](./should-not-change.md)");
|
|
748
|
-
// Tilde-fenced code block content is preserved verbatim too.
|
|
749
|
-
expect(rootMdx).toContain("[tilde-code](./also-should-not-change.md)");
|
|
750
|
-
});
|
|
751
|
-
});
|
|
752
|
-
});
|