@farming-labs/docs 0.0.38 → 0.0.44
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/cli/index.d.mts +1 -1
- package/dist/cli/index.mjs +325 -88
- package/dist/index.d.mts +3 -1283
- package/dist/index.mjs +2 -1
- package/dist/server.d.mts +29 -0
- package/dist/server.mjs +521 -0
- package/dist/types-Cg_hJBKj.d.mts +1330 -0
- package/package.json +7 -1
package/dist/index.mjs
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { n as ApiReferenceConfig, o as DocsConfig } from "./types-Cg_hJBKj.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/api-reference.d.ts
|
|
4
|
+
type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "HEAD";
|
|
5
|
+
type ApiReferenceFramework = "next" | "tanstack-start" | "sveltekit" | "astro" | "nuxt";
|
|
6
|
+
interface ApiReferenceRoute {
|
|
7
|
+
title: string;
|
|
8
|
+
summary: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
routePath: string;
|
|
11
|
+
sourceFile: string;
|
|
12
|
+
methods: HttpMethod[];
|
|
13
|
+
tag: string;
|
|
14
|
+
parameters: Array<Record<string, unknown>>;
|
|
15
|
+
}
|
|
16
|
+
interface BuildApiReferenceOptions {
|
|
17
|
+
framework: ApiReferenceFramework;
|
|
18
|
+
rootDir?: string;
|
|
19
|
+
}
|
|
20
|
+
interface BuildApiReferenceHtmlOptions extends BuildApiReferenceOptions {
|
|
21
|
+
title?: string;
|
|
22
|
+
}
|
|
23
|
+
declare function resolveApiReferenceConfig(value: DocsConfig["apiReference"]): Required<ApiReferenceConfig>;
|
|
24
|
+
declare function buildApiReferencePageTitle(config: DocsConfig, title?: string): string;
|
|
25
|
+
declare function buildApiReferenceScalarCss(config: DocsConfig): string;
|
|
26
|
+
declare function buildApiReferenceOpenApiDocument(config: DocsConfig, options: BuildApiReferenceOptions): Record<string, unknown>;
|
|
27
|
+
declare function buildApiReferenceHtmlDocument(config: DocsConfig, options: BuildApiReferenceHtmlOptions): string;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { type ApiReferenceFramework, type ApiReferenceRoute, buildApiReferenceHtmlDocument, buildApiReferenceOpenApiDocument, buildApiReferencePageTitle, buildApiReferenceScalarCss, resolveApiReferenceConfig };
|
package/dist/server.mjs
ADDED
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, join, relative } from "node:path";
|
|
3
|
+
import { getHtmlDocument } from "@scalar/core/libs/html-rendering";
|
|
4
|
+
|
|
5
|
+
//#region src/api-reference.ts
|
|
6
|
+
const NEXT_ROUTE_FILE_RE = /^route\.(ts|tsx|js|jsx)$/;
|
|
7
|
+
const SVELTE_ROUTE_FILE_RE = /^\+server\.(ts|js)$/;
|
|
8
|
+
const ASTRO_ROUTE_FILE_RE = /^[^.].*\.(ts|js|mts|mjs)$/;
|
|
9
|
+
const NUXT_ROUTE_FILE_RE = /^[^.].*\.(ts|js|mts|mjs)$/;
|
|
10
|
+
const TANSTACK_ROUTE_FILE_RE = /\.(ts|tsx|js|jsx)$/;
|
|
11
|
+
const METHOD_RE = /export\s+(?:async\s+function|function|const)\s+(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD|ALL)\b/g;
|
|
12
|
+
const METHOD_NAMES = [
|
|
13
|
+
"GET",
|
|
14
|
+
"POST",
|
|
15
|
+
"PUT",
|
|
16
|
+
"PATCH",
|
|
17
|
+
"DELETE",
|
|
18
|
+
"OPTIONS",
|
|
19
|
+
"HEAD"
|
|
20
|
+
];
|
|
21
|
+
function normalizePathSegment(value) {
|
|
22
|
+
return value.replace(/^\/+|\/+$/g, "");
|
|
23
|
+
}
|
|
24
|
+
function resolveApiReferenceConfig(value) {
|
|
25
|
+
if (value === true) return {
|
|
26
|
+
enabled: true,
|
|
27
|
+
path: "api-reference",
|
|
28
|
+
routeRoot: "api",
|
|
29
|
+
exclude: []
|
|
30
|
+
};
|
|
31
|
+
if (!value) return {
|
|
32
|
+
enabled: false,
|
|
33
|
+
path: "api-reference",
|
|
34
|
+
routeRoot: "api",
|
|
35
|
+
exclude: []
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
enabled: value.enabled !== false,
|
|
39
|
+
path: normalizePathSegment(value.path ?? "api-reference"),
|
|
40
|
+
routeRoot: normalizePathSegment(value.routeRoot ?? "api") || "api",
|
|
41
|
+
exclude: normalizeApiReferenceExcludes(value.exclude)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function buildApiReferencePageTitle(config, title = "API Reference") {
|
|
45
|
+
const template = config.metadata?.titleTemplate;
|
|
46
|
+
if (!template) return title;
|
|
47
|
+
return template.replace("%s", title);
|
|
48
|
+
}
|
|
49
|
+
function buildApiReferenceScalarCss(config) {
|
|
50
|
+
const theme = resolveTheme(config);
|
|
51
|
+
const colors = theme?.ui?.colors;
|
|
52
|
+
const typography = theme?.ui?.typography?.font?.style;
|
|
53
|
+
const layout = theme?.ui?.layout;
|
|
54
|
+
const primary = colors?.primary ?? "#6366f1";
|
|
55
|
+
const border = colors?.border ?? "#2a2a2a";
|
|
56
|
+
const muted = colors?.muted ?? "#64748b";
|
|
57
|
+
const background = colors?.background ?? "#ffffff";
|
|
58
|
+
const card = colors?.card ?? background;
|
|
59
|
+
const foreground = colors?.foreground ?? "#1b1b1b";
|
|
60
|
+
const sidebarWidth = layout?.sidebarWidth ?? 280;
|
|
61
|
+
return `
|
|
62
|
+
:root {
|
|
63
|
+
--scalar-font: ${typography?.sans ?? "\"Geist\", \"Inter\", \"Segoe UI\", sans-serif"};
|
|
64
|
+
--scalar-font-code: ${typography?.mono ?? "\"Geist Mono\", \"SFMono-Regular\", \"Menlo\", monospace"};
|
|
65
|
+
--scalar-theme-primary: ${primary};
|
|
66
|
+
--scalar-theme-border: ${border};
|
|
67
|
+
--scalar-theme-muted: ${muted};
|
|
68
|
+
--scalar-theme-background: ${background};
|
|
69
|
+
--scalar-theme-card: ${card};
|
|
70
|
+
--scalar-theme-foreground: ${foreground};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.dark-mode {
|
|
74
|
+
--scalar-background-1: color-mix(in srgb, #0b0c0b 98%, var(--scalar-theme-primary) 2%);
|
|
75
|
+
--scalar-background-2: color-mix(in srgb, #111311 96%, var(--scalar-theme-primary) 4%);
|
|
76
|
+
--scalar-background-3: color-mix(in srgb, #171917 95%, var(--scalar-theme-primary) 5%);
|
|
77
|
+
--scalar-color-1: rgba(255, 255, 255, 0.96);
|
|
78
|
+
--scalar-color-2: rgba(255, 255, 255, 0.72);
|
|
79
|
+
--scalar-color-3: rgba(255, 255, 255, 0.5);
|
|
80
|
+
--scalar-color-accent: var(--scalar-theme-primary);
|
|
81
|
+
--scalar-sidebar-color-active: var(--scalar-theme-primary);
|
|
82
|
+
--scalar-sidebar-item-active-background: color-mix(
|
|
83
|
+
in srgb,
|
|
84
|
+
var(--scalar-theme-primary) 7%,
|
|
85
|
+
transparent
|
|
86
|
+
);
|
|
87
|
+
--scalar-border-color: color-mix(
|
|
88
|
+
in srgb,
|
|
89
|
+
var(--scalar-theme-border) 14%,
|
|
90
|
+
rgba(255, 255, 255, 0.02)
|
|
91
|
+
);
|
|
92
|
+
--scalar-button-1: var(--scalar-theme-primary);
|
|
93
|
+
--scalar-button-1-color: #ffffff;
|
|
94
|
+
--scalar-button-1-hover: color-mix(in srgb, var(--scalar-theme-primary) 88%, white 12%);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.light-mode {
|
|
98
|
+
--scalar-background-1: var(--scalar-theme-background);
|
|
99
|
+
--scalar-background-2: color-mix(in srgb, var(--scalar-theme-card) 92%, white 8%);
|
|
100
|
+
--scalar-background-3: color-mix(in srgb, var(--scalar-theme-card) 84%, black 4%);
|
|
101
|
+
--scalar-color-1: var(--scalar-theme-foreground);
|
|
102
|
+
--scalar-color-2: var(--scalar-theme-muted);
|
|
103
|
+
--scalar-color-3: color-mix(in srgb, var(--scalar-theme-muted) 78%, white 22%);
|
|
104
|
+
--scalar-color-accent: var(--scalar-theme-primary);
|
|
105
|
+
--scalar-sidebar-color-active: var(--scalar-theme-primary);
|
|
106
|
+
--scalar-sidebar-item-active-background: color-mix(
|
|
107
|
+
in srgb,
|
|
108
|
+
var(--scalar-theme-primary) 5%,
|
|
109
|
+
transparent
|
|
110
|
+
);
|
|
111
|
+
--scalar-border-color: color-mix(in srgb, var(--scalar-theme-border) 30%, white 70%);
|
|
112
|
+
--scalar-button-1: var(--scalar-theme-primary);
|
|
113
|
+
--scalar-button-1-color: #ffffff;
|
|
114
|
+
--scalar-button-1-hover: color-mix(in srgb, var(--scalar-theme-primary) 88%, black 12%);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
body {
|
|
118
|
+
background: var(--scalar-background-1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.t-doc__sidebar {
|
|
122
|
+
width: min(${sidebarWidth}px, 100vw);
|
|
123
|
+
border-right: 1px solid var(--scalar-border-color);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.scalar-card,
|
|
127
|
+
.t-doc__sidebar,
|
|
128
|
+
.references-layout .reference-layout__content .request-card,
|
|
129
|
+
.references-layout .reference-layout__content .response-card,
|
|
130
|
+
.references-layout .reference-layout__content .scalar-card-header,
|
|
131
|
+
.references-layout .reference-layout__content .scalar-card-footer,
|
|
132
|
+
.references-layout .reference-layout__content .section,
|
|
133
|
+
.references-layout .reference-layout__content .section-container {
|
|
134
|
+
border-color: var(--scalar-border-color) !important;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.t-doc__sidebar,
|
|
138
|
+
.t-doc__sidebar * {
|
|
139
|
+
font-family: var(--scalar-font);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.t-doc__sidebar .sidebar-search {
|
|
143
|
+
margin: 0.5rem 0 1rem;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.t-doc__sidebar .sidebar-search input {
|
|
147
|
+
border-radius: 14px;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.t-doc__sidebar .sidebar-item,
|
|
151
|
+
.t-doc__sidebar .sidebar-heading {
|
|
152
|
+
border-radius: 14px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.t-doc__sidebar .sidebar-group-label {
|
|
156
|
+
font-size: 0.72rem;
|
|
157
|
+
letter-spacing: 0.08em;
|
|
158
|
+
text-transform: uppercase;
|
|
159
|
+
color: var(--scalar-color-3);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.t-doc__sidebar .sidebar-item--active {
|
|
163
|
+
font-weight: 600;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.scalar-card,
|
|
167
|
+
.references-layout .reference-layout__content .request-card,
|
|
168
|
+
.references-layout .reference-layout__content .response-card {
|
|
169
|
+
border-radius: 18px;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.references-layout .reference-layout__content {
|
|
173
|
+
padding-top: 1.5rem;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.references-layout .section-content,
|
|
177
|
+
.references-layout .section-flare {
|
|
178
|
+
background: transparent;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.references-layout .reference-layout__content,
|
|
182
|
+
.references-layout .reference-layout__content * {
|
|
183
|
+
font-family: var(--scalar-font);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.references-layout code,
|
|
187
|
+
.references-layout pre,
|
|
188
|
+
.references-layout .scalar-codeblock {
|
|
189
|
+
font-family: var(--scalar-font-code);
|
|
190
|
+
}
|
|
191
|
+
`;
|
|
192
|
+
}
|
|
193
|
+
function buildApiReferenceOpenApiDocument(config, options) {
|
|
194
|
+
const routes = buildApiReferenceRoutes(config, options);
|
|
195
|
+
const tags = Array.from(new Set(routes.map((route) => route.tag))).map((name) => ({
|
|
196
|
+
name,
|
|
197
|
+
description: `${name} endpoints`
|
|
198
|
+
}));
|
|
199
|
+
return {
|
|
200
|
+
openapi: "3.1.0",
|
|
201
|
+
info: {
|
|
202
|
+
title: "API Reference",
|
|
203
|
+
description: config.metadata?.description ?? `Generated API reference for ${options.framework}.`,
|
|
204
|
+
version: "0.0.0"
|
|
205
|
+
},
|
|
206
|
+
servers: [{ url: "/" }],
|
|
207
|
+
tags,
|
|
208
|
+
paths: buildOpenApiPaths(routes)
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function buildApiReferenceHtmlDocument(config, options) {
|
|
212
|
+
const apiReference = resolveApiReferenceConfig(config.apiReference);
|
|
213
|
+
const rootDir = options.rootDir ?? process.cwd();
|
|
214
|
+
const title = options.title ?? "API Reference";
|
|
215
|
+
return getHtmlDocument({
|
|
216
|
+
pageTitle: buildApiReferencePageTitle(config, title),
|
|
217
|
+
title,
|
|
218
|
+
content: () => buildApiReferenceOpenApiDocument(config, {
|
|
219
|
+
framework: options.framework,
|
|
220
|
+
rootDir
|
|
221
|
+
}),
|
|
222
|
+
theme: "deepSpace",
|
|
223
|
+
layout: "modern",
|
|
224
|
+
customCss: buildApiReferenceScalarCss(config),
|
|
225
|
+
pathRouting: { basePath: `/${apiReference.path}` },
|
|
226
|
+
showSidebar: true,
|
|
227
|
+
defaultOpenFirstTag: true,
|
|
228
|
+
tagsSorter: "alpha",
|
|
229
|
+
operationsSorter: "alpha",
|
|
230
|
+
operationTitleSource: "summary",
|
|
231
|
+
defaultHttpClient: {
|
|
232
|
+
targetKey: "shell",
|
|
233
|
+
clientKey: "curl"
|
|
234
|
+
},
|
|
235
|
+
documentDownloadType: "json"
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function buildApiReferenceRoutes(config, options) {
|
|
239
|
+
const apiReference = resolveApiReferenceConfig(config.apiReference);
|
|
240
|
+
if (!apiReference.enabled) return [];
|
|
241
|
+
const rootDir = options.rootDir ?? process.cwd();
|
|
242
|
+
switch (options.framework) {
|
|
243
|
+
case "next": return buildFileConventionRoutes({
|
|
244
|
+
rootDir,
|
|
245
|
+
sourceDir: resolveRootedDir(rootDir, apiReference.routeRoot, getNextAppDir(rootDir)),
|
|
246
|
+
routePathBase: toRouteBase(apiReference.routeRoot, getNextAppDir(rootDir)),
|
|
247
|
+
isRouteFile: (name) => NEXT_ROUTE_FILE_RE.test(name),
|
|
248
|
+
toRouteSegments: (relativeFile) => relativeFile.split("/").slice(0, -1).filter(Boolean),
|
|
249
|
+
config,
|
|
250
|
+
exclude: apiReference.exclude
|
|
251
|
+
});
|
|
252
|
+
case "sveltekit": return buildFileConventionRoutes({
|
|
253
|
+
rootDir,
|
|
254
|
+
sourceDir: resolveRootedDir(rootDir, apiReference.routeRoot, "src/routes"),
|
|
255
|
+
routePathBase: toRouteBase(apiReference.routeRoot, "src/routes"),
|
|
256
|
+
isRouteFile: (name) => SVELTE_ROUTE_FILE_RE.test(name),
|
|
257
|
+
toRouteSegments: (relativeFile) => relativeFile.split("/").slice(0, -1).filter(Boolean),
|
|
258
|
+
config,
|
|
259
|
+
exclude: apiReference.exclude
|
|
260
|
+
});
|
|
261
|
+
case "astro": return buildFileConventionRoutes({
|
|
262
|
+
rootDir,
|
|
263
|
+
sourceDir: resolveRootedDir(rootDir, apiReference.routeRoot, "src/pages"),
|
|
264
|
+
routePathBase: toRouteBase(apiReference.routeRoot, "src/pages"),
|
|
265
|
+
isRouteFile: (name) => ASTRO_ROUTE_FILE_RE.test(name),
|
|
266
|
+
toRouteSegments: (relativeFile) => routeSegmentsFromEndpointFile(relativeFile),
|
|
267
|
+
config,
|
|
268
|
+
exclude: apiReference.exclude
|
|
269
|
+
});
|
|
270
|
+
case "nuxt": return buildFileConventionRoutes({
|
|
271
|
+
rootDir,
|
|
272
|
+
sourceDir: resolveRootedDir(rootDir, apiReference.routeRoot, "server"),
|
|
273
|
+
routePathBase: toRouteBase(apiReference.routeRoot, "server"),
|
|
274
|
+
isRouteFile: (name) => NUXT_ROUTE_FILE_RE.test(name),
|
|
275
|
+
toRouteSegments: (relativeFile) => routeSegmentsFromEndpointFile(stripNuxtMethodSuffix(relativeFile)),
|
|
276
|
+
config,
|
|
277
|
+
exclude: apiReference.exclude,
|
|
278
|
+
getMethods: (source, file) => extractNuxtMethods(source, file)
|
|
279
|
+
});
|
|
280
|
+
case "tanstack-start": return buildTanstackRoutes(config, rootDir, apiReference);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function buildFileConventionRoutes({ rootDir, sourceDir, routePathBase, isRouteFile, toRouteSegments, config, exclude, getMethods = extractMethods }) {
|
|
284
|
+
const files = scanRouteFiles(sourceDir, isRouteFile);
|
|
285
|
+
const routes = [];
|
|
286
|
+
for (const file of files) {
|
|
287
|
+
const source = readFileSync(file, "utf-8");
|
|
288
|
+
const methods = getMethods(source, file);
|
|
289
|
+
if (methods.length === 0) continue;
|
|
290
|
+
const relativeFile = relative(sourceDir, file).replace(/\\/g, "/");
|
|
291
|
+
const routeSegments = toRouteSegments(relativeFile);
|
|
292
|
+
const routePath = buildRoutePath(routePathBase, routeSegments);
|
|
293
|
+
if (shouldExcludeRoute(exclude, routePath, relativeFile, routeSegments.join("/"))) continue;
|
|
294
|
+
routes.push(createApiReferenceRoute({
|
|
295
|
+
rootDir,
|
|
296
|
+
file,
|
|
297
|
+
source,
|
|
298
|
+
methods,
|
|
299
|
+
routePath
|
|
300
|
+
}));
|
|
301
|
+
}
|
|
302
|
+
return routes.sort((a, b) => a.routePath.localeCompare(b.routePath));
|
|
303
|
+
}
|
|
304
|
+
function buildTanstackRoutes(config, rootDir, apiReference) {
|
|
305
|
+
const routesDir = join(rootDir, "src", "routes");
|
|
306
|
+
const files = scanRouteFiles(routesDir, (name) => TANSTACK_ROUTE_FILE_RE.test(name));
|
|
307
|
+
const routeBase = `/${normalizePathSegment(apiReference.routeRoot)}`;
|
|
308
|
+
const routes = [];
|
|
309
|
+
for (const file of files) {
|
|
310
|
+
const source = readFileSync(file, "utf-8");
|
|
311
|
+
if (!source.includes("createFileRoute(") || !source.includes("handlers")) continue;
|
|
312
|
+
const pathMatch = source.match(/createFileRoute\(\s*["'`]([^"'`]+)["'`]\s*\)/);
|
|
313
|
+
if (!pathMatch) continue;
|
|
314
|
+
const routePath = normalizeTanstackRoutePath(pathMatch[1]);
|
|
315
|
+
if (!routePath.startsWith(routeBase)) continue;
|
|
316
|
+
const methods = extractTanstackMethods(source);
|
|
317
|
+
if (methods.length === 0) continue;
|
|
318
|
+
const relativeFile = relative(routesDir, file).replace(/\\/g, "/");
|
|
319
|
+
if (shouldExcludeRoute(apiReference.exclude, routePath, relativeFile, relativeFile)) continue;
|
|
320
|
+
routes.push(createApiReferenceRoute({
|
|
321
|
+
rootDir,
|
|
322
|
+
file,
|
|
323
|
+
source,
|
|
324
|
+
methods,
|
|
325
|
+
routePath
|
|
326
|
+
}));
|
|
327
|
+
}
|
|
328
|
+
return routes.sort((a, b) => a.routePath.localeCompare(b.routePath));
|
|
329
|
+
}
|
|
330
|
+
function createApiReferenceRoute({ rootDir, file, source, methods, routePath }) {
|
|
331
|
+
const docBlock = extractDocBlock(source);
|
|
332
|
+
const pathSegments = routePath.split("/").filter(Boolean);
|
|
333
|
+
const titleSegment = pathSegments[pathSegments.length - 1] ?? "overview";
|
|
334
|
+
const tagSegment = pathSegments[0] ?? "general";
|
|
335
|
+
const title = humanizeSegment(titleSegment);
|
|
336
|
+
return {
|
|
337
|
+
title,
|
|
338
|
+
summary: docBlock.summary ?? `${title} endpoint`,
|
|
339
|
+
description: docBlock.description,
|
|
340
|
+
routePath,
|
|
341
|
+
sourceFile: relative(rootDir, file).replace(/\\/g, "/"),
|
|
342
|
+
methods,
|
|
343
|
+
tag: humanizeSegment(tagSegment),
|
|
344
|
+
parameters: buildPathParameters(routePath)
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
function buildPathParameters(routePath) {
|
|
348
|
+
const parameters = [];
|
|
349
|
+
for (const segment of routePath.split("/")) {
|
|
350
|
+
const match = segment.match(/^\{(.+)\}$/);
|
|
351
|
+
if (!match) continue;
|
|
352
|
+
parameters.push({
|
|
353
|
+
name: match[1],
|
|
354
|
+
in: "path",
|
|
355
|
+
required: true,
|
|
356
|
+
description: `${humanizeSegment(match[1])} path parameter.`,
|
|
357
|
+
schema: { type: "string" }
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
return parameters;
|
|
361
|
+
}
|
|
362
|
+
function buildOpenApiPaths(routes) {
|
|
363
|
+
const paths = {};
|
|
364
|
+
for (const route of routes) {
|
|
365
|
+
const pathItem = {};
|
|
366
|
+
for (const method of route.methods) pathItem[method.toLowerCase()] = {
|
|
367
|
+
tags: [route.tag],
|
|
368
|
+
summary: route.summary,
|
|
369
|
+
description: route.description ?? route.summary,
|
|
370
|
+
operationId: createOperationId(route, method),
|
|
371
|
+
...route.parameters.length > 0 ? { parameters: route.parameters } : {},
|
|
372
|
+
...buildRequestBody(method) ? { requestBody: buildRequestBody(method) } : {},
|
|
373
|
+
responses: buildResponses(method),
|
|
374
|
+
"x-farming-labs-source": route.sourceFile
|
|
375
|
+
};
|
|
376
|
+
paths[route.routePath] = pathItem;
|
|
377
|
+
}
|
|
378
|
+
return paths;
|
|
379
|
+
}
|
|
380
|
+
function buildRequestBody(method) {
|
|
381
|
+
if (![
|
|
382
|
+
"POST",
|
|
383
|
+
"PUT",
|
|
384
|
+
"PATCH"
|
|
385
|
+
].includes(method)) return void 0;
|
|
386
|
+
return {
|
|
387
|
+
required: method === "POST",
|
|
388
|
+
content: { "application/json": {
|
|
389
|
+
schema: {
|
|
390
|
+
type: "object",
|
|
391
|
+
additionalProperties: true
|
|
392
|
+
},
|
|
393
|
+
example: { example: true }
|
|
394
|
+
} }
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function buildResponses(method) {
|
|
398
|
+
return { "200": {
|
|
399
|
+
description: method === "DELETE" ? "Resource removed successfully." : "Request completed successfully.",
|
|
400
|
+
content: { "application/json": {
|
|
401
|
+
schema: {
|
|
402
|
+
type: "object",
|
|
403
|
+
additionalProperties: true
|
|
404
|
+
},
|
|
405
|
+
example: { ok: true }
|
|
406
|
+
} }
|
|
407
|
+
} };
|
|
408
|
+
}
|
|
409
|
+
function createOperationId(route, method) {
|
|
410
|
+
return `${method.toLowerCase()}_${route.routePath.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`;
|
|
411
|
+
}
|
|
412
|
+
function resolveTheme(config) {
|
|
413
|
+
return config.theme;
|
|
414
|
+
}
|
|
415
|
+
function normalizeApiReferenceExcludes(values) {
|
|
416
|
+
return (values ?? []).map(normalizeExcludeMatcher).filter(Boolean);
|
|
417
|
+
}
|
|
418
|
+
function normalizeExcludeMatcher(value) {
|
|
419
|
+
return value.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "").replace(/\.(ts|tsx|js|jsx|mjs|mts)$/i, "").replace(/\/route$/i, "").replace(/\/\+server$/i, "").replace(/\/index$/i, "").replace(/\.(get|post|put|patch|delete|options|head)$/i, "");
|
|
420
|
+
}
|
|
421
|
+
function shouldExcludeRoute(excludes, routePath, relativeFile, relativeDir) {
|
|
422
|
+
if (excludes.length === 0) return false;
|
|
423
|
+
const candidates = new Set([
|
|
424
|
+
normalizeExcludeMatcher(routePath),
|
|
425
|
+
normalizeExcludeMatcher(routePath.replace(/^\/+/, "")),
|
|
426
|
+
normalizeExcludeMatcher(relativeFile),
|
|
427
|
+
normalizeExcludeMatcher(relativeDir)
|
|
428
|
+
]);
|
|
429
|
+
return excludes.some((entry) => candidates.has(entry));
|
|
430
|
+
}
|
|
431
|
+
function scanRouteFiles(dir, isRouteFile) {
|
|
432
|
+
if (!existsSync(dir)) return [];
|
|
433
|
+
const results = [];
|
|
434
|
+
for (const name of readdirSync(dir)) {
|
|
435
|
+
const full = join(dir, name);
|
|
436
|
+
if (statSync(full).isDirectory()) {
|
|
437
|
+
results.push(...scanRouteFiles(full, isRouteFile));
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (isRouteFile(name)) results.push(full);
|
|
441
|
+
}
|
|
442
|
+
return results;
|
|
443
|
+
}
|
|
444
|
+
function resolveRootedDir(rootDir, routeRoot, defaultRoot) {
|
|
445
|
+
const normalized = normalizePathSegment(routeRoot) || "api";
|
|
446
|
+
if (normalized === defaultRoot || normalized.startsWith(`${defaultRoot}/`)) return join(rootDir, ...normalized.split("/"));
|
|
447
|
+
return join(rootDir, ...defaultRoot.split("/"), ...normalized.split("/"));
|
|
448
|
+
}
|
|
449
|
+
function toRouteBase(routeRoot, defaultRoot) {
|
|
450
|
+
const normalized = normalizePathSegment(routeRoot) || "api";
|
|
451
|
+
return `/${normalizePathSegment(normalized === defaultRoot || normalized.startsWith(`${defaultRoot}/`) ? normalized.slice(defaultRoot.length).replace(/^\/+/, "") : normalized)}`;
|
|
452
|
+
}
|
|
453
|
+
function getNextAppDir(rootDir) {
|
|
454
|
+
if (existsSync(join(rootDir, "src", "app"))) return "src/app";
|
|
455
|
+
return "app";
|
|
456
|
+
}
|
|
457
|
+
function routeSegmentsFromEndpointFile(relativeFile) {
|
|
458
|
+
const segments = relativeFile.split("/");
|
|
459
|
+
const name = (segments.pop() ?? "").replace(/\.(ts|js|mts|mjs)$/i, "");
|
|
460
|
+
if (name !== "index") segments.push(name);
|
|
461
|
+
return segments.filter(Boolean);
|
|
462
|
+
}
|
|
463
|
+
function stripNuxtMethodSuffix(relativeFile) {
|
|
464
|
+
return relativeFile.replace(/\.(get|post|put|patch|delete|options|head)(?=\.(ts|js|mts|mjs)$)/i, "");
|
|
465
|
+
}
|
|
466
|
+
function buildRoutePath(basePath, rawSegments) {
|
|
467
|
+
const segments = rawSegments.filter(Boolean).map((segment) => endpointSegmentFromConvention(segment)).join("/");
|
|
468
|
+
const path = [normalizePathSegment(basePath), segments].filter(Boolean).join("/");
|
|
469
|
+
return path ? `/${path}` : "/";
|
|
470
|
+
}
|
|
471
|
+
function endpointSegmentFromConvention(value) {
|
|
472
|
+
if (value.startsWith("[[...") && value.endsWith("]]")) return `{${value.slice(5, -2)}}`;
|
|
473
|
+
if (value.startsWith("[...") && value.endsWith("]")) return `{${value.slice(4, -1)}}`;
|
|
474
|
+
if (value.startsWith("[") && value.endsWith("]")) return `{${value.slice(1, -1)}}`;
|
|
475
|
+
return value;
|
|
476
|
+
}
|
|
477
|
+
function normalizeTanstackRoutePath(value) {
|
|
478
|
+
return `/${value.replace(/^\/+|\/+$/g, "").split("/").map((segment) => segment.startsWith("$") ? `{${segment.slice(1)}}` : segment).filter(Boolean).join("/")}`;
|
|
479
|
+
}
|
|
480
|
+
function extractDocBlock(source) {
|
|
481
|
+
const match = source.match(/\/\*\*([\s\S]*?)\*\//);
|
|
482
|
+
if (!match) return {};
|
|
483
|
+
const lines = match[1].split("\n").map((line) => line.replace(/^\s*\*\s?/, "").trim()).filter(Boolean).filter((line) => !line.startsWith("@"));
|
|
484
|
+
if (lines.length === 0) return {};
|
|
485
|
+
return {
|
|
486
|
+
summary: lines[0],
|
|
487
|
+
description: lines.slice(1).join(" ")
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
function extractMethods(source) {
|
|
491
|
+
const methods = /* @__PURE__ */ new Set();
|
|
492
|
+
for (const match of source.matchAll(METHOD_RE)) {
|
|
493
|
+
if (match[1] === "ALL") {
|
|
494
|
+
METHOD_NAMES.forEach((method) => methods.add(method));
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
methods.add(match[1]);
|
|
498
|
+
}
|
|
499
|
+
return Array.from(methods);
|
|
500
|
+
}
|
|
501
|
+
function extractNuxtMethods(source, file) {
|
|
502
|
+
const methods = extractMethods(source);
|
|
503
|
+
if (methods.length > 0) return methods;
|
|
504
|
+
const suffix = basename(file).match(/\.(get|post|put|patch|delete|options|head)\.(ts|js|mts|mjs)$/i);
|
|
505
|
+
if (suffix) return [suffix[1].toUpperCase()];
|
|
506
|
+
if (/defineEventHandler|eventHandler/.test(source)) return ["GET"];
|
|
507
|
+
return [];
|
|
508
|
+
}
|
|
509
|
+
function extractTanstackMethods(source) {
|
|
510
|
+
const methods = /* @__PURE__ */ new Set();
|
|
511
|
+
const handlersMatch = source.match(/handlers\s*:\s*\{([\s\S]*?)\}/m);
|
|
512
|
+
if (!handlersMatch) return [];
|
|
513
|
+
for (const match of handlersMatch[1].matchAll(/\b(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\s*:/g)) methods.add(match[1]);
|
|
514
|
+
return Array.from(methods);
|
|
515
|
+
}
|
|
516
|
+
function humanizeSegment(value) {
|
|
517
|
+
return value.replace(/^\{/, "").replace(/\}$/, "").replace(/^\[\[?\.{3}/, "").replace(/^\[/, "").replace(/\]\]?$/, "").replace(/^\$/, "").replace(/-/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
//#endregion
|
|
521
|
+
export { buildApiReferenceHtmlDocument, buildApiReferenceOpenApiDocument, buildApiReferencePageTitle, buildApiReferenceScalarCss, resolveApiReferenceConfig };
|