sourcey 3.5.10 → 3.6.1
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/README.md +24 -97
- package/dist/adapters/index.d.ts +12 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +130 -0
- package/dist/adapters/mkdocs.d.ts +5 -0
- package/dist/adapters/mkdocs.d.ts.map +1 -0
- package/dist/adapters/mkdocs.js +367 -0
- package/dist/adapters/shared.d.ts +12 -0
- package/dist/adapters/shared.d.ts.map +1 -0
- package/dist/adapters/shared.js +134 -0
- package/dist/adapters/types.d.ts +74 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +1 -0
- package/dist/cli.js +28 -34
- package/dist/client/search.js +213 -82
- package/dist/client/tabs.js +23 -0
- package/dist/components/layout/Head.js +17 -4
- package/dist/components/layout/TableOfContents.d.ts.map +1 -1
- package/dist/components/layout/TableOfContents.js +4 -2
- package/dist/components/openapi/Introduction.d.ts.map +1 -1
- package/dist/components/openapi/Introduction.js +16 -1
- package/dist/components/openapi/Security.d.ts.map +1 -1
- package/dist/components/openapi/Security.js +18 -2
- package/dist/config.d.ts +101 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +95 -143
- package/dist/core/api-rendering.d.ts +47 -0
- package/dist/core/api-rendering.d.ts.map +1 -0
- package/dist/core/api-rendering.js +76 -0
- package/dist/core/doxygen-loader.d.ts.map +1 -1
- package/dist/core/doxygen-loader.js +36 -17
- package/dist/core/godoc-loader.d.ts.map +1 -1
- package/dist/core/godoc-loader.js +17 -6
- package/dist/core/markdown-loader.d.ts +14 -0
- package/dist/core/markdown-loader.d.ts.map +1 -1
- package/dist/core/markdown-loader.js +70 -26
- package/dist/core/mcp-normalizer.d.ts.map +1 -1
- package/dist/core/mcp-normalizer.js +48 -34
- package/dist/core/navigation.d.ts.map +1 -1
- package/dist/core/navigation.js +8 -7
- package/dist/core/parser.d.ts.map +1 -1
- package/dist/core/parser.js +75 -4
- package/dist/core/rustdoc-introspector.d.ts +29 -0
- package/dist/core/rustdoc-introspector.d.ts.map +1 -0
- package/dist/core/rustdoc-introspector.js +236 -0
- package/dist/core/rustdoc-loader.d.ts +25 -0
- package/dist/core/rustdoc-loader.d.ts.map +1 -0
- package/dist/core/rustdoc-loader.js +469 -0
- package/dist/core/rustdoc-render.d.ts +76 -0
- package/dist/core/rustdoc-render.d.ts.map +1 -0
- package/dist/core/rustdoc-render.js +785 -0
- package/dist/core/rustdoc-types.d.ts +314 -0
- package/dist/core/rustdoc-types.d.ts.map +1 -0
- package/dist/core/rustdoc-types.js +18 -0
- package/dist/core/search-indexer.d.ts +10 -0
- package/dist/core/search-indexer.d.ts.map +1 -1
- package/dist/core/search-indexer.js +6 -1
- package/dist/core/sourcey-rustdoc/Cargo.lock +705 -0
- package/dist/core/sourcey-rustdoc/Cargo.toml +38 -0
- package/dist/core/sourcey-rustdoc/README.md +59 -0
- package/dist/core/sourcey-rustdoc/src/diagnostics.rs +42 -0
- package/dist/core/sourcey-rustdoc/src/doctest.rs +268 -0
- package/dist/core/sourcey-rustdoc/src/extract.rs +492 -0
- package/dist/core/sourcey-rustdoc/src/lib.rs +58 -0
- package/dist/core/sourcey-rustdoc/src/links.rs +126 -0
- package/dist/core/sourcey-rustdoc/src/main.rs +256 -0
- package/dist/core/sourcey-rustdoc/src/signature.rs +415 -0
- package/dist/core/sourcey-rustdoc/src/spec.rs +390 -0
- package/dist/core/sourcey-rustdoc/tests/format_version.rs +45 -0
- package/dist/dev-server.d.ts.map +1 -1
- package/dist/dev-server.js +109 -33
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -14
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +21 -16
- package/dist/renderer/changelog-feed.d.ts.map +1 -1
- package/dist/renderer/changelog-feed.js +27 -19
- package/dist/renderer/html-builder.d.ts.map +1 -1
- package/dist/renderer/html-builder.js +4 -3
- package/dist/renderer/llms.js +57 -2
- package/dist/site-assembly.d.ts +5 -0
- package/dist/site-assembly.d.ts.map +1 -1
- package/dist/site-assembly.js +161 -25
- package/dist/themes/default/sourcey.css +300 -0
- package/dist/utils/code-samples.d.ts +1 -0
- package/dist/utils/code-samples.d.ts.map +1 -1
- package/dist/utils/code-samples.js +30 -22
- package/dist/utils/html.d.ts +10 -0
- package/dist/utils/html.d.ts.map +1 -0
- package/dist/utils/html.js +152 -0
- package/dist/utils/http.d.ts +0 -2
- package/dist/utils/http.d.ts.map +1 -1
- package/dist/utils/http.js +19 -34
- package/dist/utils/markdown.d.ts.map +1 -1
- package/dist/utils/markdown.js +115 -39
- package/dist/vite-plugin.d.ts +2 -0
- package/dist/vite-plugin.d.ts.map +1 -1
- package/dist/vite-plugin.js +71 -1
- package/package.json +11 -5
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { access, readFile, readdir } from "node:fs/promises";
|
|
3
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { load as parseYaml } from "js-yaml";
|
|
5
|
+
import { assertLocalPath, collectStaticAssets } from "./shared.js";
|
|
6
|
+
export function mkdocs(options) {
|
|
7
|
+
return {
|
|
8
|
+
name: "mkdocs",
|
|
9
|
+
async resolve(ctx) {
|
|
10
|
+
const configPath = assertLocalPath(ctx, options.config, "MkDocs config");
|
|
11
|
+
await ctx.assertExists(configPath, `MkDocs config "${options.config}" in tab "${ctx.tabName}"`);
|
|
12
|
+
const { docsDir, groups } = await resolveMkDocsGroups(configPath);
|
|
13
|
+
return {
|
|
14
|
+
kind: "markdown",
|
|
15
|
+
adapter: "mkdocs",
|
|
16
|
+
configPath,
|
|
17
|
+
groups,
|
|
18
|
+
assets: await collectStaticAssets(docsDir),
|
|
19
|
+
watchPaths: [configPath, docsDir],
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async function resolveMkDocsGroups(configPath) {
|
|
25
|
+
const raw = await readFile(configPath, "utf-8");
|
|
26
|
+
const parsed = parseYaml(sanitizeMkDocsYaml(raw));
|
|
27
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
28
|
+
throw new Error(`MkDocs config must be a YAML object: ${configPath}`);
|
|
29
|
+
}
|
|
30
|
+
const configDir = dirname(configPath);
|
|
31
|
+
const docsDirName = typeof parsed.docs_dir === "string" && parsed.docs_dir.trim() ? parsed.docs_dir.trim() : "docs";
|
|
32
|
+
const docsDir = resolve(configDir, docsDirName);
|
|
33
|
+
const nav = parsed.nav ?? parsed.pages;
|
|
34
|
+
const groups = Array.isArray(nav)
|
|
35
|
+
? mkDocsGroupsFromNav(nav, docsDir)
|
|
36
|
+
: [{ label: "Pages", pages: await discoverMkDocsPages(docsDir) }];
|
|
37
|
+
const nonEmptyGroups = groups.filter((group) => group.pages.length > 0);
|
|
38
|
+
if (nonEmptyGroups.length === 0) {
|
|
39
|
+
throw new Error(`MkDocs config did not resolve any markdown pages: ${configPath}`);
|
|
40
|
+
}
|
|
41
|
+
for (const group of nonEmptyGroups) {
|
|
42
|
+
for (const page of group.pages) {
|
|
43
|
+
await access(page.file).catch(() => {
|
|
44
|
+
throw new Error(`MkDocs nav page "${relative(docsDir, page.file).replace(/\\/g, "/")}" not found`);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { docsDir, groups: nonEmptyGroups };
|
|
49
|
+
}
|
|
50
|
+
function sanitizeMkDocsYaml(raw) {
|
|
51
|
+
return raw.replace(/!!python\/name:[^\s\]]+/g, "").replace(/!ENV\b/g, "");
|
|
52
|
+
}
|
|
53
|
+
function mkDocsGroupsFromNav(nav, docsDir) {
|
|
54
|
+
const groups = [];
|
|
55
|
+
const loosePages = [];
|
|
56
|
+
const seenPaths = new Set();
|
|
57
|
+
for (const entry of nav) {
|
|
58
|
+
if (typeof entry === "string") {
|
|
59
|
+
const page = resolveMkDocsNavPage({ path: entry }, docsDir, seenPaths);
|
|
60
|
+
if (page)
|
|
61
|
+
loosePages.push(page);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (!isPlainRecord(entry))
|
|
65
|
+
continue;
|
|
66
|
+
for (const [label, value] of Object.entries(entry)) {
|
|
67
|
+
if (typeof value === "string") {
|
|
68
|
+
const page = resolveMkDocsNavPage({ path: value, label }, docsDir, seenPaths);
|
|
69
|
+
if (page)
|
|
70
|
+
loosePages.push(page);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const pages = collectMkDocsNavPages(value, [])
|
|
74
|
+
.map((page) => resolveMkDocsNavPage(page, docsDir, seenPaths))
|
|
75
|
+
.filter((page) => Boolean(page));
|
|
76
|
+
if (pages.length > 0) {
|
|
77
|
+
groups.push({ label, pages });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return loosePages.length > 0 ? [{ label: "Pages", pages: loosePages }, ...groups] : groups;
|
|
82
|
+
}
|
|
83
|
+
function collectMkDocsNavPages(value, labelParts) {
|
|
84
|
+
if (typeof value === "string") {
|
|
85
|
+
return [
|
|
86
|
+
{
|
|
87
|
+
path: value,
|
|
88
|
+
label: isMkDocsIndexPage(value) ? labelParts.join(" / ") || undefined : undefined,
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
if (Array.isArray(value)) {
|
|
93
|
+
return value.flatMap((entry) => collectMkDocsNavPages(entry, labelParts));
|
|
94
|
+
}
|
|
95
|
+
if (!isPlainRecord(value)) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
return Object.entries(value).flatMap(([label, nested]) => {
|
|
99
|
+
if (typeof nested === "string") {
|
|
100
|
+
return [
|
|
101
|
+
{
|
|
102
|
+
path: nested,
|
|
103
|
+
label: [...labelParts, label].join(" / "),
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
return collectMkDocsNavPages(nested, [...labelParts, label]);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function resolveMkDocsNavPage(page, docsDir, seenPaths) {
|
|
111
|
+
const navPath = normalizeMkDocsPagePath(page.path);
|
|
112
|
+
if (!navPath || seenPaths.has(navPath)) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const file = resolveWithinRoot(docsDir, navPath);
|
|
116
|
+
if (!file) {
|
|
117
|
+
throw new Error(`MkDocs nav page "${page.path}" escapes docs_dir: ${docsDir}`);
|
|
118
|
+
}
|
|
119
|
+
seenPaths.add(navPath);
|
|
120
|
+
return {
|
|
121
|
+
slug: slugFromMkDocsPath(navPath),
|
|
122
|
+
file,
|
|
123
|
+
label: page.label,
|
|
124
|
+
sourceRoot: docsDir,
|
|
125
|
+
preprocess: [preprocessMkDocsMarkdown],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function normalizeMkDocsPagePath(value) {
|
|
129
|
+
const text = value.trim();
|
|
130
|
+
if (!text || isExternalNavTarget(text) || !/\.(?:md|mdx)$/i.test(text)) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
return text.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
134
|
+
}
|
|
135
|
+
function isExternalNavTarget(value) {
|
|
136
|
+
return /^(?:https?:|mailto:|tel:|#)/i.test(value);
|
|
137
|
+
}
|
|
138
|
+
function slugFromMkDocsPath(value) {
|
|
139
|
+
return value.replace(/\.(?:md|mdx)$/i, "");
|
|
140
|
+
}
|
|
141
|
+
function isMkDocsIndexPage(value) {
|
|
142
|
+
return /(^|\/)index\.(?:md|mdx)$/i.test(value.trim());
|
|
143
|
+
}
|
|
144
|
+
async function discoverMkDocsPages(docsDir) {
|
|
145
|
+
const pages = [];
|
|
146
|
+
async function walk(dir) {
|
|
147
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
148
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
149
|
+
if (entry.name.startsWith("."))
|
|
150
|
+
continue;
|
|
151
|
+
const absolutePath = join(dir, entry.name);
|
|
152
|
+
if (entry.isDirectory()) {
|
|
153
|
+
await walk(absolutePath);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (!entry.isFile() || !/\.(?:md|mdx)$/i.test(entry.name))
|
|
157
|
+
continue;
|
|
158
|
+
const relPath = relative(docsDir, absolutePath).replace(/\\/g, "/");
|
|
159
|
+
pages.push({
|
|
160
|
+
slug: slugFromMkDocsPath(relPath),
|
|
161
|
+
file: absolutePath,
|
|
162
|
+
sourceRoot: docsDir,
|
|
163
|
+
preprocess: [preprocessMkDocsMarkdown],
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
await walk(docsDir);
|
|
168
|
+
return pages;
|
|
169
|
+
}
|
|
170
|
+
export const preprocessMkDocsMarkdown = (body, context) => {
|
|
171
|
+
if (!context.sourceRoot)
|
|
172
|
+
return body;
|
|
173
|
+
return normalizeMkDocsAdmonitions(normalizeMkDocsTabs(escapeMkDocsCppTemplateAngles(stripMkDocsInlineLanguageMarkers(expandMkDocsSnippets(body, context.filePath, context.sourceRoot)))));
|
|
174
|
+
};
|
|
175
|
+
function expandMkDocsSnippets(body, filePath, sourceRoot) {
|
|
176
|
+
return body
|
|
177
|
+
.split("\n")
|
|
178
|
+
.map((line) => {
|
|
179
|
+
const match = line.match(/^(\s*)--8<--\s+"([^"]+)"\s*$/);
|
|
180
|
+
if (!match)
|
|
181
|
+
return line;
|
|
182
|
+
const [, indent, includePath] = match;
|
|
183
|
+
const included = readMkDocsSnippet(includePath, filePath, sourceRoot);
|
|
184
|
+
if (included === undefined)
|
|
185
|
+
return line;
|
|
186
|
+
return included
|
|
187
|
+
.replace(/\r\n?/g, "\n")
|
|
188
|
+
.split("\n")
|
|
189
|
+
.map((includedLine) => (includedLine ? `${indent}${includedLine}` : includedLine))
|
|
190
|
+
.join("\n");
|
|
191
|
+
})
|
|
192
|
+
.join("\n");
|
|
193
|
+
}
|
|
194
|
+
function readMkDocsSnippet(includePath, filePath, sourceRoot) {
|
|
195
|
+
const candidates = uniquePaths([
|
|
196
|
+
isAbsolute(includePath) ? includePath : resolve(sourceRoot, includePath),
|
|
197
|
+
isAbsolute(includePath) ? includePath : resolve(dirname(filePath), includePath),
|
|
198
|
+
]);
|
|
199
|
+
const safeCandidates = candidates.filter((candidate) => isWithinRoot(sourceRoot, candidate));
|
|
200
|
+
if (safeCandidates.length === 0) {
|
|
201
|
+
throw new Error(`MkDocs snippet "${includePath}" escapes docs_dir: ${sourceRoot}`);
|
|
202
|
+
}
|
|
203
|
+
for (const candidate of safeCandidates) {
|
|
204
|
+
try {
|
|
205
|
+
return readFileSync(candidate, "utf-8");
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
if (err.code !== "ENOENT")
|
|
209
|
+
throw err;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
function stripMkDocsInlineLanguageMarkers(body) {
|
|
215
|
+
return body
|
|
216
|
+
.replace(/`#!([A-Za-z0-9_+.-]+)\s+([^`]+)`/g, "`$2`")
|
|
217
|
+
.replace(/`#!([A-Za-z0-9_+.-]+)\s*`/g, "");
|
|
218
|
+
}
|
|
219
|
+
function escapeMkDocsCppTemplateAngles(body) {
|
|
220
|
+
return body.replace(/<([A-Za-z_][A-Za-z0-9_:]*)(\\?)>/g, (match, name, escaped) => {
|
|
221
|
+
if (!escaped && HTML_TAG_NAMES.has(name.toLowerCase()))
|
|
222
|
+
return match;
|
|
223
|
+
return `<${name}>`;
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
const HTML_TAG_NAMES = new Set([
|
|
227
|
+
"a",
|
|
228
|
+
"abbr",
|
|
229
|
+
"article",
|
|
230
|
+
"aside",
|
|
231
|
+
"b",
|
|
232
|
+
"blockquote",
|
|
233
|
+
"br",
|
|
234
|
+
"button",
|
|
235
|
+
"caption",
|
|
236
|
+
"code",
|
|
237
|
+
"dd",
|
|
238
|
+
"del",
|
|
239
|
+
"details",
|
|
240
|
+
"div",
|
|
241
|
+
"dl",
|
|
242
|
+
"dt",
|
|
243
|
+
"em",
|
|
244
|
+
"figcaption",
|
|
245
|
+
"figure",
|
|
246
|
+
"h1",
|
|
247
|
+
"h2",
|
|
248
|
+
"h3",
|
|
249
|
+
"h4",
|
|
250
|
+
"h5",
|
|
251
|
+
"h6",
|
|
252
|
+
"hr",
|
|
253
|
+
"i",
|
|
254
|
+
"iframe",
|
|
255
|
+
"img",
|
|
256
|
+
"input",
|
|
257
|
+
"ins",
|
|
258
|
+
"kbd",
|
|
259
|
+
"li",
|
|
260
|
+
"mark",
|
|
261
|
+
"ol",
|
|
262
|
+
"p",
|
|
263
|
+
"pre",
|
|
264
|
+
"s",
|
|
265
|
+
"section",
|
|
266
|
+
"small",
|
|
267
|
+
"span",
|
|
268
|
+
"strong",
|
|
269
|
+
"sub",
|
|
270
|
+
"summary",
|
|
271
|
+
"sup",
|
|
272
|
+
"table",
|
|
273
|
+
"tbody",
|
|
274
|
+
"td",
|
|
275
|
+
"th",
|
|
276
|
+
"thead",
|
|
277
|
+
"tr",
|
|
278
|
+
"u",
|
|
279
|
+
"ul",
|
|
280
|
+
]);
|
|
281
|
+
function normalizeMkDocsTabs(body) {
|
|
282
|
+
const lines = body.split("\n");
|
|
283
|
+
const out = [];
|
|
284
|
+
for (let i = 0; i < lines.length;) {
|
|
285
|
+
const tab = lines[i].match(/^(\s*)===\s+"([^"]+)"\s*$/);
|
|
286
|
+
if (!tab) {
|
|
287
|
+
out.push(lines[i]);
|
|
288
|
+
i += 1;
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
const [, indent, title] = tab;
|
|
292
|
+
out.push(`${indent}**${title}**`);
|
|
293
|
+
i += 1;
|
|
294
|
+
while (i < lines.length) {
|
|
295
|
+
if (lines[i].match(new RegExp(`^${escapeRegExp(indent)}===\\s+"[^"]+"\\s*$`))) {
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
if (/^\s*$/.test(lines[i])) {
|
|
299
|
+
out.push(lines[i]);
|
|
300
|
+
i += 1;
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (!lines[i].startsWith(`${indent} `)) {
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
out.push(indent + lines[i].slice(indent.length + 4));
|
|
307
|
+
i += 1;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return out.join("\n");
|
|
311
|
+
}
|
|
312
|
+
function normalizeMkDocsAdmonitions(body) {
|
|
313
|
+
const lines = body.split("\n");
|
|
314
|
+
const out = [];
|
|
315
|
+
for (let i = 0; i < lines.length;) {
|
|
316
|
+
const admonition = lines[i].match(/^(\s*)(!!!|\?\?\?)\s+([A-Za-z0-9_-]+)(?:\s+"([^"]+)")?\s*$/);
|
|
317
|
+
if (!admonition) {
|
|
318
|
+
out.push(lines[i]);
|
|
319
|
+
i += 1;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
const [, indent, , type, title] = admonition;
|
|
323
|
+
out.push(`${indent}> **${formatMkDocsAdmonitionTitle(type, title)}**`);
|
|
324
|
+
i += 1;
|
|
325
|
+
const content = [];
|
|
326
|
+
while (i < lines.length) {
|
|
327
|
+
if (/^\s*$/.test(lines[i])) {
|
|
328
|
+
content.push("");
|
|
329
|
+
i += 1;
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
if (!lines[i].startsWith(`${indent} `)) {
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
content.push(lines[i].slice(indent.length + 4));
|
|
336
|
+
i += 1;
|
|
337
|
+
}
|
|
338
|
+
for (const line of normalizeMkDocsAdmonitions(content.join("\n")).split("\n")) {
|
|
339
|
+
out.push(line ? `${indent}> ${line}` : `${indent}>`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return out.join("\n");
|
|
343
|
+
}
|
|
344
|
+
function formatMkDocsAdmonitionTitle(type, title) {
|
|
345
|
+
if (title?.trim())
|
|
346
|
+
return title.trim();
|
|
347
|
+
return type.slice(0, 1).toUpperCase() + type.slice(1).replace(/-/g, " ");
|
|
348
|
+
}
|
|
349
|
+
function escapeRegExp(value) {
|
|
350
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
351
|
+
}
|
|
352
|
+
function isPlainRecord(value) {
|
|
353
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
354
|
+
}
|
|
355
|
+
function resolveWithinRoot(root, path) {
|
|
356
|
+
const candidate = resolve(root, path);
|
|
357
|
+
return isWithinRoot(root, candidate) ? candidate : undefined;
|
|
358
|
+
}
|
|
359
|
+
function isWithinRoot(root, candidate) {
|
|
360
|
+
const rootAbs = resolve(root);
|
|
361
|
+
const candidateAbs = resolve(candidate);
|
|
362
|
+
const rel = relative(rootAbs, candidateAbs);
|
|
363
|
+
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
|
|
364
|
+
}
|
|
365
|
+
function uniquePaths(paths) {
|
|
366
|
+
return [...new Set(paths)];
|
|
367
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ResolvedGroup, ResolvedPage } from "../config.js";
|
|
2
|
+
import type { PageMarkdownOptions, ResolvedSourceAsset, SourceAdapterContext } from "./types.js";
|
|
3
|
+
export declare const STATIC_ASSET_EXTENSIONS: Set<string>;
|
|
4
|
+
export declare function resolveMarkdownGroups(groups: {
|
|
5
|
+
group: string;
|
|
6
|
+
pages: string[];
|
|
7
|
+
}[], tabName: string, configDir: string, markdown?: PageMarkdownOptions): Promise<ResolvedGroup[]>;
|
|
8
|
+
export declare function collectStaticAssets(sourceRoot: string): Promise<ResolvedSourceAsset[]>;
|
|
9
|
+
export declare function toWatchPaths(paths: Array<string | undefined>): string[];
|
|
10
|
+
export declare function assertLocalPath(ctx: SourceAdapterContext, source: string, label: string): string;
|
|
11
|
+
export declare function outputSlugForPage(page: ResolvedPage): string;
|
|
12
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/adapters/shared.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEhE,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEjG,eAAO,MAAM,uBAAuB,aAsBlC,CAAC;AAEH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,EAAE,EAC5C,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,mBAAmB,GAC7B,OAAO,CAAC,aAAa,EAAE,CAAC,CA0B1B;AAED,wBAAsB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAuB5F;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,EAAE,CAEvE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKhG;AA0DD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAE5D"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { access, readdir } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, extname, join, relative, resolve } from "node:path";
|
|
3
|
+
import { slugFromPath } from "../core/markdown-loader.js";
|
|
4
|
+
export const STATIC_ASSET_EXTENSIONS = new Set([
|
|
5
|
+
".apng",
|
|
6
|
+
".avif",
|
|
7
|
+
".bmp",
|
|
8
|
+
".css",
|
|
9
|
+
".gif",
|
|
10
|
+
".ico",
|
|
11
|
+
".jpeg",
|
|
12
|
+
".jpg",
|
|
13
|
+
".js",
|
|
14
|
+
".json",
|
|
15
|
+
".mjs",
|
|
16
|
+
".mp4",
|
|
17
|
+
".pdf",
|
|
18
|
+
".png",
|
|
19
|
+
".svg",
|
|
20
|
+
".txt",
|
|
21
|
+
".webm",
|
|
22
|
+
".webp",
|
|
23
|
+
".woff",
|
|
24
|
+
".woff2",
|
|
25
|
+
".zip",
|
|
26
|
+
]);
|
|
27
|
+
export async function resolveMarkdownGroups(groups, tabName, configDir, markdown) {
|
|
28
|
+
const resolved = [];
|
|
29
|
+
for (const group of groups) {
|
|
30
|
+
if (!group.group)
|
|
31
|
+
throw new Error(`Group missing "group" name in tab "${tabName}"`);
|
|
32
|
+
if (!group.pages?.length)
|
|
33
|
+
throw new Error(`Group "${group.group}" in tab "${tabName}" has no pages`);
|
|
34
|
+
const pages = [];
|
|
35
|
+
for (const pageSlug of group.pages) {
|
|
36
|
+
if (pageSlug.includes("*")) {
|
|
37
|
+
const expanded = await expandGlob(pageSlug, configDir);
|
|
38
|
+
for (const file of expanded) {
|
|
39
|
+
const rel = relative(configDir, file).replace(/\.[^.]+$/, "");
|
|
40
|
+
pages.push({ slug: rel, file, ...markdown });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const absPath = await resolvePagePath(pageSlug, configDir);
|
|
45
|
+
pages.push({ slug: pageSlug, file: absPath, ...markdown });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
resolved.push({ label: group.group, pages });
|
|
49
|
+
}
|
|
50
|
+
return resolved;
|
|
51
|
+
}
|
|
52
|
+
export async function collectStaticAssets(sourceRoot) {
|
|
53
|
+
const assets = [];
|
|
54
|
+
async function walk(dir) {
|
|
55
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
56
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
57
|
+
if (entry.name.startsWith("."))
|
|
58
|
+
continue;
|
|
59
|
+
const absolutePath = join(dir, entry.name);
|
|
60
|
+
if (entry.isDirectory()) {
|
|
61
|
+
await walk(absolutePath);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (!entry.isFile())
|
|
65
|
+
continue;
|
|
66
|
+
if (!STATIC_ASSET_EXTENSIONS.has(extname(entry.name).toLowerCase()))
|
|
67
|
+
continue;
|
|
68
|
+
assets.push({
|
|
69
|
+
file: absolutePath,
|
|
70
|
+
outputPath: relative(sourceRoot, absolutePath).replace(/\\/g, "/"),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
await walk(sourceRoot);
|
|
75
|
+
return assets;
|
|
76
|
+
}
|
|
77
|
+
export function toWatchPaths(paths) {
|
|
78
|
+
return paths.filter((path) => Boolean(path));
|
|
79
|
+
}
|
|
80
|
+
export function assertLocalPath(ctx, source, label) {
|
|
81
|
+
if (ctx.isUrl(source)) {
|
|
82
|
+
throw new Error(`${label} "${source}" in tab "${ctx.tabName}" must be a local file path`);
|
|
83
|
+
}
|
|
84
|
+
return ctx.resolvePath(source);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Expand a simple glob pattern like "doc/api-*" into matching .md/.mdx files.
|
|
88
|
+
* Supports trailing * only (e.g. "doc/api-*", "guides/*").
|
|
89
|
+
*/
|
|
90
|
+
async function expandGlob(pattern, configDir) {
|
|
91
|
+
const absPattern = resolve(configDir, pattern);
|
|
92
|
+
const dir = dirname(absPattern);
|
|
93
|
+
const prefix = basename(absPattern).replace("*", "");
|
|
94
|
+
let entries;
|
|
95
|
+
try {
|
|
96
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
if (err.code === "ENOENT") {
|
|
100
|
+
throw new Error(`Glob directory not found: ${dir}`);
|
|
101
|
+
}
|
|
102
|
+
throw err;
|
|
103
|
+
}
|
|
104
|
+
const matches = entries
|
|
105
|
+
.filter((entry) => entry.isFile() && entry.name.startsWith(prefix) && /\.(md|mdx)$/i.test(entry.name))
|
|
106
|
+
.map((entry) => join(dir, entry.name))
|
|
107
|
+
.sort();
|
|
108
|
+
if (matches.length === 0) {
|
|
109
|
+
throw new Error(`Glob "${pattern}" matched no files in ${dir}`);
|
|
110
|
+
}
|
|
111
|
+
return matches;
|
|
112
|
+
}
|
|
113
|
+
async function resolvePagePath(pageSlug, configDir) {
|
|
114
|
+
const direct = resolve(configDir, pageSlug);
|
|
115
|
+
const candidates = extname(direct)
|
|
116
|
+
? [direct]
|
|
117
|
+
: [`${direct}.md`, `${direct}.mdx`, join(direct, "index.md"), join(direct, "index.mdx")];
|
|
118
|
+
for (const candidate of candidates) {
|
|
119
|
+
try {
|
|
120
|
+
await access(candidate);
|
|
121
|
+
return candidate;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// keep trying candidates
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
throw new Error(`Page "${pageSlug}" not found. Tried:\n` +
|
|
128
|
+
candidates.map((candidate) => ` - ${candidate}`).join("\n") +
|
|
129
|
+
`\n\nPages are referenced relative to sourcey.config.ts. ` +
|
|
130
|
+
`Use slugs like "getting-started" for getting-started.md or "run/index" for run/index.md.`);
|
|
131
|
+
}
|
|
132
|
+
export function outputSlugForPage(page) {
|
|
133
|
+
return slugFromPath(page.slug);
|
|
134
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { MarkdownPreprocessor } from "../core/markdown-loader.js";
|
|
2
|
+
import type { DoxygenConfig, GodocConfig, GroupConfig, ResolvedDoxygenConfig, ResolvedGodocConfig, ResolvedGroup, ResolvedRustdocConfig, RustdocConfig } from "../config.js";
|
|
3
|
+
export interface SourceAdapterContext {
|
|
4
|
+
configDir: string;
|
|
5
|
+
tabName: string;
|
|
6
|
+
tabSlug: string;
|
|
7
|
+
resolvePath(path: string): string;
|
|
8
|
+
assertExists(path: string, label: string): Promise<void>;
|
|
9
|
+
isUrl(source: string): boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface SourceAdapter<TResolved extends ResolvedTabSource = ResolvedTabSource> {
|
|
12
|
+
name: string;
|
|
13
|
+
resolve(ctx: SourceAdapterContext): Promise<TResolved>;
|
|
14
|
+
}
|
|
15
|
+
export interface ResolvedSourceAsset {
|
|
16
|
+
/** Absolute source file path. */
|
|
17
|
+
file: string;
|
|
18
|
+
/** Output path relative to the adapter tab root. */
|
|
19
|
+
outputPath: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ResolvedOpenApiSource {
|
|
22
|
+
kind: "openapi";
|
|
23
|
+
spec: string;
|
|
24
|
+
watchPaths?: string[];
|
|
25
|
+
}
|
|
26
|
+
export interface ResolvedMcpSource {
|
|
27
|
+
kind: "mcp";
|
|
28
|
+
spec: string;
|
|
29
|
+
watchPaths?: string[];
|
|
30
|
+
}
|
|
31
|
+
export interface ResolvedMarkdownSource {
|
|
32
|
+
kind: "markdown";
|
|
33
|
+
adapter?: string;
|
|
34
|
+
configPath?: string;
|
|
35
|
+
groups: ResolvedGroup[];
|
|
36
|
+
assets?: ResolvedSourceAsset[];
|
|
37
|
+
watchPaths?: string[];
|
|
38
|
+
}
|
|
39
|
+
export interface ResolvedDoxygenSource {
|
|
40
|
+
kind: "doxygen";
|
|
41
|
+
config: ResolvedDoxygenConfig;
|
|
42
|
+
watchPaths?: string[];
|
|
43
|
+
}
|
|
44
|
+
export interface ResolvedGodocSource {
|
|
45
|
+
kind: "godoc";
|
|
46
|
+
config: ResolvedGodocConfig;
|
|
47
|
+
watchPaths?: string[];
|
|
48
|
+
}
|
|
49
|
+
export interface ResolvedRustdocSource {
|
|
50
|
+
kind: "rustdoc";
|
|
51
|
+
config: ResolvedRustdocConfig;
|
|
52
|
+
watchPaths?: string[];
|
|
53
|
+
}
|
|
54
|
+
export type ResolvedTabSource = ResolvedOpenApiSource | ResolvedMcpSource | ResolvedMarkdownSource | ResolvedDoxygenSource | ResolvedGodocSource | ResolvedRustdocSource;
|
|
55
|
+
export interface MarkdownSourceOptions {
|
|
56
|
+
groups: GroupConfig[];
|
|
57
|
+
}
|
|
58
|
+
export interface OpenApiSourceOptions {
|
|
59
|
+
spec: string;
|
|
60
|
+
}
|
|
61
|
+
export interface McpSourceOptions {
|
|
62
|
+
spec: string;
|
|
63
|
+
}
|
|
64
|
+
export interface MkDocsSourceOptions {
|
|
65
|
+
config: string;
|
|
66
|
+
}
|
|
67
|
+
export type DoxygenSourceOptions = DoxygenConfig;
|
|
68
|
+
export type GodocSourceOptions = GodocConfig | string;
|
|
69
|
+
export type RustdocSourceOptions = RustdocConfig | string;
|
|
70
|
+
export interface PageMarkdownOptions {
|
|
71
|
+
sourceRoot?: string;
|
|
72
|
+
preprocess?: MarkdownPreprocessor[];
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|