@farming-labs/docs 0.1.81 → 0.1.83
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/{agent-C1EGa9Ot.mjs → agent-D97eZuWH.mjs} +5 -4
- package/dist/cli/index.mjs +33 -10
- package/dist/{dev-zn7AkGBT.mjs → dev-D4aVZkZl.mjs} +2 -2
- package/dist/{doctor-BBiL70Ec.mjs → doctor-DHUZK-iX.mjs} +86 -8
- package/dist/index.d.mts +38 -3
- package/dist/index.mjs +6 -4
- package/dist/{init-BBdFgtTm.mjs → init-Bt-RwmSW.mjs} +2 -2
- package/dist/{mcp-BPW62uf-.mjs → mcp-OVgCyHrR.mjs} +3 -3
- package/dist/mcp.d.mts +1 -1
- package/dist/mcp.mjs +2 -1
- package/dist/reading-time-pKUeloSI.mjs +315 -0
- package/dist/related-BNj_NdHq.mjs +50 -0
- package/dist/{agent-BbhLbeC7.mjs → robots-DCR-ZFLO.mjs} +166 -315
- package/dist/robots-Div3kkxI.mjs +177 -0
- package/dist/{search-kP0mAXCp.mjs → search-B5ze-heM.mjs} +1 -50
- package/dist/{search-D6uAGmiH.d.mts → search-BH07-Otd.d.mts} +1 -1
- package/dist/{search-DD2SP_hD.mjs → search-BIL0Zap1.mjs} +3 -3
- package/dist/server.d.mts +2 -2
- package/dist/server.mjs +3 -3
- package/dist/{sitemap-DCR8tuA7.mjs → sitemap-C2ocmew_.mjs} +5 -5
- package/dist/{sitemap-server-B370zkEo.mjs → sitemap-server-C8Ppk29g.mjs} +1 -1
- package/dist/{types-OAHZJ7NI.d.mts → types-Ch0kE7uS.d.mts} +57 -1
- package/dist/{upgrade-upRj5Fw-.mjs → upgrade-D9c60phM.mjs} +1 -1
- package/package.json +1 -1
- /package/dist/{config-C7sUsMkm.mjs → config-BR6CcCfr.mjs} +0 -0
- /package/dist/{sitemap-Buobvabq.mjs → sitemap-Ccfh6GXO.mjs} +0 -0
- /package/dist/{templates-BIk7zhQ3.mjs → templates-CtPtjNu5.mjs} +0 -0
- /package/dist/{utils-l0lcezN8.mjs → utils-AmYxHDoz.mjs} +0 -0
|
@@ -1,318 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { d as resolveDocsSitemapRequest, i as DEFAULT_SITEMAP_XML_ROUTE, n as DEFAULT_SITEMAP_MD_ROUTE, r as DEFAULT_SITEMAP_MD_WELL_KNOWN_ROUTE, u as resolveDocsSitemapConfig } from "./sitemap-
|
|
3
|
-
import matter from "gray-matter";
|
|
1
|
+
import { n as renderDocsRelatedMarkdownLines } from "./related-BNj_NdHq.mjs";
|
|
2
|
+
import { d as resolveDocsSitemapRequest, i as DEFAULT_SITEMAP_XML_ROUTE, n as DEFAULT_SITEMAP_MD_ROUTE, r as DEFAULT_SITEMAP_MD_WELL_KNOWN_ROUTE, u as resolveDocsSitemapConfig } from "./sitemap-Ccfh6GXO.mjs";
|
|
4
3
|
|
|
5
|
-
//#region src/define-docs.ts
|
|
6
|
-
/**
|
|
7
|
-
* Define docs configuration. Validates and returns the config.
|
|
8
|
-
*/
|
|
9
|
-
function defineDocs(config) {
|
|
10
|
-
return {
|
|
11
|
-
entry: config.entry ?? "docs",
|
|
12
|
-
contentDir: config.contentDir,
|
|
13
|
-
i18n: config.i18n,
|
|
14
|
-
theme: config.theme,
|
|
15
|
-
nav: config.nav,
|
|
16
|
-
github: config.github,
|
|
17
|
-
themeToggle: config.themeToggle,
|
|
18
|
-
breadcrumb: config.breadcrumb,
|
|
19
|
-
sidebar: config.sidebar,
|
|
20
|
-
components: config.components,
|
|
21
|
-
analytics: config.analytics,
|
|
22
|
-
observability: config.observability,
|
|
23
|
-
onCopyClick: config.onCopyClick,
|
|
24
|
-
feedback: config.feedback,
|
|
25
|
-
search: config.search,
|
|
26
|
-
mcp: config.mcp,
|
|
27
|
-
icons: config.icons,
|
|
28
|
-
pageActions: config.pageActions,
|
|
29
|
-
lastUpdated: config.lastUpdated,
|
|
30
|
-
readingTime: config.readingTime,
|
|
31
|
-
llmsTxt: config.llmsTxt,
|
|
32
|
-
sitemap: config.sitemap,
|
|
33
|
-
ai: config.ai,
|
|
34
|
-
ordering: config.ordering,
|
|
35
|
-
metadata: config.metadata,
|
|
36
|
-
og: config.og,
|
|
37
|
-
changelog: config.changelog,
|
|
38
|
-
apiReference: config.apiReference,
|
|
39
|
-
agent: config.agent
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
//#endregion
|
|
44
|
-
//#region src/changelog.ts
|
|
45
|
-
function normalizePathSegment(value, fallback) {
|
|
46
|
-
return (value ?? fallback).trim().replace(/^\/+|\/+$/g, "") || fallback;
|
|
47
|
-
}
|
|
48
|
-
function normalizeContentDir(value) {
|
|
49
|
-
const trimmed = value?.trim();
|
|
50
|
-
if (!trimmed) return "changelog";
|
|
51
|
-
return trimmed.replace(/\/+$/, "") || "changelog";
|
|
52
|
-
}
|
|
53
|
-
function resolveChangelogConfig(value) {
|
|
54
|
-
if (value === false || value === void 0) return {
|
|
55
|
-
enabled: false,
|
|
56
|
-
path: "changelog",
|
|
57
|
-
contentDir: "changelog",
|
|
58
|
-
title: "Changelog",
|
|
59
|
-
description: void 0,
|
|
60
|
-
search: true
|
|
61
|
-
};
|
|
62
|
-
if (value === true) return {
|
|
63
|
-
enabled: true,
|
|
64
|
-
path: "changelog",
|
|
65
|
-
contentDir: "changelog",
|
|
66
|
-
title: "Changelog",
|
|
67
|
-
description: void 0,
|
|
68
|
-
search: true
|
|
69
|
-
};
|
|
70
|
-
return {
|
|
71
|
-
enabled: value.enabled !== false,
|
|
72
|
-
path: normalizePathSegment(value.path, "changelog"),
|
|
73
|
-
contentDir: normalizeContentDir(value.contentDir),
|
|
74
|
-
title: value.title?.trim() || "Changelog",
|
|
75
|
-
description: value.description?.trim() || void 0,
|
|
76
|
-
search: value.search !== false,
|
|
77
|
-
actionsComponent: value.actionsComponent
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
//#endregion
|
|
82
|
-
//#region src/utils.ts
|
|
83
|
-
/**
|
|
84
|
-
* Deep merge utility for theme overrides.
|
|
85
|
-
* Merges objects recursively; later values override earlier ones.
|
|
86
|
-
*/
|
|
87
|
-
function deepMerge(target, ...sources) {
|
|
88
|
-
if (!sources.length) return target;
|
|
89
|
-
const source = sources.shift();
|
|
90
|
-
if (!source) return target;
|
|
91
|
-
const result = { ...target };
|
|
92
|
-
for (const key of Object.keys(source)) {
|
|
93
|
-
const sourceVal = source[key];
|
|
94
|
-
const targetVal = result[key];
|
|
95
|
-
if (sourceVal && typeof sourceVal === "object" && !Array.isArray(sourceVal) && targetVal && typeof targetVal === "object" && !Array.isArray(targetVal)) result[key] = deepMerge(targetVal, sourceVal);
|
|
96
|
-
else if (sourceVal !== void 0) result[key] = sourceVal;
|
|
97
|
-
}
|
|
98
|
-
if (sources.length) return deepMerge(result, ...sources);
|
|
99
|
-
return result;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
//#endregion
|
|
103
|
-
//#region src/create-theme.ts
|
|
104
|
-
/**
|
|
105
|
-
* Create a theme preset factory.
|
|
106
|
-
*
|
|
107
|
-
* Returns a function that accepts optional overrides and deep-merges them
|
|
108
|
-
* with the base theme defaults. This is the same pattern used by the
|
|
109
|
-
* built-in `fumadocs()`, `darksharp()`, and `pixelBorder()` presets.
|
|
110
|
-
*
|
|
111
|
-
* @param baseTheme - The default theme configuration
|
|
112
|
-
* @returns A factory function `(overrides?) => DocsTheme`
|
|
113
|
-
*
|
|
114
|
-
* @example
|
|
115
|
-
* ```ts
|
|
116
|
-
* import { createTheme } from "@farming-labs/docs";
|
|
117
|
-
*
|
|
118
|
-
* export const myTheme = createTheme({
|
|
119
|
-
* name: "my-theme",
|
|
120
|
-
* ui: {
|
|
121
|
-
* colors: { primary: "#6366f1" },
|
|
122
|
-
* layout: { contentWidth: 800 },
|
|
123
|
-
* },
|
|
124
|
-
* });
|
|
125
|
-
* ```
|
|
126
|
-
*/
|
|
127
|
-
function createTheme(baseTheme) {
|
|
128
|
-
return function themeFactory(overrides = {}) {
|
|
129
|
-
const merged = deepMerge(baseTheme, overrides);
|
|
130
|
-
if (overrides.ui?.colors) merged._userColorOverrides = { ...overrides.ui.colors };
|
|
131
|
-
return merged;
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Extend an existing theme preset with additional defaults.
|
|
136
|
-
*
|
|
137
|
-
* Useful when you want to build on top of an existing theme (e.g. fumadocs)
|
|
138
|
-
* rather than starting from scratch.
|
|
139
|
-
*
|
|
140
|
-
* @example
|
|
141
|
-
* ```ts
|
|
142
|
-
* import { extendTheme } from "@farming-labs/docs";
|
|
143
|
-
* import { fumadocs } from "@farming-labs/theme/default";
|
|
144
|
-
*
|
|
145
|
-
* // Start with fumadocs defaults, override some values
|
|
146
|
-
* export const myTheme = extendTheme(fumadocs(), {
|
|
147
|
-
* name: "my-custom-fumadocs",
|
|
148
|
-
* ui: { colors: { primary: "#22c55e" } },
|
|
149
|
-
* });
|
|
150
|
-
* ```
|
|
151
|
-
*/
|
|
152
|
-
function extendTheme(baseTheme, extensions) {
|
|
153
|
-
return deepMerge(baseTheme, extensions);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
//#endregion
|
|
157
|
-
//#region src/i18n.ts
|
|
158
|
-
function normalizeSegment(value) {
|
|
159
|
-
return value.replace(/^\/+|\/+$/g, "");
|
|
160
|
-
}
|
|
161
|
-
function splitSegments(value) {
|
|
162
|
-
const cleaned = normalizeSegment(value);
|
|
163
|
-
return cleaned ? cleaned.split("/").filter(Boolean) : [];
|
|
164
|
-
}
|
|
165
|
-
function resolveDocsI18n(config) {
|
|
166
|
-
if (!config || !Array.isArray(config.locales)) return null;
|
|
167
|
-
const locales = Array.from(new Set(config.locales.map((l) => l.trim()).filter(Boolean)));
|
|
168
|
-
if (locales.length === 0) return null;
|
|
169
|
-
return {
|
|
170
|
-
locales,
|
|
171
|
-
defaultLocale: config.defaultLocale && locales.includes(config.defaultLocale) ? config.defaultLocale : locales[0]
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
function resolveDocsLocale(searchParams, i18n) {
|
|
175
|
-
if (!i18n) return void 0;
|
|
176
|
-
const raw = searchParams.get("lang") ?? searchParams.get("locale");
|
|
177
|
-
if (!raw) return void 0;
|
|
178
|
-
if (i18n.locales.includes(raw)) return raw;
|
|
179
|
-
return i18n.defaultLocale;
|
|
180
|
-
}
|
|
181
|
-
function resolveDocsPath(pathname, entry) {
|
|
182
|
-
const entryBase = normalizeSegment(entry || "docs") || "docs";
|
|
183
|
-
const entryParts = splitSegments(entryBase);
|
|
184
|
-
const pathParts = splitSegments(pathname);
|
|
185
|
-
let rest = pathParts;
|
|
186
|
-
if (entryParts.length > 0) {
|
|
187
|
-
if (pathParts.slice(0, entryParts.length).join("/") === entryParts.join("/")) rest = pathParts.slice(entryParts.length);
|
|
188
|
-
}
|
|
189
|
-
return {
|
|
190
|
-
slug: rest.join("/"),
|
|
191
|
-
entryPath: entryBase
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
//#endregion
|
|
196
|
-
//#region src/metadata.ts
|
|
197
|
-
/**
|
|
198
|
-
* Resolve page title using metadata titleTemplate.
|
|
199
|
-
* %s is replaced with page title.
|
|
200
|
-
*/
|
|
201
|
-
function resolveTitle(pageTitle, metadata) {
|
|
202
|
-
return (metadata?.titleTemplate ?? "%s").replace("%s", pageTitle);
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Resolve OG image URL for a page.
|
|
206
|
-
* Prefers page.openGraph.images[0], then page.ogImage, then config endpoint/default.
|
|
207
|
-
*/
|
|
208
|
-
function resolveOGImage(page, ogConfig, baseUrl) {
|
|
209
|
-
if (page.openGraph?.images?.length) return resolveImageUrl(page.openGraph.images[0].url, baseUrl);
|
|
210
|
-
if (!ogConfig?.enabled) return void 0;
|
|
211
|
-
if (page.ogImage) return resolveImageUrl(page.ogImage, baseUrl);
|
|
212
|
-
if (ogConfig.type === "dynamic" && ogConfig.endpoint) return `${baseUrl ?? ""}${ogConfig.endpoint}`;
|
|
213
|
-
return ogConfig.defaultImage;
|
|
214
|
-
}
|
|
215
|
-
function resolveImageUrl(url, baseUrl) {
|
|
216
|
-
if (url.startsWith("/") || url.startsWith("http")) return url;
|
|
217
|
-
const base = baseUrl ?? "";
|
|
218
|
-
return `${base}${base.length > 0 && !base.endsWith("/") ? "/" : ""}${url}`;
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Build the Open Graph metadata object for a page.
|
|
222
|
-
* When the page has openGraph in frontmatter, uses it (with title/description filled from page if omitted).
|
|
223
|
-
* Otherwise uses ogImage or config (dynamic endpoint / defaultImage).
|
|
224
|
-
*/
|
|
225
|
-
function buildPageOpenGraph(page, ogConfig, baseUrl) {
|
|
226
|
-
if (page.openGraph) {
|
|
227
|
-
const images = page.openGraph.images?.length ? page.openGraph.images.map((img) => ({
|
|
228
|
-
url: resolveImageUrl(img.url, baseUrl),
|
|
229
|
-
width: img.width ?? 1200,
|
|
230
|
-
height: img.height ?? 630
|
|
231
|
-
})) : void 0;
|
|
232
|
-
return {
|
|
233
|
-
title: page.openGraph.title ?? page.title,
|
|
234
|
-
description: page.openGraph.description ?? page.description,
|
|
235
|
-
...images && { images }
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
const url = resolveOGImage(page, ogConfig, baseUrl);
|
|
239
|
-
if (!url) return void 0;
|
|
240
|
-
return {
|
|
241
|
-
title: page.title,
|
|
242
|
-
...page.description && { description: page.description },
|
|
243
|
-
images: [{
|
|
244
|
-
url,
|
|
245
|
-
width: 1200,
|
|
246
|
-
height: 630
|
|
247
|
-
}]
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Build the Twitter card metadata object for a page.
|
|
252
|
-
* When the page has twitter in frontmatter, uses it.
|
|
253
|
-
* Otherwise builds from ogImage or config (dynamic endpoint).
|
|
254
|
-
*/
|
|
255
|
-
function buildPageTwitter(page, ogConfig, baseUrl) {
|
|
256
|
-
if (page.twitter) {
|
|
257
|
-
const images = page.twitter.images?.length ? page.twitter.images.map((url) => resolveImageUrl(url, baseUrl)) : void 0;
|
|
258
|
-
return {
|
|
259
|
-
...page.twitter.card && { card: page.twitter.card },
|
|
260
|
-
...page.twitter.title !== void 0 && { title: page.twitter.title },
|
|
261
|
-
...page.twitter.description !== void 0 && { description: page.twitter.description },
|
|
262
|
-
...images && { images }
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
const url = resolveOGImage(page, ogConfig, baseUrl);
|
|
266
|
-
if (!url) return void 0;
|
|
267
|
-
return {
|
|
268
|
-
card: "summary_large_image",
|
|
269
|
-
title: page.title,
|
|
270
|
-
...page.description && { description: page.description },
|
|
271
|
-
images: [url]
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
//#endregion
|
|
276
|
-
//#region src/reading-time.ts
|
|
277
|
-
function hasExplicitReadingTime(frontmatter) {
|
|
278
|
-
return Object.prototype.hasOwnProperty.call(frontmatter ?? {}, "readingTime");
|
|
279
|
-
}
|
|
280
|
-
function normalizeWordsPerMinute(wordsPerMinute) {
|
|
281
|
-
if (typeof wordsPerMinute !== "number" || !Number.isFinite(wordsPerMinute)) return 220;
|
|
282
|
-
return Math.max(1, Math.floor(wordsPerMinute));
|
|
283
|
-
}
|
|
284
|
-
function stripNonReadingContent(content) {
|
|
285
|
-
return content.replace(/(`{3,})[^\n]*\n[\s\S]*?\1/g, " ").replace(/(~{3,})[^\n]*\n[\s\S]*?\1/g, " ").replace(/`[^`\n]+`/g, " ").replace(/!\[[^\]]*\]\([^)]+\)/g, " ").replace(/\[([^\]]+)\]\([^)]+\)/g, " $1 ").replace(/<[^>]+>/g, " ").replace(/\{[^{}]*\}/g, " ").replace(/https?:\/\/\S+/g, " ").replace(/[#>*_~|]/g, " ");
|
|
286
|
-
}
|
|
287
|
-
function estimateReadingTimeMinutes(content, wordsPerMinute) {
|
|
288
|
-
const wordCount = stripNonReadingContent(content).match(/\b[\p{L}\p{N}][\p{L}\p{N}'’-]*\b/gu)?.length ?? 0;
|
|
289
|
-
return Math.max(1, Math.ceil(wordCount / normalizeWordsPerMinute(wordsPerMinute)));
|
|
290
|
-
}
|
|
291
|
-
function resolveReadingTimeOptions(readingTime) {
|
|
292
|
-
if (readingTime === true) return { enabled: true };
|
|
293
|
-
if (readingTime === false || readingTime === void 0 || readingTime === null) return { enabled: false };
|
|
294
|
-
if (typeof readingTime !== "object") return { enabled: false };
|
|
295
|
-
return {
|
|
296
|
-
enabled: readingTime.enabled !== false,
|
|
297
|
-
wordsPerMinute: typeof readingTime.wordsPerMinute === "number" && Number.isFinite(readingTime.wordsPerMinute) ? readingTime.wordsPerMinute : void 0
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
function resolveReadingTimeFromContent(frontmatter, content, wordsPerMinute) {
|
|
301
|
-
const pageData = frontmatter ?? {};
|
|
302
|
-
if (pageData.readingTime === false) return null;
|
|
303
|
-
if (typeof pageData.readingTime === "number" && Number.isFinite(pageData.readingTime)) return Math.max(1, Math.ceil(pageData.readingTime));
|
|
304
|
-
return estimateReadingTimeMinutes(content, wordsPerMinute);
|
|
305
|
-
}
|
|
306
|
-
function resolvePageReadingTime(frontmatter, content, options) {
|
|
307
|
-
if (!(options?.enabledByDefault ?? false) && !hasExplicitReadingTime(frontmatter)) return;
|
|
308
|
-
return resolveReadingTimeFromContent(frontmatter, content, options?.wordsPerMinute);
|
|
309
|
-
}
|
|
310
|
-
function resolveReadingTimeFromSource(source, wordsPerMinute) {
|
|
311
|
-
const { data, content } = matter(source);
|
|
312
|
-
return resolveReadingTimeFromContent(data, content, wordsPerMinute);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
//#endregion
|
|
316
4
|
//#region src/agent.ts
|
|
317
5
|
const DEFAULT_DOCS_API_ROUTE = "/api/docs";
|
|
318
6
|
const DEFAULT_AGENT_SPEC_ROUTE = "/api/docs/agent/spec";
|
|
@@ -713,4 +401,167 @@ function toYamlString(value) {
|
|
|
713
401
|
}
|
|
714
402
|
|
|
715
403
|
//#endregion
|
|
716
|
-
|
|
404
|
+
//#region src/robots.ts
|
|
405
|
+
const DEFAULT_ROBOTS_TXT_ROUTE = "/robots.txt";
|
|
406
|
+
const DOCS_ROBOTS_GENERATED_BLOCK_START = "# BEGIN @farming-labs/docs robots";
|
|
407
|
+
const DOCS_ROBOTS_GENERATED_BLOCK_END = "# END @farming-labs/docs robots";
|
|
408
|
+
const DEFAULT_DOCS_AI_ROBOTS_USER_AGENTS = [
|
|
409
|
+
"GPTBot",
|
|
410
|
+
"ChatGPT-User",
|
|
411
|
+
"OAI-SearchBot",
|
|
412
|
+
"ClaudeBot",
|
|
413
|
+
"Claude-User",
|
|
414
|
+
"anthropic-ai",
|
|
415
|
+
"CCBot",
|
|
416
|
+
"Google-Extended"
|
|
417
|
+
];
|
|
418
|
+
function normalizeRoute(value) {
|
|
419
|
+
const trimmed = value.trim();
|
|
420
|
+
if (!trimmed) return "/";
|
|
421
|
+
return trimmed.startsWith("/") ? trimmed.replace(/\/{2,}/g, "/") : `/${trimmed}`;
|
|
422
|
+
}
|
|
423
|
+
function normalizeBaseUrl(value) {
|
|
424
|
+
if (!value) return void 0;
|
|
425
|
+
try {
|
|
426
|
+
const url = new URL(value);
|
|
427
|
+
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
428
|
+
url.search = "";
|
|
429
|
+
url.hash = "";
|
|
430
|
+
return url.toString().replace(/\/+$/, "");
|
|
431
|
+
} catch {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function normalizeStringArray(value) {
|
|
436
|
+
if (Array.isArray(value)) return value;
|
|
437
|
+
return value ? [value] : [];
|
|
438
|
+
}
|
|
439
|
+
function normalizeAiPolicy(value) {
|
|
440
|
+
return value === false || value === "disallow" ? "disallow" : "allow";
|
|
441
|
+
}
|
|
442
|
+
function unique(values) {
|
|
443
|
+
return [...new Set(values.filter(Boolean))];
|
|
444
|
+
}
|
|
445
|
+
function resolveDocsRobotsConfig(input, options = {}) {
|
|
446
|
+
if (input === false) return {
|
|
447
|
+
enabled: false,
|
|
448
|
+
ai: "allow",
|
|
449
|
+
userAgents: [...DEFAULT_DOCS_AI_ROBOTS_USER_AGENTS],
|
|
450
|
+
extraRules: []
|
|
451
|
+
};
|
|
452
|
+
const config = typeof input === "object" ? input : {};
|
|
453
|
+
return {
|
|
454
|
+
enabled: config.enabled ?? true,
|
|
455
|
+
path: config.path,
|
|
456
|
+
baseUrl: normalizeBaseUrl(config.baseUrl) ?? normalizeBaseUrl(options.baseUrl),
|
|
457
|
+
ai: normalizeAiPolicy(config.ai),
|
|
458
|
+
userAgents: unique([...DEFAULT_DOCS_AI_ROBOTS_USER_AGENTS, ...normalizeStringArray(config.aiUserAgents)]),
|
|
459
|
+
extraRules: config.extraRules ?? []
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function getDocsRobotsAllowRoutes(options = {}) {
|
|
463
|
+
const normalizedEntry = normalizeDocsPathSegment(options.entry ?? "docs") || "docs";
|
|
464
|
+
const sitemapConfig = resolveDocsSitemapConfig(options.sitemap);
|
|
465
|
+
const routes = [
|
|
466
|
+
"/",
|
|
467
|
+
`/${normalizedEntry}`,
|
|
468
|
+
`/${normalizedEntry}/`,
|
|
469
|
+
`/${normalizedEntry}.md`,
|
|
470
|
+
`/${normalizedEntry}/*.md`,
|
|
471
|
+
DEFAULT_LLMS_TXT_ROUTE,
|
|
472
|
+
DEFAULT_LLMS_FULL_TXT_ROUTE,
|
|
473
|
+
DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE,
|
|
474
|
+
DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE,
|
|
475
|
+
DEFAULT_SKILL_MD_ROUTE,
|
|
476
|
+
DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE,
|
|
477
|
+
DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE,
|
|
478
|
+
DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE,
|
|
479
|
+
DEFAULT_MCP_PUBLIC_ROUTE,
|
|
480
|
+
DEFAULT_MCP_WELL_KNOWN_ROUTE
|
|
481
|
+
];
|
|
482
|
+
if (sitemapConfig.enabled) {
|
|
483
|
+
if (sitemapConfig.xml.enabled) routes.push(sitemapConfig.xml.route);
|
|
484
|
+
if (sitemapConfig.markdown.enabled) routes.push(sitemapConfig.markdown.route, sitemapConfig.markdown.wellKnownRoute);
|
|
485
|
+
}
|
|
486
|
+
return unique(routes.map(normalizeRoute));
|
|
487
|
+
}
|
|
488
|
+
function sitemapUrls(robots, sitemap) {
|
|
489
|
+
const sitemapConfig = resolveDocsSitemapConfig(sitemap, { baseUrl: robots.baseUrl });
|
|
490
|
+
if (!robots.baseUrl || !sitemapConfig.enabled || !sitemapConfig.xml.enabled) return [];
|
|
491
|
+
try {
|
|
492
|
+
return [new URL(sitemapConfig.xml.route, `${robots.baseUrl}/`).toString()];
|
|
493
|
+
} catch {
|
|
494
|
+
return [];
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
function appendRule(lines, rule) {
|
|
498
|
+
const userAgents = normalizeStringArray(rule.userAgent);
|
|
499
|
+
if (userAgents.length === 0) return;
|
|
500
|
+
lines.push("");
|
|
501
|
+
for (const userAgent of userAgents) lines.push(`User-agent: ${userAgent}`);
|
|
502
|
+
for (const route of normalizeStringArray(rule.allow)) lines.push(`Allow: ${normalizeRoute(route)}`);
|
|
503
|
+
for (const route of normalizeStringArray(rule.disallow)) lines.push(`Disallow: ${normalizeRoute(route)}`);
|
|
504
|
+
if (typeof rule.crawlDelay === "number" && Number.isFinite(rule.crawlDelay)) lines.push(`Crawl-delay: ${rule.crawlDelay}`);
|
|
505
|
+
}
|
|
506
|
+
function renderDocsRobotsTxt(options = {}) {
|
|
507
|
+
const robots = resolveDocsRobotsConfig(options.robots, { baseUrl: options.baseUrl });
|
|
508
|
+
if (!robots.enabled) return "";
|
|
509
|
+
const lines = [
|
|
510
|
+
"# Generated by @farming-labs/docs.",
|
|
511
|
+
"# Edit docs.config or rerun `docs robots generate --append` / `--force`.",
|
|
512
|
+
"",
|
|
513
|
+
"User-agent: *"
|
|
514
|
+
];
|
|
515
|
+
for (const route of getDocsRobotsAllowRoutes(options)) lines.push(`Allow: ${route}`);
|
|
516
|
+
for (const userAgent of robots.userAgents) {
|
|
517
|
+
lines.push("", `User-agent: ${userAgent}`);
|
|
518
|
+
lines.push(robots.ai === "allow" ? "Allow: /" : "Disallow: /");
|
|
519
|
+
}
|
|
520
|
+
for (const rule of robots.extraRules) appendRule(lines, rule);
|
|
521
|
+
const sitemap = sitemapUrls(robots, options.sitemap);
|
|
522
|
+
if (sitemap.length > 0) {
|
|
523
|
+
lines.push("");
|
|
524
|
+
for (const url of sitemap) lines.push(`Sitemap: ${url}`);
|
|
525
|
+
}
|
|
526
|
+
return `${lines.join("\n")}\n`;
|
|
527
|
+
}
|
|
528
|
+
function renderDocsRobotsGeneratedBlock(options = {}) {
|
|
529
|
+
return `${DOCS_ROBOTS_GENERATED_BLOCK_START}\n${renderDocsRobotsTxt(options).trimEnd()}\n${DOCS_ROBOTS_GENERATED_BLOCK_END}\n`;
|
|
530
|
+
}
|
|
531
|
+
function upsertDocsRobotsGeneratedBlock(existing, block) {
|
|
532
|
+
const escapedStart = DOCS_ROBOTS_GENERATED_BLOCK_START.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
533
|
+
const escapedEnd = DOCS_ROBOTS_GENERATED_BLOCK_END.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
534
|
+
const pattern = new RegExp(`${escapedStart}[\\s\\S]*?${escapedEnd}\\n?`);
|
|
535
|
+
if (pattern.test(existing)) return existing.replace(pattern, block);
|
|
536
|
+
const prefix = existing.trimEnd();
|
|
537
|
+
return `${prefix}${prefix ? "\n\n" : ""}${block}`;
|
|
538
|
+
}
|
|
539
|
+
function blockForUserAgent(content, userAgent) {
|
|
540
|
+
const escaped = userAgent.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
541
|
+
return content.match(new RegExp(`(?:^|\\n)\\s*User-agent:\\s*${escaped}\\s*(?:\\n[ \\t]*(?!User-agent:).*)*`, "i"))?.[0] ?? "";
|
|
542
|
+
}
|
|
543
|
+
function disallowsRoute(content, route) {
|
|
544
|
+
const escapedRoute = route.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
545
|
+
return new RegExp(`^\\s*Disallow:\\s*${escapedRoute}(?:\\s|$)`, "im").test(content);
|
|
546
|
+
}
|
|
547
|
+
function analyzeDocsRobotsTxt(content, options = {}) {
|
|
548
|
+
const expectedRoutes = getDocsRobotsAllowRoutes(options).filter((route) => [
|
|
549
|
+
DEFAULT_LLMS_TXT_ROUTE,
|
|
550
|
+
DEFAULT_LLMS_FULL_TXT_ROUTE,
|
|
551
|
+
DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE,
|
|
552
|
+
DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE,
|
|
553
|
+
DEFAULT_SKILL_MD_ROUTE,
|
|
554
|
+
DEFAULT_MCP_PUBLIC_ROUTE
|
|
555
|
+
].includes(route) || route.includes("sitemap"));
|
|
556
|
+
const missingRoutes = expectedRoutes.filter((route) => !content.includes(route));
|
|
557
|
+
return {
|
|
558
|
+
blocksAgentRoutes: /Disallow:\s*\/(?:\s|$)/i.test(blockForUserAgent(content, "*")) || expectedRoutes.some((route) => disallowsRoute(content, route)),
|
|
559
|
+
blocksAiAgents: DEFAULT_DOCS_AI_ROBOTS_USER_AGENTS.some((userAgent) => /Disallow:\s*\/(?:\s|$)/i.test(blockForUserAgent(content, userAgent))),
|
|
560
|
+
hasAgentRoutes: missingRoutes.length === 0,
|
|
561
|
+
hasAiPolicy: DEFAULT_DOCS_AI_ROBOTS_USER_AGENTS.some((userAgent) => content.includes(userAgent)),
|
|
562
|
+
missingRoutes
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
//#endregion
|
|
567
|
+
export { isDocsAgentDiscoveryRequest as A, resolveDocsLlmsTxtFormat as B, DEFAULT_SKILL_MD_ROUTE as C, findDocsMarkdownPage as D, buildDocsAgentDiscoverySpec as E, normalizeDocsUrlPath as F, resolveDocsSkillFormat as H, renderDocsMarkdownDocument as I, renderDocsMarkdownNotFound as L, isDocsPublicGetRequest as M, isDocsSkillRequest as N, getDocsMarkdownVaryHeader as O, normalizeDocsPathSegment as P, renderDocsSkillDocument as R, DEFAULT_MCP_WELL_KNOWN_ROUTE as S, DOCS_MARKDOWN_SIGNATURE_AGENT_HEADER as T, toDocsMarkdownUrl as U, resolveDocsMarkdownRequest as V, DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE as _, analyzeDocsRobotsTxt as a, DEFAULT_MCP_PUBLIC_ROUTE as b, renderDocsRobotsTxt as c, DEFAULT_AGENT_FEEDBACK_ROUTE as d, DEFAULT_AGENT_SPEC_ROUTE as f, DEFAULT_LLMS_FULL_TXT_ROUTE as g, DEFAULT_DOCS_API_ROUTE as h, DOCS_ROBOTS_GENERATED_BLOCK_START as i, isDocsMcpRequest as j, hasDocsMarkdownSignatureAgent as k, resolveDocsRobotsConfig as l, DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE as m, DEFAULT_ROBOTS_TXT_ROUTE as n, getDocsRobotsAllowRoutes as o, DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE as p, DOCS_ROBOTS_GENERATED_BLOCK_END as r, renderDocsRobotsGeneratedBlock as s, DEFAULT_DOCS_AI_ROBOTS_USER_AGENTS as t, upsertDocsRobotsGeneratedBlock as u, DEFAULT_LLMS_TXT_ROUTE as v, DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE as w, DEFAULT_MCP_ROUTE as x, DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE as y, resolveDocsAgentMdxContent as z };
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { i as DOCS_ROBOTS_GENERATED_BLOCK_START, l as resolveDocsRobotsConfig, r as DOCS_ROBOTS_GENERATED_BLOCK_END, s as renderDocsRobotsGeneratedBlock, u as upsertDocsRobotsGeneratedBlock } from "./robots-DCR-ZFLO.mjs";
|
|
2
|
+
import { d as readTopLevelStringProperty, f as resolveDocsConfigPath, i as loadDocsConfigModule, o as readBooleanProperty, t as extractNestedObjectLiteral, u as readStringProperty } from "./config-BR6CcCfr.mjs";
|
|
3
|
+
import { t as detectFramework } from "./utils-AmYxHDoz.mjs";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import pc from "picocolors";
|
|
7
|
+
|
|
8
|
+
//#region src/cli/robots.ts
|
|
9
|
+
function parseInlineFlag(arg) {
|
|
10
|
+
const [rawKey, value] = arg.slice(2).split("=", 2);
|
|
11
|
+
return {
|
|
12
|
+
key: rawKey.trim(),
|
|
13
|
+
value
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function parseRobotsGenerateArgs(argv) {
|
|
17
|
+
const parsed = {};
|
|
18
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
19
|
+
const arg = argv[index];
|
|
20
|
+
if (arg === "--help" || arg === "-h") {
|
|
21
|
+
parsed.help = true;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (arg === "--append") {
|
|
25
|
+
parsed.append = true;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (arg === "--force") {
|
|
29
|
+
parsed.force = true;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (arg === "--check") {
|
|
33
|
+
parsed.check = true;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (arg.startsWith("--config=")) {
|
|
37
|
+
const value = parseInlineFlag(arg).value;
|
|
38
|
+
if (!value) throw new Error("Missing value for --config.");
|
|
39
|
+
parsed.configPath = value;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (arg === "--config") {
|
|
43
|
+
const value = argv[index + 1];
|
|
44
|
+
if (!value || value.startsWith("--")) throw new Error("Missing value for --config.");
|
|
45
|
+
parsed.configPath = value;
|
|
46
|
+
index += 1;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (arg.startsWith("--path=")) {
|
|
50
|
+
const value = parseInlineFlag(arg).value;
|
|
51
|
+
if (!value) throw new Error("Missing value for --path.");
|
|
52
|
+
parsed.path = value;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (arg === "--path") {
|
|
56
|
+
const value = argv[index + 1];
|
|
57
|
+
if (!value || value.startsWith("--")) throw new Error("Missing value for --path.");
|
|
58
|
+
parsed.path = value;
|
|
59
|
+
index += 1;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (!arg.startsWith("--") && !parsed.path) {
|
|
63
|
+
parsed.path = arg;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
throw new Error(`Unknown robots generate flag: ${arg}.`);
|
|
67
|
+
}
|
|
68
|
+
if (parsed.append && parsed.force) throw new Error("Use either --append or --force, not both.");
|
|
69
|
+
return parsed;
|
|
70
|
+
}
|
|
71
|
+
function readTopLevelBooleanProperty(content, key) {
|
|
72
|
+
const match = content.match(new RegExp(`\\b${key}\\b\\s*:\\s*(true|false)`));
|
|
73
|
+
return match ? match[1] === "true" : void 0;
|
|
74
|
+
}
|
|
75
|
+
function readLlmsBaseUrlFromConfig(content, config) {
|
|
76
|
+
if (config?.llmsTxt && typeof config.llmsTxt === "object") return config.llmsTxt.baseUrl;
|
|
77
|
+
const block = extractNestedObjectLiteral(content, ["llmsTxt"]);
|
|
78
|
+
return block ? readStringProperty(block, "baseUrl") : void 0;
|
|
79
|
+
}
|
|
80
|
+
function readSitemapConfigFromStatic(content) {
|
|
81
|
+
const topLevelBoolean = readTopLevelBooleanProperty(content, "sitemap");
|
|
82
|
+
if (typeof topLevelBoolean === "boolean") return topLevelBoolean;
|
|
83
|
+
const block = extractNestedObjectLiteral(content, ["sitemap"]);
|
|
84
|
+
if (!block) return void 0;
|
|
85
|
+
return {
|
|
86
|
+
enabled: readBooleanProperty(block, "enabled") ?? true,
|
|
87
|
+
routePrefix: readStringProperty(block, "routePrefix"),
|
|
88
|
+
baseUrl: readStringProperty(block, "baseUrl"),
|
|
89
|
+
manifestPath: readStringProperty(block, "manifestPath")
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function readRobotsConfigFromStatic(content) {
|
|
93
|
+
const topLevelBoolean = readTopLevelBooleanProperty(content, "robots");
|
|
94
|
+
if (typeof topLevelBoolean === "boolean") return topLevelBoolean;
|
|
95
|
+
const block = extractNestedObjectLiteral(content, ["robots"]);
|
|
96
|
+
if (!block) return void 0;
|
|
97
|
+
const aiString = readStringProperty(block, "ai");
|
|
98
|
+
const aiBoolean = readBooleanProperty(block, "ai");
|
|
99
|
+
return {
|
|
100
|
+
enabled: readBooleanProperty(block, "enabled") ?? true,
|
|
101
|
+
path: readStringProperty(block, "path"),
|
|
102
|
+
baseUrl: readStringProperty(block, "baseUrl"),
|
|
103
|
+
ai: aiString === "allow" || aiString === "disallow" ? aiString : typeof aiBoolean === "boolean" ? aiBoolean : void 0
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function resolveConfiguredRobots(content, config) {
|
|
107
|
+
if (config?.robots !== void 0) return config.robots;
|
|
108
|
+
return readRobotsConfigFromStatic(content) ?? true;
|
|
109
|
+
}
|
|
110
|
+
function resolvePublicDir(rootDir) {
|
|
111
|
+
if (detectFramework(rootDir) === "sveltekit") return path.join(rootDir, "static");
|
|
112
|
+
return path.join(rootDir, "public");
|
|
113
|
+
}
|
|
114
|
+
function resolveRobotsPath(rootDir, options, robots) {
|
|
115
|
+
const configuredPath = options.path ?? robots?.path;
|
|
116
|
+
if (configuredPath) return path.isAbsolute(configuredPath) ? configuredPath : path.resolve(rootDir, configuredPath);
|
|
117
|
+
return path.join(resolvePublicDir(rootDir), "robots.txt");
|
|
118
|
+
}
|
|
119
|
+
function writeIfChanged(filePath, content, check) {
|
|
120
|
+
if ((existsSync(filePath) ? readFileSync(filePath, "utf-8") : void 0) === content) return false;
|
|
121
|
+
if (check) return true;
|
|
122
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
123
|
+
writeFileSync(filePath, content, "utf-8");
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
async function generateRobots(options = {}) {
|
|
127
|
+
const rootDir = process.cwd();
|
|
128
|
+
const loadedConfigModule = await loadDocsConfigModule(rootDir, options.configPath);
|
|
129
|
+
const configContent = readFileSync(loadedConfigModule?.path ?? resolveDocsConfigPath(rootDir, options.configPath), "utf-8");
|
|
130
|
+
const config = loadedConfigModule?.config;
|
|
131
|
+
const entry = config?.entry ?? readTopLevelStringProperty(configContent, "entry") ?? "docs";
|
|
132
|
+
const sitemap = config?.sitemap ?? readSitemapConfigFromStatic(configContent) ?? true;
|
|
133
|
+
const sitemapBaseUrl = typeof sitemap === "object" ? sitemap.baseUrl : void 0;
|
|
134
|
+
const llmsBaseUrl = readLlmsBaseUrlFromConfig(configContent, config);
|
|
135
|
+
const configuredRobots = resolveConfiguredRobots(configContent, config);
|
|
136
|
+
if (configuredRobots === false) throw new Error("Robots generation is disabled by `robots: false`.");
|
|
137
|
+
const robotsInput = typeof configuredRobots === "object" ? configuredRobots : {};
|
|
138
|
+
const robots = resolveDocsRobotsConfig(robotsInput, { baseUrl: robotsInput.baseUrl ?? sitemapBaseUrl ?? llmsBaseUrl });
|
|
139
|
+
if (!robots.enabled) throw new Error("Robots generation is disabled by `robots.enabled: false`.");
|
|
140
|
+
const robotsPath = resolveRobotsPath(rootDir, options, robotsInput);
|
|
141
|
+
const relativeRobotsPath = path.relative(rootDir, robotsPath).replace(/\\/g, "/");
|
|
142
|
+
const generatedBlock = renderDocsRobotsGeneratedBlock({
|
|
143
|
+
entry,
|
|
144
|
+
sitemap,
|
|
145
|
+
baseUrl: robots.baseUrl,
|
|
146
|
+
robots
|
|
147
|
+
});
|
|
148
|
+
const existing = existsSync(robotsPath) ? readFileSync(robotsPath, "utf-8") : void 0;
|
|
149
|
+
const hasGeneratedBlock = existing?.includes(DOCS_ROBOTS_GENERATED_BLOCK_START) === true && existing.includes(DOCS_ROBOTS_GENERATED_BLOCK_END);
|
|
150
|
+
if (existing !== void 0 && !options.force && !options.append && !hasGeneratedBlock) {
|
|
151
|
+
console.log(pc.yellow(`Found existing robots.txt at ${relativeRobotsPath}.`));
|
|
152
|
+
console.log(pc.dim("Keeping the user-owned file. Use --append to add/update the generated block, or --force to replace it."));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const changed = writeIfChanged(robotsPath, existing !== void 0 && (options.append || hasGeneratedBlock) ? upsertDocsRobotsGeneratedBlock(existing, generatedBlock) : generatedBlock, options.check === true);
|
|
156
|
+
if (options.check && changed) throw new Error(`Robots output is stale at ${relativeRobotsPath}. Run \`docs robots generate${options.append ? " --append" : options.force ? " --force" : ""}${options.path ? ` --path ${options.path}` : ""}\` to update it.`);
|
|
157
|
+
console.log(changed ? pc.green(`Generated robots policy at ${relativeRobotsPath}.`) : pc.green(`Robots policy is current at ${relativeRobotsPath}.`));
|
|
158
|
+
}
|
|
159
|
+
function printRobotsGenerateHelp() {
|
|
160
|
+
console.log(`
|
|
161
|
+
${pc.bold("docs robots generate")} — Generate a static robots.txt agent access policy.
|
|
162
|
+
|
|
163
|
+
${pc.dim("Usage:")}
|
|
164
|
+
pnpm exec docs ${pc.cyan("robots generate")} ${pc.dim("[path]")}
|
|
165
|
+
|
|
166
|
+
${pc.dim("Options:")}
|
|
167
|
+
${pc.cyan("--path <path>")} Write to a specific robots.txt path; defaults to the framework public directory
|
|
168
|
+
${pc.cyan("--append")} Add or update a generated block inside an existing robots.txt
|
|
169
|
+
${pc.cyan("--force")} Replace the target robots.txt with the generated policy
|
|
170
|
+
${pc.cyan("--check")} Fail if the generated output would change
|
|
171
|
+
${pc.cyan("--config <path>")} Use a custom docs config path instead of ${pc.dim("docs.config.ts[x]")}
|
|
172
|
+
${pc.cyan("-h, --help")} Show this help message
|
|
173
|
+
`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
//#endregion
|
|
177
|
+
export { generateRobots, parseRobotsGenerateArgs, printRobotsGenerateHelp };
|