@marlinjai/clearify 1.20.0 → 2.1.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/config-schema.json +142 -44
- package/dist/node/{chunk-JZZ2BNIL.js → chunk-GPBEDIOY.js} +2 -2
- package/dist/node/{chunk-HZRIVY25.js → chunk-GZE6QLJT.js} +1 -1
- package/dist/node/{chunk-XR4WG237.js → chunk-PZA4UAFG.js} +42 -2
- package/dist/node/{chunk-GWYMXKDL.js → chunk-Q5FUAK6J.js} +1 -1
- package/dist/node/chunk-TVDWHKQF.js +134 -0
- package/dist/node/{chunk-2WML2D77.js → chunk-ZWF6VCSQ.js} +68 -32
- package/dist/node/core/config.d.ts +23 -11
- package/dist/node/core/config.js +1 -1
- package/dist/node/core/llms-txt.d.ts +54 -0
- package/dist/node/core/llms-txt.js +10 -0
- package/dist/node/{hub-register-7NF5BP3E.js → hub-register-QB73GUF2.js} +6 -3
- package/dist/node/node/build.js +4 -3
- package/dist/node/node/check.js +2 -2
- package/dist/node/node/index.js +6 -5
- package/dist/node/node/init.js +1 -1
- package/dist/node/types/index.d.ts +72 -8
- package/dist/node/vite-plugin/index.js +2 -2
- package/package.json +5 -3
- package/src/theme/Sidebar.tsx +2 -2
- package/src/theme/admin/ProjectsManager.tsx +227 -109
- package/src/theme/components/ProjectGrid.tsx +17 -1
package/dist/config-schema.json
CHANGED
|
@@ -116,9 +116,6 @@
|
|
|
116
116
|
"description": {
|
|
117
117
|
"type": "string"
|
|
118
118
|
},
|
|
119
|
-
"href": {
|
|
120
|
-
"type": "string"
|
|
121
|
-
},
|
|
122
119
|
"repo": {
|
|
123
120
|
"type": "string"
|
|
124
121
|
},
|
|
@@ -144,64 +141,165 @@
|
|
|
144
141
|
"group": {
|
|
145
142
|
"type": "string"
|
|
146
143
|
},
|
|
147
|
-
"
|
|
148
|
-
"
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
144
|
+
"source": {
|
|
145
|
+
"oneOf": [
|
|
146
|
+
{
|
|
147
|
+
"type": "object",
|
|
148
|
+
"properties": {
|
|
149
|
+
"kind": {
|
|
150
|
+
"type": "string",
|
|
151
|
+
"const": "none"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"required": [
|
|
155
|
+
"kind"
|
|
156
|
+
],
|
|
157
|
+
"additionalProperties": false
|
|
160
158
|
},
|
|
161
|
-
|
|
162
|
-
"type": "
|
|
159
|
+
{
|
|
160
|
+
"type": "object",
|
|
161
|
+
"properties": {
|
|
162
|
+
"kind": {
|
|
163
|
+
"type": "string",
|
|
164
|
+
"const": "git"
|
|
165
|
+
},
|
|
166
|
+
"repo": {
|
|
167
|
+
"type": "string"
|
|
168
|
+
},
|
|
169
|
+
"ref": {
|
|
170
|
+
"type": "string"
|
|
171
|
+
},
|
|
172
|
+
"path": {
|
|
173
|
+
"type": "string"
|
|
174
|
+
},
|
|
175
|
+
"sparse": {
|
|
176
|
+
"type": "boolean"
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
"required": [
|
|
180
|
+
"kind",
|
|
181
|
+
"repo"
|
|
182
|
+
],
|
|
183
|
+
"additionalProperties": false
|
|
163
184
|
},
|
|
164
|
-
|
|
165
|
-
"type": "
|
|
185
|
+
{
|
|
186
|
+
"type": "object",
|
|
187
|
+
"properties": {
|
|
188
|
+
"kind": {
|
|
189
|
+
"type": "string",
|
|
190
|
+
"const": "url"
|
|
191
|
+
},
|
|
192
|
+
"url": {
|
|
193
|
+
"type": "string"
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
"required": [
|
|
197
|
+
"kind",
|
|
198
|
+
"url"
|
|
199
|
+
],
|
|
200
|
+
"additionalProperties": false
|
|
166
201
|
},
|
|
167
|
-
|
|
168
|
-
"type": "
|
|
202
|
+
{
|
|
203
|
+
"type": "object",
|
|
204
|
+
"properties": {
|
|
205
|
+
"kind": {
|
|
206
|
+
"type": "string",
|
|
207
|
+
"const": "inline"
|
|
208
|
+
},
|
|
209
|
+
"markdown": {
|
|
210
|
+
"type": "string"
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
"required": [
|
|
214
|
+
"kind",
|
|
215
|
+
"markdown"
|
|
216
|
+
],
|
|
217
|
+
"additionalProperties": false
|
|
169
218
|
}
|
|
170
|
-
|
|
171
|
-
"required": [
|
|
172
|
-
"repo"
|
|
173
|
-
],
|
|
174
|
-
"additionalProperties": false
|
|
219
|
+
]
|
|
175
220
|
},
|
|
176
|
-
"
|
|
177
|
-
"
|
|
221
|
+
"placement": {
|
|
222
|
+
"oneOf": [
|
|
178
223
|
{
|
|
179
|
-
"type": "
|
|
180
|
-
"
|
|
224
|
+
"type": "object",
|
|
225
|
+
"properties": {
|
|
226
|
+
"kind": {
|
|
227
|
+
"type": "string",
|
|
228
|
+
"const": "card"
|
|
229
|
+
},
|
|
230
|
+
"href": {
|
|
231
|
+
"type": "string"
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
"required": [
|
|
235
|
+
"kind",
|
|
236
|
+
"href"
|
|
237
|
+
],
|
|
238
|
+
"additionalProperties": false
|
|
181
239
|
},
|
|
182
240
|
{
|
|
183
|
-
"type": "
|
|
184
|
-
"
|
|
241
|
+
"type": "object",
|
|
242
|
+
"properties": {
|
|
243
|
+
"kind": {
|
|
244
|
+
"type": "string",
|
|
245
|
+
"const": "tab"
|
|
246
|
+
},
|
|
247
|
+
"sections": {
|
|
248
|
+
"anyOf": [
|
|
249
|
+
{
|
|
250
|
+
"type": "string",
|
|
251
|
+
"const": "all"
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"type": "string",
|
|
255
|
+
"const": "public"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"type": "array",
|
|
259
|
+
"items": {
|
|
260
|
+
"type": "string"
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
]
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
"required": [
|
|
267
|
+
"kind"
|
|
268
|
+
],
|
|
269
|
+
"additionalProperties": false
|
|
185
270
|
},
|
|
186
271
|
{
|
|
187
|
-
"type": "
|
|
188
|
-
"
|
|
189
|
-
"
|
|
190
|
-
|
|
272
|
+
"type": "object",
|
|
273
|
+
"properties": {
|
|
274
|
+
"kind": {
|
|
275
|
+
"type": "string",
|
|
276
|
+
"const": "nested"
|
|
277
|
+
},
|
|
278
|
+
"into": {
|
|
279
|
+
"type": "string"
|
|
280
|
+
},
|
|
281
|
+
"docsPath": {
|
|
282
|
+
"type": "string"
|
|
283
|
+
},
|
|
284
|
+
"group": {
|
|
285
|
+
"type": "string"
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
"required": [
|
|
289
|
+
"kind",
|
|
290
|
+
"into"
|
|
291
|
+
],
|
|
292
|
+
"additionalProperties": false
|
|
191
293
|
}
|
|
192
294
|
]
|
|
193
|
-
},
|
|
194
|
-
"injectInto": {
|
|
195
|
-
"type": "string"
|
|
196
|
-
},
|
|
197
|
-
"docsPath": {
|
|
198
|
-
"type": "string"
|
|
199
295
|
}
|
|
200
296
|
},
|
|
201
297
|
"required": [
|
|
202
298
|
"name",
|
|
203
299
|
"description",
|
|
204
|
-
"status"
|
|
300
|
+
"status",
|
|
301
|
+
"source",
|
|
302
|
+
"placement"
|
|
205
303
|
],
|
|
206
304
|
"additionalProperties": false
|
|
207
305
|
}
|
|
@@ -282,7 +282,7 @@ ${hasInternal ? `
|
|
|
282
282
|
`);
|
|
283
283
|
const secretMode = options.secretMode ?? "prompt";
|
|
284
284
|
if (options.hub) {
|
|
285
|
-
const { registerWithHub } = await import("./hub-register-
|
|
285
|
+
const { registerWithHub } = await import("./hub-register-QB73GUF2.js");
|
|
286
286
|
await registerWithHub({
|
|
287
287
|
projectName: detectProjectName(),
|
|
288
288
|
hubToken: options.hubToken,
|
|
@@ -298,7 +298,7 @@ ${hasInternal ? `
|
|
|
298
298
|
});
|
|
299
299
|
rl.close();
|
|
300
300
|
if (answer.toLowerCase() === "y") {
|
|
301
|
-
const { registerWithHub } = await import("./hub-register-
|
|
301
|
+
const { registerWithHub } = await import("./hub-register-QB73GUF2.js");
|
|
302
302
|
await registerWithHub({
|
|
303
303
|
projectName: detectProjectName(),
|
|
304
304
|
hubToken: options.hubToken,
|
|
@@ -6,14 +6,18 @@ import {
|
|
|
6
6
|
import {
|
|
7
7
|
mermaidContentHash
|
|
8
8
|
} from "./chunk-SCZZB7OE.js";
|
|
9
|
+
import {
|
|
10
|
+
generateLlmsFullTxt,
|
|
11
|
+
generateLlmsTxt
|
|
12
|
+
} from "./chunk-TVDWHKQF.js";
|
|
9
13
|
import {
|
|
10
14
|
clearifyPlugin
|
|
11
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-Q5FUAK6J.js";
|
|
12
16
|
import {
|
|
13
17
|
loadUserConfig,
|
|
14
18
|
resolveConfig,
|
|
15
19
|
resolveSections
|
|
16
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-ZWF6VCSQ.js";
|
|
17
21
|
import {
|
|
18
22
|
buildSectionData
|
|
19
23
|
} from "./chunk-CTL4YL7C.js";
|
|
@@ -72,6 +76,29 @@ function generateSitemap(routes, siteUrl) {
|
|
|
72
76
|
${urls.join("\n")}
|
|
73
77
|
</urlset>`;
|
|
74
78
|
}
|
|
79
|
+
function formatBytes(bytes) {
|
|
80
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
81
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
82
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
83
|
+
}
|
|
84
|
+
function collectLlmsDocs(sections) {
|
|
85
|
+
const out = [];
|
|
86
|
+
for (const sd of sections) {
|
|
87
|
+
for (const doc of sd.docs) {
|
|
88
|
+
const title = doc.frontmatter.title ?? "Untitled";
|
|
89
|
+
const optional = (doc.frontmatter.order ?? 0) >= 9e3;
|
|
90
|
+
const markdown = existsSync(doc.filePath) ? readFileSync(doc.filePath, "utf-8") : "";
|
|
91
|
+
out.push({
|
|
92
|
+
title,
|
|
93
|
+
routePath: doc.routePath,
|
|
94
|
+
sectionLabel: sd.section.label,
|
|
95
|
+
markdown,
|
|
96
|
+
optional
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
75
102
|
function generateRobotsTxt(siteUrl) {
|
|
76
103
|
const lines = ["User-agent: *", "Allow: /"];
|
|
77
104
|
if (siteUrl) {
|
|
@@ -312,6 +339,19 @@ async function buildSite() {
|
|
|
312
339
|
const sitemap = generateSitemap(sitemapRoutes, config.siteUrl);
|
|
313
340
|
writeFileSync(resolve(outDir, "sitemap.xml"), sitemap);
|
|
314
341
|
console.log(" Generated sitemap.xml");
|
|
342
|
+
if (config.generateLlmsTxt !== false) {
|
|
343
|
+
const llmsDocs = collectLlmsDocs(sectionDataList);
|
|
344
|
+
const llmsTxt = generateLlmsTxt(config, llmsDocs);
|
|
345
|
+
const llmsFullTxt = generateLlmsFullTxt(config, llmsDocs);
|
|
346
|
+
const llmsTxtPath = resolve(outDir, "llms.txt");
|
|
347
|
+
const llmsFullTxtPath = resolve(outDir, "llms-full.txt");
|
|
348
|
+
writeFileSync(llmsTxtPath, llmsTxt);
|
|
349
|
+
writeFileSync(llmsFullTxtPath, llmsFullTxt);
|
|
350
|
+
const llmsTxtSize = Buffer.byteLength(llmsTxt, "utf-8");
|
|
351
|
+
const llmsFullSize = Buffer.byteLength(llmsFullTxt, "utf-8");
|
|
352
|
+
console.log(` Generated ${llmsTxtPath} (${formatBytes(llmsTxtSize)})`);
|
|
353
|
+
console.log(` Generated ${llmsFullTxtPath} (${formatBytes(llmsFullSize)})`);
|
|
354
|
+
}
|
|
315
355
|
rmSync(ssrOutDir, { recursive: true, force: true });
|
|
316
356
|
console.log(" Cleaned up SSR build artifacts.");
|
|
317
357
|
if (mermaidStrategy === "build") {
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// src/core/llms-txt.ts
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import matter from "gray-matter";
|
|
4
|
+
function normalizeBase(siteUrl) {
|
|
5
|
+
if (!siteUrl) return "";
|
|
6
|
+
return siteUrl.replace(/\/$/, "");
|
|
7
|
+
}
|
|
8
|
+
function toAbsoluteUrl(base, routePath) {
|
|
9
|
+
if (!base) return routePath;
|
|
10
|
+
if (!routePath.startsWith("/")) return `${base}/${routePath}`;
|
|
11
|
+
return `${base}${routePath}`;
|
|
12
|
+
}
|
|
13
|
+
function stripFrontmatterAndH1(markdown) {
|
|
14
|
+
const { content } = matter(markdown);
|
|
15
|
+
return content.replace(/^\s*#\s+[^\n]*\n+/, "").trimStart();
|
|
16
|
+
}
|
|
17
|
+
function firstParagraphSummary(markdown) {
|
|
18
|
+
const parsed = matter(markdown);
|
|
19
|
+
const fromFrontmatter = typeof parsed.data.summary === "string" && parsed.data.summary.trim() || typeof parsed.data.description === "string" && parsed.data.description.trim() || "";
|
|
20
|
+
const body = parsed.content;
|
|
21
|
+
const lines = body.split(/\r?\n/);
|
|
22
|
+
let inFence = false;
|
|
23
|
+
const paragraph = [];
|
|
24
|
+
for (const rawLine of lines) {
|
|
25
|
+
const line = rawLine.trimEnd();
|
|
26
|
+
if (/^```/.test(line)) {
|
|
27
|
+
inFence = !inFence;
|
|
28
|
+
if (paragraph.length > 0) break;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (inFence) continue;
|
|
32
|
+
if (!line.trim()) {
|
|
33
|
+
if (paragraph.length > 0) break;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (/^(#{1,6}\s|>\s|[-*+]\s|\d+\.\s|\|.*\|\s*$|<)/.test(line)) {
|
|
37
|
+
if (paragraph.length > 0) break;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
paragraph.push(line.trim());
|
|
41
|
+
}
|
|
42
|
+
const text = paragraph.join(" ").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/([*_]{1,3})([^*_]+)\1/g, "$2").replace(/\s+/g, " ").trim();
|
|
43
|
+
return text || fromFrontmatter;
|
|
44
|
+
}
|
|
45
|
+
function uniqueOrdered(items) {
|
|
46
|
+
const seen = /* @__PURE__ */ new Set();
|
|
47
|
+
const out = [];
|
|
48
|
+
for (const item of items) {
|
|
49
|
+
if (seen.has(item)) continue;
|
|
50
|
+
seen.add(item);
|
|
51
|
+
out.push(item);
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
function buildHeader(config) {
|
|
56
|
+
const name = config.name.trim();
|
|
57
|
+
const tagline = config.hubProject?.description && config.hubProject.description.trim() || `Documentation for ${name}.`;
|
|
58
|
+
return `# ${name}
|
|
59
|
+
|
|
60
|
+
> ${tagline}
|
|
61
|
+
|
|
62
|
+
`;
|
|
63
|
+
}
|
|
64
|
+
function generateLlmsTxt(config, docs) {
|
|
65
|
+
const base = normalizeBase(config.siteUrl);
|
|
66
|
+
let out = buildHeader(config);
|
|
67
|
+
const mainDocs = docs.filter((d) => !d.optional);
|
|
68
|
+
const optionalDocs = docs.filter((d) => d.optional);
|
|
69
|
+
const sectionLabels = uniqueOrdered(mainDocs.map((d) => d.sectionLabel));
|
|
70
|
+
for (const label of sectionLabels) {
|
|
71
|
+
const sectionDocs = mainDocs.filter((d) => d.sectionLabel === label);
|
|
72
|
+
if (sectionDocs.length === 0) continue;
|
|
73
|
+
out += `## ${label}
|
|
74
|
+
|
|
75
|
+
`;
|
|
76
|
+
for (const doc of sectionDocs) {
|
|
77
|
+
const url = toAbsoluteUrl(base, doc.routePath);
|
|
78
|
+
const summary = firstParagraphSummary(doc.markdown);
|
|
79
|
+
if (summary) {
|
|
80
|
+
out += `- [${doc.title}](${url}): ${summary}
|
|
81
|
+
`;
|
|
82
|
+
} else {
|
|
83
|
+
out += `- [${doc.title}](${url})
|
|
84
|
+
`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
out += "\n";
|
|
88
|
+
}
|
|
89
|
+
if (optionalDocs.length > 0) {
|
|
90
|
+
out += `## Optional
|
|
91
|
+
|
|
92
|
+
`;
|
|
93
|
+
for (const doc of optionalDocs) {
|
|
94
|
+
const url = toAbsoluteUrl(base, doc.routePath);
|
|
95
|
+
const summary = firstParagraphSummary(doc.markdown);
|
|
96
|
+
if (summary) {
|
|
97
|
+
out += `- [${doc.title}](${url}): ${summary}
|
|
98
|
+
`;
|
|
99
|
+
} else {
|
|
100
|
+
out += `- [${doc.title}](${url})
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
out += "\n";
|
|
105
|
+
}
|
|
106
|
+
return out.trimEnd() + "\n";
|
|
107
|
+
}
|
|
108
|
+
function generateLlmsFullTxt(config, docs) {
|
|
109
|
+
const base = normalizeBase(config.siteUrl);
|
|
110
|
+
let out = buildHeader(config);
|
|
111
|
+
for (const doc of docs) {
|
|
112
|
+
const url = toAbsoluteUrl(base, doc.routePath);
|
|
113
|
+
const body = stripFrontmatterAndH1(doc.markdown);
|
|
114
|
+
out += `## ${doc.title}
|
|
115
|
+
|
|
116
|
+
`;
|
|
117
|
+
out += `Source: ${url}
|
|
118
|
+
|
|
119
|
+
`;
|
|
120
|
+
if (body) {
|
|
121
|
+
out += body.trimEnd() + "\n\n";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return out.trimEnd() + "\n";
|
|
125
|
+
}
|
|
126
|
+
function readMarkdownFile(filePath) {
|
|
127
|
+
return readFileSync(filePath, "utf-8");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export {
|
|
131
|
+
generateLlmsTxt,
|
|
132
|
+
generateLlmsFullTxt,
|
|
133
|
+
readMarkdownFile
|
|
134
|
+
};
|
|
@@ -42,28 +42,44 @@ var OpenAPIConfigSchema = z.object({
|
|
|
42
42
|
basePath: z.string().default("/api"),
|
|
43
43
|
generatePages: z.boolean().default(true)
|
|
44
44
|
}).optional();
|
|
45
|
+
var HubProjectSourceSchema = z.discriminatedUnion("kind", [
|
|
46
|
+
z.object({ kind: z.literal("none") }),
|
|
47
|
+
z.object({
|
|
48
|
+
kind: z.literal("git"),
|
|
49
|
+
repo: z.string(),
|
|
50
|
+
ref: z.string().optional(),
|
|
51
|
+
path: z.string().optional(),
|
|
52
|
+
sparse: z.boolean().optional()
|
|
53
|
+
}),
|
|
54
|
+
z.object({ kind: z.literal("url"), url: z.string() }),
|
|
55
|
+
z.object({ kind: z.literal("inline"), markdown: z.string() })
|
|
56
|
+
]);
|
|
57
|
+
var HubProjectPlacementSchema = z.discriminatedUnion("kind", [
|
|
58
|
+
z.object({ kind: z.literal("card"), href: z.string() }),
|
|
59
|
+
z.object({
|
|
60
|
+
kind: z.literal("tab"),
|
|
61
|
+
sections: z.union([z.literal("all"), z.literal("public"), z.array(z.string())]).optional()
|
|
62
|
+
}),
|
|
63
|
+
z.object({
|
|
64
|
+
kind: z.literal("nested"),
|
|
65
|
+
into: z.string(),
|
|
66
|
+
docsPath: z.string().optional(),
|
|
67
|
+
group: z.string().optional()
|
|
68
|
+
})
|
|
69
|
+
]);
|
|
45
70
|
var HubProjectSchema = z.object({
|
|
46
71
|
name: z.string(),
|
|
47
72
|
description: z.string(),
|
|
48
|
-
href: z.string().optional(),
|
|
49
73
|
repo: z.string().optional(),
|
|
50
74
|
status: z.enum(["active", "beta", "planned", "deprecated"]).default("active"),
|
|
51
75
|
icon: z.string().optional(),
|
|
52
76
|
tags: z.array(z.string()).optional(),
|
|
53
77
|
group: z.string().optional(),
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
embedSections: z.union([
|
|
57
|
-
z.literal("all"),
|
|
58
|
-
z.literal("public"),
|
|
59
|
-
z.array(z.string())
|
|
60
|
-
]).optional(),
|
|
61
|
-
injectInto: z.string().optional(),
|
|
62
|
-
docsPath: z.string().optional()
|
|
78
|
+
source: HubProjectSourceSchema,
|
|
79
|
+
placement: HubProjectPlacementSchema
|
|
63
80
|
});
|
|
64
81
|
var HubProjectPartialSchema = z.object({
|
|
65
82
|
description: z.string(),
|
|
66
|
-
href: z.string().optional(),
|
|
67
83
|
repo: z.string().optional(),
|
|
68
84
|
status: z.enum(["active", "beta", "planned", "deprecated"]).default("active"),
|
|
69
85
|
icon: z.string().optional(),
|
|
@@ -101,7 +117,8 @@ var ClearifyConfigSchema = z.object({
|
|
|
101
117
|
hubProject: HubProjectPartialSchema,
|
|
102
118
|
customCss: z.string().optional(),
|
|
103
119
|
headTags: z.array(z.string()).optional(),
|
|
104
|
-
includeReadme: z.boolean().default(false)
|
|
120
|
+
includeReadme: z.boolean().default(false),
|
|
121
|
+
generateLlmsTxt: z.boolean().default(true)
|
|
105
122
|
});
|
|
106
123
|
var ClearifyDataSchema = z.object({
|
|
107
124
|
name: z.string().optional(),
|
|
@@ -249,6 +266,11 @@ function resolveConfig(userConfig = {}, root) {
|
|
|
249
266
|
if (!userConfig.name && root) {
|
|
250
267
|
parsed.name = detectProjectName(root);
|
|
251
268
|
}
|
|
269
|
+
if (parsed.generateLlmsTxt && !parsed.siteUrl) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
"clearify config: `generateLlmsTxt` is enabled (default) but `siteUrl` is not set. Set `siteUrl` to the production URL of your docs site, or set `generateLlmsTxt: false` to disable."
|
|
272
|
+
);
|
|
273
|
+
}
|
|
252
274
|
return parsed;
|
|
253
275
|
}
|
|
254
276
|
function slugify(str) {
|
|
@@ -335,8 +357,14 @@ function buildStagingOverlay(stagingDir, originalDir, injections) {
|
|
|
335
357
|
symlinkSync(sourcePath, destPath);
|
|
336
358
|
}
|
|
337
359
|
}
|
|
360
|
+
function isReservedSource(source) {
|
|
361
|
+
return source.kind === "url" || source.kind === "inline";
|
|
362
|
+
}
|
|
338
363
|
async function scanHubProjects(config, root) {
|
|
339
|
-
|
|
364
|
+
const hasGitTabOrNested = config.hub?.projects?.some(
|
|
365
|
+
(p) => p.source.kind === "git" && (p.placement.kind === "tab" || p.placement.kind === "nested")
|
|
366
|
+
) ?? false;
|
|
367
|
+
if (!config.hub?.scan && !hasGitTabOrNested) {
|
|
340
368
|
return config;
|
|
341
369
|
}
|
|
342
370
|
const scannedProjects = [];
|
|
@@ -350,15 +378,17 @@ async function scanHubProjects(config, root) {
|
|
|
350
378
|
if (!childConfig.hubProject) continue;
|
|
351
379
|
const name = childConfig.name ?? basename(dirname(configPath));
|
|
352
380
|
const hp = childConfig.hubProject;
|
|
381
|
+
const href = childConfig.siteUrl;
|
|
353
382
|
scannedProjects.push({
|
|
354
383
|
name,
|
|
355
384
|
description: hp.description,
|
|
356
|
-
href: hp.href ?? childConfig.siteUrl,
|
|
357
385
|
repo: hp.repo,
|
|
358
386
|
status: hp.status,
|
|
359
387
|
icon: hp.icon,
|
|
360
388
|
tags: hp.tags,
|
|
361
|
-
group: hp.group
|
|
389
|
+
group: hp.group,
|
|
390
|
+
source: { kind: "none" },
|
|
391
|
+
placement: { kind: "card", href: href ?? "#" }
|
|
362
392
|
});
|
|
363
393
|
} catch {
|
|
364
394
|
}
|
|
@@ -369,18 +399,26 @@ async function scanHubProjects(config, root) {
|
|
|
369
399
|
...scannedProjects.filter((p) => !manualNames.has(p.name)),
|
|
370
400
|
...config.hub?.projects ?? []
|
|
371
401
|
];
|
|
402
|
+
for (const project of merged) {
|
|
403
|
+
if (isReservedSource(project.source)) {
|
|
404
|
+
throw new Error(
|
|
405
|
+
`Hub project "${project.name}": source.kind "${project.source.kind}" is reserved and not implemented. Use source.kind "none" or "git".`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
372
409
|
const embedSections = [];
|
|
373
410
|
for (const project of merged) {
|
|
374
|
-
if (project.
|
|
411
|
+
if (project.source.kind !== "git" || project.placement.kind !== "tab") continue;
|
|
375
412
|
const cacheDir = config.hub?.cacheDir ? resolve(root, config.hub.cacheDir) : resolve(root, "node_modules/.cache/clearify-remote");
|
|
376
413
|
try {
|
|
377
414
|
const { resolveRemoteSource } = await import("./core/remote.js");
|
|
378
|
-
const {
|
|
415
|
+
const { kind: _kind, ...gitSource } = project.source;
|
|
416
|
+
const { localPath } = await resolveRemoteSource(gitSource, cacheDir);
|
|
379
417
|
const remoteConfig = await loadUserConfig(localPath);
|
|
380
418
|
const remoteSections = remoteConfig.sections ?? [
|
|
381
419
|
{ label: remoteConfig.name ?? project.name, docsDir: remoteConfig.docsDir ?? "./docs" }
|
|
382
420
|
];
|
|
383
|
-
const filter = project.
|
|
421
|
+
const filter = project.placement.sections ?? "public";
|
|
384
422
|
const filtered = remoteSections.filter((s) => {
|
|
385
423
|
if (filter === "all") return true;
|
|
386
424
|
if (filter === "public") return !s.draft;
|
|
@@ -400,46 +438,44 @@ async function scanHubProjects(config, root) {
|
|
|
400
438
|
exclude: s.exclude
|
|
401
439
|
});
|
|
402
440
|
}
|
|
403
|
-
console.log(` Hub
|
|
441
|
+
console.log(` Hub tab "${project.name}": ${filtered.length} sections pulled`);
|
|
404
442
|
} catch (err) {
|
|
405
443
|
console.warn(
|
|
406
|
-
`
|
|
444
|
+
` Hub tab "${project.name}" failed:`,
|
|
407
445
|
err instanceof Error ? err.message : err
|
|
408
446
|
);
|
|
409
447
|
}
|
|
410
448
|
}
|
|
411
449
|
const injectionsBySection = /* @__PURE__ */ new Map();
|
|
412
450
|
for (const project of merged) {
|
|
413
|
-
if (project.
|
|
451
|
+
if (project.source.kind !== "git" || project.placement.kind !== "nested") continue;
|
|
414
452
|
const cacheDir = config.hub?.cacheDir ? resolve(root, config.hub.cacheDir) : resolve(root, "node_modules/.cache/clearify-remote");
|
|
415
453
|
try {
|
|
416
454
|
const { resolveRemoteSource } = await import("./core/remote.js");
|
|
417
|
-
const {
|
|
418
|
-
const
|
|
455
|
+
const { kind: _kind, ...gitSource } = project.source;
|
|
456
|
+
const { localPath, cached } = await resolveRemoteSource(gitSource, cacheDir);
|
|
457
|
+
const docsPath = project.placement.docsPath ?? "docs";
|
|
419
458
|
const absoluteDocsPath = resolve(localPath, docsPath);
|
|
420
459
|
if (!existsSync(absoluteDocsPath)) {
|
|
421
460
|
console.warn(
|
|
422
|
-
`
|
|
461
|
+
` Hub nested "${project.name}": docs path "${docsPath}" not found in cloned repo.`
|
|
423
462
|
);
|
|
424
463
|
continue;
|
|
425
464
|
}
|
|
426
|
-
const targetLabel = project.
|
|
427
|
-
if (!targetLabel) {
|
|
428
|
-
console.warn(` \u26A0 Hub inject "${project.name}": missing injectInto field.`);
|
|
429
|
-
continue;
|
|
430
|
-
}
|
|
465
|
+
const targetLabel = project.placement.into;
|
|
431
466
|
if (!injectionsBySection.has(targetLabel)) {
|
|
432
467
|
injectionsBySection.set(targetLabel, /* @__PURE__ */ new Map());
|
|
433
468
|
}
|
|
434
469
|
const slugifiedName = slugify(project.name);
|
|
435
|
-
const
|
|
470
|
+
const groupSeg = project.placement.group;
|
|
471
|
+
const relPath = groupSeg ? `projects/${groupSeg}/${slugifiedName}` : `projects/${slugifiedName}`;
|
|
436
472
|
injectionsBySection.get(targetLabel).set(relPath, absoluteDocsPath);
|
|
437
473
|
console.log(
|
|
438
|
-
` Hub
|
|
474
|
+
` Hub nested "${project.name}": ${cached ? "cached" : "cloned"} to ${relPath}`
|
|
439
475
|
);
|
|
440
476
|
} catch (err) {
|
|
441
477
|
console.warn(
|
|
442
|
-
`
|
|
478
|
+
` Hub nested "${project.name}" failed:`,
|
|
443
479
|
err instanceof Error ? err.message : err
|
|
444
480
|
);
|
|
445
481
|
}
|