@forge-ts/gen 0.4.0 → 0.6.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/index.d.ts +378 -48
- package/dist/index.js +1857 -265
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,3 +1,761 @@
|
|
|
1
|
+
// src/adapters/registry.ts
|
|
2
|
+
var adapters = /* @__PURE__ */ new Map();
|
|
3
|
+
function registerAdapter(adapter) {
|
|
4
|
+
adapters.set(adapter.target, adapter);
|
|
5
|
+
}
|
|
6
|
+
function getAdapter(target) {
|
|
7
|
+
const adapter = adapters.get(target);
|
|
8
|
+
if (!adapter) {
|
|
9
|
+
const available = [...adapters.keys()].join(", ");
|
|
10
|
+
throw new Error(`Unknown SSG target "${target}". Available targets: ${available}`);
|
|
11
|
+
}
|
|
12
|
+
return adapter;
|
|
13
|
+
}
|
|
14
|
+
function getAvailableTargets() {
|
|
15
|
+
return [...adapters.keys()];
|
|
16
|
+
}
|
|
17
|
+
var DEFAULT_TARGET = "mintlify";
|
|
18
|
+
|
|
19
|
+
// src/adapters/mintlify.ts
|
|
20
|
+
var styleGuide = {
|
|
21
|
+
pageExtension: "mdx",
|
|
22
|
+
supportsMdx: true,
|
|
23
|
+
requiresFrontmatter: true,
|
|
24
|
+
maxHeadingDepth: 3,
|
|
25
|
+
defaultImports: [],
|
|
26
|
+
codeBlockLanguage: "typescript"
|
|
27
|
+
};
|
|
28
|
+
function pageSlug(pagePath) {
|
|
29
|
+
return pagePath.replace(/\.[^.]+$/, "");
|
|
30
|
+
}
|
|
31
|
+
function buildDocsJson(context) {
|
|
32
|
+
const { pages, projectName } = context;
|
|
33
|
+
const byPackage = /* @__PURE__ */ new Map();
|
|
34
|
+
for (const page of pages) {
|
|
35
|
+
const slug = pageSlug(page.path);
|
|
36
|
+
const packageMatch = /^packages\/([^/]+)\/(.+)$/.exec(slug);
|
|
37
|
+
if (packageMatch) {
|
|
38
|
+
const pkgName = packageMatch[1];
|
|
39
|
+
const list = byPackage.get(pkgName) ?? [];
|
|
40
|
+
list.push(slug);
|
|
41
|
+
byPackage.set(pkgName, list);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const tabs = [];
|
|
45
|
+
const docGroups = [];
|
|
46
|
+
docGroups.push({
|
|
47
|
+
group: "Getting Started",
|
|
48
|
+
pages: ["index", "getting-started"]
|
|
49
|
+
});
|
|
50
|
+
docGroups.push({
|
|
51
|
+
group: "Learn",
|
|
52
|
+
pages: ["concepts", "guides/index"]
|
|
53
|
+
});
|
|
54
|
+
docGroups.push({
|
|
55
|
+
group: "Reference",
|
|
56
|
+
pages: ["configuration", "changelog"]
|
|
57
|
+
});
|
|
58
|
+
docGroups.push({
|
|
59
|
+
group: "Community",
|
|
60
|
+
pages: ["faq", "contributing"]
|
|
61
|
+
});
|
|
62
|
+
tabs.push({ tab: "Documentation", groups: docGroups });
|
|
63
|
+
if (byPackage.size > 0) {
|
|
64
|
+
const apiGroups = [];
|
|
65
|
+
for (const [pkgName, slugs] of byPackage) {
|
|
66
|
+
const sorted = slugs.sort((a, b) => {
|
|
67
|
+
const order = (s) => {
|
|
68
|
+
if (s.endsWith("/index")) return 0;
|
|
69
|
+
if (s.includes("/api/index")) return 1;
|
|
70
|
+
if (s.includes("/api/functions")) return 2;
|
|
71
|
+
if (s.includes("/api/types")) return 3;
|
|
72
|
+
if (s.includes("/api/examples")) return 4;
|
|
73
|
+
return 5;
|
|
74
|
+
};
|
|
75
|
+
return order(a) - order(b);
|
|
76
|
+
});
|
|
77
|
+
apiGroups.push({ group: `@forge-ts/${pkgName}`, pages: sorted });
|
|
78
|
+
}
|
|
79
|
+
tabs.push({ tab: "API Reference", groups: apiGroups });
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
name: projectName,
|
|
83
|
+
theme: "mint",
|
|
84
|
+
colors: { primary: "#0ea5e9", light: "#38bdf8", dark: "#0284c7" },
|
|
85
|
+
navigation: { tabs },
|
|
86
|
+
footer: {
|
|
87
|
+
socials: context.config.project.repository ? { github: context.config.project.repository } : {}
|
|
88
|
+
},
|
|
89
|
+
contextual: {
|
|
90
|
+
options: ["copy", "view", "chatgpt", "claude", "perplexity"]
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function addMintlifyFrontmatter(page) {
|
|
95
|
+
const title = String(page.frontmatter.title ?? "");
|
|
96
|
+
const description = page.frontmatter.description ? String(page.frontmatter.description) : void 0;
|
|
97
|
+
const lines = ["---", `title: "${title}"`];
|
|
98
|
+
if (description) {
|
|
99
|
+
lines.push(`description: "${description}"`);
|
|
100
|
+
}
|
|
101
|
+
lines.push("---", "");
|
|
102
|
+
const body = page.content.replace(/^---[\s\S]*?---\n+/, "");
|
|
103
|
+
return lines.join("\n") + body;
|
|
104
|
+
}
|
|
105
|
+
var mintlifyAdapter = {
|
|
106
|
+
target: "mintlify",
|
|
107
|
+
displayName: "Mintlify",
|
|
108
|
+
styleGuide,
|
|
109
|
+
scaffold(context) {
|
|
110
|
+
const docsJson = buildDocsJson(context);
|
|
111
|
+
return {
|
|
112
|
+
target: "mintlify",
|
|
113
|
+
files: [{ path: "docs.json", content: JSON.stringify(docsJson, null, 2) }],
|
|
114
|
+
dependencies: {},
|
|
115
|
+
devDependencies: {},
|
|
116
|
+
scripts: {},
|
|
117
|
+
instructions: [
|
|
118
|
+
"Preview locally: npx @mintlify/cli dev",
|
|
119
|
+
"Deploy: Push to GitHub and connect at mintlify.com"
|
|
120
|
+
]
|
|
121
|
+
};
|
|
122
|
+
},
|
|
123
|
+
transformPages(pages, _context) {
|
|
124
|
+
return pages.map((page) => {
|
|
125
|
+
let content = addMintlifyFrontmatter(page);
|
|
126
|
+
content = content.replace(/^# .+$/m, "").replace(/\n{3,}/g, "\n\n");
|
|
127
|
+
let insideFence = false;
|
|
128
|
+
content = content.split("\n").map((line) => {
|
|
129
|
+
if (insideFence) {
|
|
130
|
+
if (/^```\s*$/.test(line)) {
|
|
131
|
+
insideFence = false;
|
|
132
|
+
}
|
|
133
|
+
return line;
|
|
134
|
+
}
|
|
135
|
+
if (/^```\S/.test(line)) {
|
|
136
|
+
insideFence = true;
|
|
137
|
+
return line;
|
|
138
|
+
}
|
|
139
|
+
if (/^```\s*$/.test(line)) {
|
|
140
|
+
insideFence = true;
|
|
141
|
+
return "```typescript";
|
|
142
|
+
}
|
|
143
|
+
return line;
|
|
144
|
+
}).join("\n");
|
|
145
|
+
return {
|
|
146
|
+
path: page.path.replace(/\.md$/, ".mdx"),
|
|
147
|
+
content,
|
|
148
|
+
stub: page.stub
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
},
|
|
152
|
+
generateConfig(context) {
|
|
153
|
+
const config = buildDocsJson(context);
|
|
154
|
+
return [
|
|
155
|
+
{
|
|
156
|
+
path: "docs.json",
|
|
157
|
+
content: `${JSON.stringify(config, null, 2)}
|
|
158
|
+
`
|
|
159
|
+
}
|
|
160
|
+
];
|
|
161
|
+
},
|
|
162
|
+
getDevCommand(outDir) {
|
|
163
|
+
return {
|
|
164
|
+
bin: "npx",
|
|
165
|
+
args: ["@mintlify/cli", "dev"],
|
|
166
|
+
cwd: outDir,
|
|
167
|
+
label: "Mintlify Dev Server",
|
|
168
|
+
url: "http://localhost:3000"
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
async detectExisting(outDir) {
|
|
172
|
+
const { existsSync: existsSync3 } = await import("fs");
|
|
173
|
+
const { join: join2 } = await import("path");
|
|
174
|
+
return existsSync3(join2(outDir, "docs.json"));
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
registerAdapter(mintlifyAdapter);
|
|
178
|
+
|
|
179
|
+
// src/adapters/docusaurus.ts
|
|
180
|
+
var styleGuide2 = {
|
|
181
|
+
pageExtension: "mdx",
|
|
182
|
+
supportsMdx: true,
|
|
183
|
+
requiresFrontmatter: true,
|
|
184
|
+
maxHeadingDepth: 4,
|
|
185
|
+
defaultImports: [],
|
|
186
|
+
codeBlockLanguage: "typescript"
|
|
187
|
+
};
|
|
188
|
+
function pageSlug2(pagePath) {
|
|
189
|
+
return pagePath.replace(/\.[^.]+$/, "");
|
|
190
|
+
}
|
|
191
|
+
function buildSidebarsTs(pages) {
|
|
192
|
+
const topLevel = [];
|
|
193
|
+
const byPackage = /* @__PURE__ */ new Map();
|
|
194
|
+
for (const page of pages) {
|
|
195
|
+
const slug = pageSlug2(page.path);
|
|
196
|
+
const packageMatch = /^packages\/([^/]+)\/(.+)$/.exec(slug);
|
|
197
|
+
if (packageMatch) {
|
|
198
|
+
const pkgName = packageMatch[1];
|
|
199
|
+
const list = byPackage.get(pkgName) ?? [];
|
|
200
|
+
list.push(slug);
|
|
201
|
+
byPackage.set(pkgName, list);
|
|
202
|
+
} else {
|
|
203
|
+
topLevel.push(slug);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const items = [...topLevel];
|
|
207
|
+
for (const [pkgName, slugs] of byPackage) {
|
|
208
|
+
items.push({
|
|
209
|
+
type: "category",
|
|
210
|
+
label: pkgName,
|
|
211
|
+
items: slugs
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
const sidebarObj = { docs: items };
|
|
215
|
+
const json = JSON.stringify(sidebarObj, null, 2);
|
|
216
|
+
return `import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";
|
|
217
|
+
|
|
218
|
+
const sidebars: SidebarsConfig = ${json};
|
|
219
|
+
|
|
220
|
+
export default sidebars;
|
|
221
|
+
`;
|
|
222
|
+
}
|
|
223
|
+
function buildDocusaurusConfig(context) {
|
|
224
|
+
const { projectName, projectDescription } = context;
|
|
225
|
+
const description = projectDescription ?? `${projectName} documentation`;
|
|
226
|
+
return `import { themes as prismThemes } from "prism-react-renderer";
|
|
227
|
+
import type { Config } from "@docusaurus/types";
|
|
228
|
+
import type * as Preset from "@docusaurus/preset-classic";
|
|
229
|
+
|
|
230
|
+
const config: Config = {
|
|
231
|
+
title: "${projectName}",
|
|
232
|
+
tagline: "${description}",
|
|
233
|
+
url: "https://your-domain.com",
|
|
234
|
+
baseUrl: "/",
|
|
235
|
+
onBrokenLinks: "throw",
|
|
236
|
+
onBrokenMarkdownLinks: "warn",
|
|
237
|
+
i18n: {
|
|
238
|
+
defaultLocale: "en",
|
|
239
|
+
locales: ["en"],
|
|
240
|
+
},
|
|
241
|
+
presets: [
|
|
242
|
+
[
|
|
243
|
+
"classic",
|
|
244
|
+
{
|
|
245
|
+
docs: {
|
|
246
|
+
routeBasePath: "/",
|
|
247
|
+
sidebarPath: "./sidebars.ts",
|
|
248
|
+
},
|
|
249
|
+
blog: false,
|
|
250
|
+
} satisfies Preset.Options,
|
|
251
|
+
],
|
|
252
|
+
],
|
|
253
|
+
themeConfig: {
|
|
254
|
+
prism: {
|
|
255
|
+
theme: prismThemes.github,
|
|
256
|
+
darkTheme: prismThemes.dracula,
|
|
257
|
+
},
|
|
258
|
+
} satisfies Preset.ThemeConfig,
|
|
259
|
+
} satisfies Config;
|
|
260
|
+
|
|
261
|
+
export default config;
|
|
262
|
+
`;
|
|
263
|
+
}
|
|
264
|
+
function buildPackageJson(context) {
|
|
265
|
+
const pkg = {
|
|
266
|
+
name: `${context.projectName}-docs`,
|
|
267
|
+
version: "0.0.0",
|
|
268
|
+
private: true,
|
|
269
|
+
scripts: {
|
|
270
|
+
"docs:dev": "docusaurus start",
|
|
271
|
+
"docs:build": "docusaurus build",
|
|
272
|
+
"docs:serve": "docusaurus serve"
|
|
273
|
+
},
|
|
274
|
+
dependencies: {
|
|
275
|
+
"@docusaurus/core": "^3.9.2",
|
|
276
|
+
"@docusaurus/preset-classic": "^3.9.2",
|
|
277
|
+
"@mdx-js/react": "^3.0.0",
|
|
278
|
+
react: "^19.0.0",
|
|
279
|
+
"react-dom": "^19.0.0",
|
|
280
|
+
"prism-react-renderer": "^2.3.0"
|
|
281
|
+
},
|
|
282
|
+
devDependencies: {
|
|
283
|
+
"@docusaurus/tsconfig": "^3.9.2",
|
|
284
|
+
"@docusaurus/types": "^3.9.2"
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
return `${JSON.stringify(pkg, null, 2)}
|
|
288
|
+
`;
|
|
289
|
+
}
|
|
290
|
+
function addDocusaurusFrontmatter(page) {
|
|
291
|
+
const title = String(page.frontmatter.title ?? "");
|
|
292
|
+
const description = page.frontmatter.description ? String(page.frontmatter.description) : void 0;
|
|
293
|
+
const sidebarLabel = page.frontmatter.sidebar_label ? String(page.frontmatter.sidebar_label) : title;
|
|
294
|
+
const sidebarPosition = page.frontmatter.sidebar_position;
|
|
295
|
+
const lines = ["---", `title: "${title}"`, `sidebar_label: "${sidebarLabel}"`];
|
|
296
|
+
if (sidebarPosition !== void 0) {
|
|
297
|
+
lines.push(`sidebar_position: ${sidebarPosition}`);
|
|
298
|
+
}
|
|
299
|
+
if (description) {
|
|
300
|
+
lines.push(`description: "${description}"`);
|
|
301
|
+
}
|
|
302
|
+
lines.push("---", "");
|
|
303
|
+
const body = page.content.replace(/^---[\s\S]*?---\n+/, "");
|
|
304
|
+
return lines.join("\n") + body;
|
|
305
|
+
}
|
|
306
|
+
var docusaurusAdapter = {
|
|
307
|
+
target: "docusaurus",
|
|
308
|
+
displayName: "Docusaurus",
|
|
309
|
+
styleGuide: styleGuide2,
|
|
310
|
+
scaffold(context) {
|
|
311
|
+
return {
|
|
312
|
+
target: "docusaurus",
|
|
313
|
+
files: [
|
|
314
|
+
{ path: "docusaurus.config.ts", content: buildDocusaurusConfig(context) },
|
|
315
|
+
{ path: "sidebars.ts", content: buildSidebarsTs(context.pages) },
|
|
316
|
+
{ path: "package.json", content: buildPackageJson(context) },
|
|
317
|
+
{ path: "tsconfig.json", content: '{ "extends": "@docusaurus/tsconfig" }\n' }
|
|
318
|
+
],
|
|
319
|
+
dependencies: {
|
|
320
|
+
"@docusaurus/core": "^3.9.2",
|
|
321
|
+
"@docusaurus/preset-classic": "^3.9.2",
|
|
322
|
+
"@mdx-js/react": "^3.0.0",
|
|
323
|
+
react: "^19.0.0",
|
|
324
|
+
"react-dom": "^19.0.0",
|
|
325
|
+
"prism-react-renderer": "^2.3.0"
|
|
326
|
+
},
|
|
327
|
+
devDependencies: {
|
|
328
|
+
"@docusaurus/tsconfig": "^3.9.2",
|
|
329
|
+
"@docusaurus/types": "^3.9.2"
|
|
330
|
+
},
|
|
331
|
+
scripts: {
|
|
332
|
+
"docs:dev": "docusaurus start",
|
|
333
|
+
"docs:build": "docusaurus build",
|
|
334
|
+
"docs:serve": "docusaurus serve"
|
|
335
|
+
},
|
|
336
|
+
instructions: [`Run \`npm run docs:dev\` inside ${context.outDir} to preview your docs`]
|
|
337
|
+
};
|
|
338
|
+
},
|
|
339
|
+
transformPages(pages, _context) {
|
|
340
|
+
return pages.map((page) => ({
|
|
341
|
+
path: page.path.replace(/\.md$/, ".mdx"),
|
|
342
|
+
content: addDocusaurusFrontmatter(page),
|
|
343
|
+
stub: page.stub
|
|
344
|
+
}));
|
|
345
|
+
},
|
|
346
|
+
generateConfig(context) {
|
|
347
|
+
return [
|
|
348
|
+
{
|
|
349
|
+
path: "sidebars.ts",
|
|
350
|
+
content: buildSidebarsTs(context.pages)
|
|
351
|
+
}
|
|
352
|
+
];
|
|
353
|
+
},
|
|
354
|
+
getDevCommand(outDir) {
|
|
355
|
+
return {
|
|
356
|
+
bin: "npx",
|
|
357
|
+
args: ["docusaurus", "start"],
|
|
358
|
+
cwd: outDir,
|
|
359
|
+
label: "Docusaurus Dev Server",
|
|
360
|
+
url: "http://localhost:3000"
|
|
361
|
+
};
|
|
362
|
+
},
|
|
363
|
+
async detectExisting(outDir) {
|
|
364
|
+
const { existsSync: existsSync3 } = await import("fs");
|
|
365
|
+
const { join: join2 } = await import("path");
|
|
366
|
+
return existsSync3(join2(outDir, "sidebars.ts")) || existsSync3(join2(outDir, "docusaurus.config.ts")) || existsSync3(join2(outDir, "docusaurus.config.js"));
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
registerAdapter(docusaurusAdapter);
|
|
370
|
+
|
|
371
|
+
// src/adapters/nextra.ts
|
|
372
|
+
var styleGuide3 = {
|
|
373
|
+
pageExtension: "mdx",
|
|
374
|
+
supportsMdx: true,
|
|
375
|
+
requiresFrontmatter: false,
|
|
376
|
+
maxHeadingDepth: 4,
|
|
377
|
+
defaultImports: [],
|
|
378
|
+
codeBlockLanguage: "typescript"
|
|
379
|
+
};
|
|
380
|
+
function pageSlug3(pagePath) {
|
|
381
|
+
return pagePath.replace(/\.[^.]+$/, "");
|
|
382
|
+
}
|
|
383
|
+
function slugToLabel(slug) {
|
|
384
|
+
return slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
385
|
+
}
|
|
386
|
+
function renderMetaJs(meta) {
|
|
387
|
+
const json = JSON.stringify(meta, null, 2);
|
|
388
|
+
return `export default ${json};
|
|
389
|
+
`;
|
|
390
|
+
}
|
|
391
|
+
function buildMetaFiles(pages) {
|
|
392
|
+
const topLevel = [];
|
|
393
|
+
const byPackage = /* @__PURE__ */ new Map();
|
|
394
|
+
for (const page of pages) {
|
|
395
|
+
const slug = pageSlug3(page.path);
|
|
396
|
+
const packageMatch = /^packages\/([^/]+)\/(.+)$/.exec(slug);
|
|
397
|
+
if (packageMatch) {
|
|
398
|
+
const pkgName = packageMatch[1];
|
|
399
|
+
const list = byPackage.get(pkgName) ?? [];
|
|
400
|
+
list.push(slug);
|
|
401
|
+
byPackage.set(pkgName, list);
|
|
402
|
+
} else {
|
|
403
|
+
topLevel.push(slug);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
const files = [];
|
|
407
|
+
const rootMeta = {};
|
|
408
|
+
for (const slug of topLevel) {
|
|
409
|
+
const segment = slug.split("/").pop() ?? slug;
|
|
410
|
+
rootMeta[segment] = slugToLabel(segment);
|
|
411
|
+
}
|
|
412
|
+
if (byPackage.size > 0) {
|
|
413
|
+
rootMeta.packages = "Packages";
|
|
414
|
+
}
|
|
415
|
+
files.push({
|
|
416
|
+
path: "content/_meta.js",
|
|
417
|
+
content: renderMetaJs(rootMeta)
|
|
418
|
+
});
|
|
419
|
+
for (const [pkgName, slugs] of byPackage) {
|
|
420
|
+
const pkgMeta = {};
|
|
421
|
+
for (const slug of slugs) {
|
|
422
|
+
const segment = slug.split("/").pop() ?? slug;
|
|
423
|
+
pkgMeta[segment] = slugToLabel(segment);
|
|
424
|
+
}
|
|
425
|
+
files.push({
|
|
426
|
+
path: `content/packages/${pkgName}/_meta.js`,
|
|
427
|
+
content: renderMetaJs(pkgMeta)
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
if (byPackage.size > 1) {
|
|
431
|
+
const packagesMeta = {};
|
|
432
|
+
for (const pkgName of byPackage.keys()) {
|
|
433
|
+
packagesMeta[pkgName] = pkgName;
|
|
434
|
+
}
|
|
435
|
+
files.push({
|
|
436
|
+
path: "content/packages/_meta.js",
|
|
437
|
+
content: renderMetaJs(packagesMeta)
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
return files;
|
|
441
|
+
}
|
|
442
|
+
function buildAppLayout(context) {
|
|
443
|
+
const { projectName } = context;
|
|
444
|
+
return `import { Layout } from "nextra-theme-docs";
|
|
445
|
+
import { Head } from "nextra/components";
|
|
446
|
+
import { getPageMap } from "nextra/page-map";
|
|
447
|
+
import type { ReactNode } from "react";
|
|
448
|
+
|
|
449
|
+
export const metadata = {
|
|
450
|
+
title: {
|
|
451
|
+
template: \`%s \u2013 ${projectName}\`,
|
|
452
|
+
},
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
export default async function RootLayout({ children }: { children: ReactNode }) {
|
|
456
|
+
const pageMap = await getPageMap();
|
|
457
|
+
return (
|
|
458
|
+
<html lang="en" suppressHydrationWarning>
|
|
459
|
+
<Head />
|
|
460
|
+
<body>
|
|
461
|
+
<Layout pageMap={pageMap} docsRepositoryBase="">
|
|
462
|
+
{children}
|
|
463
|
+
</Layout>
|
|
464
|
+
</body>
|
|
465
|
+
</html>
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
`;
|
|
469
|
+
}
|
|
470
|
+
function buildCatchAllPage() {
|
|
471
|
+
return `import { generateStaticParamsFor, importPage } from "nextra/pages";
|
|
472
|
+
|
|
473
|
+
export const generateStaticParams = generateStaticParamsFor("mdxPath");
|
|
474
|
+
|
|
475
|
+
export async function generateMetadata(props: {
|
|
476
|
+
params: Promise<Record<string, string[]>>;
|
|
477
|
+
}) {
|
|
478
|
+
const params = await props.params;
|
|
479
|
+
const { metadata } = await importPage(params.mdxPath);
|
|
480
|
+
return metadata;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const Wrapper = importPage;
|
|
484
|
+
export default async function Page(props: {
|
|
485
|
+
params: Promise<Record<string, string[]>>;
|
|
486
|
+
}) {
|
|
487
|
+
const params = await props.params;
|
|
488
|
+
const result = await importPage(params.mdxPath);
|
|
489
|
+
const { default: MDXContent } = result;
|
|
490
|
+
return <MDXContent {...props} params={params} />;
|
|
491
|
+
}
|
|
492
|
+
`;
|
|
493
|
+
}
|
|
494
|
+
function buildMdxComponents() {
|
|
495
|
+
return `import { useMDXComponents as getNextraComponents } from "nextra-theme-docs";
|
|
496
|
+
|
|
497
|
+
export function useMDXComponents(components: Record<string, unknown>) {
|
|
498
|
+
return getNextraComponents(components);
|
|
499
|
+
}
|
|
500
|
+
`;
|
|
501
|
+
}
|
|
502
|
+
function buildNextConfig() {
|
|
503
|
+
return `import nextra from "nextra";
|
|
504
|
+
|
|
505
|
+
const withNextra = nextra({
|
|
506
|
+
contentDirBasePath: "/",
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
export default withNextra({
|
|
510
|
+
output: "export",
|
|
511
|
+
images: { unoptimized: true },
|
|
512
|
+
});
|
|
513
|
+
`;
|
|
514
|
+
}
|
|
515
|
+
function buildPackageJson2(context) {
|
|
516
|
+
const pkg = {
|
|
517
|
+
name: `${context.projectName}-docs`,
|
|
518
|
+
version: "0.0.0",
|
|
519
|
+
private: true,
|
|
520
|
+
scripts: {
|
|
521
|
+
"docs:dev": "next dev",
|
|
522
|
+
"docs:build": "next build",
|
|
523
|
+
"docs:serve": "next start"
|
|
524
|
+
},
|
|
525
|
+
dependencies: {
|
|
526
|
+
next: "^15",
|
|
527
|
+
nextra: "^4",
|
|
528
|
+
"nextra-theme-docs": "^4",
|
|
529
|
+
react: "^19",
|
|
530
|
+
"react-dom": "^19"
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
return `${JSON.stringify(pkg, null, 2)}
|
|
534
|
+
`;
|
|
535
|
+
}
|
|
536
|
+
function addNextraFrontmatter(page) {
|
|
537
|
+
const title = String(page.frontmatter.title ?? "");
|
|
538
|
+
if (!title) {
|
|
539
|
+
return page.content;
|
|
540
|
+
}
|
|
541
|
+
const lines = ["---", `title: "${title}"`, "---", ""];
|
|
542
|
+
const body = page.content.replace(/^---[\s\S]*?---\n+/, "");
|
|
543
|
+
return lines.join("\n") + body;
|
|
544
|
+
}
|
|
545
|
+
var nextraAdapter = {
|
|
546
|
+
target: "nextra",
|
|
547
|
+
displayName: "Nextra",
|
|
548
|
+
styleGuide: styleGuide3,
|
|
549
|
+
scaffold(context) {
|
|
550
|
+
const metaFiles = buildMetaFiles(context.pages);
|
|
551
|
+
return {
|
|
552
|
+
target: "nextra",
|
|
553
|
+
files: [
|
|
554
|
+
{ path: "next.config.ts", content: buildNextConfig() },
|
|
555
|
+
{ path: "package.json", content: buildPackageJson2(context) },
|
|
556
|
+
{ path: "mdx-components.ts", content: buildMdxComponents() },
|
|
557
|
+
{ path: "app/layout.tsx", content: buildAppLayout(context) },
|
|
558
|
+
{ path: "app/[[...mdxPath]]/page.tsx", content: buildCatchAllPage() },
|
|
559
|
+
...metaFiles
|
|
560
|
+
],
|
|
561
|
+
dependencies: {
|
|
562
|
+
next: "^15",
|
|
563
|
+
nextra: "^4",
|
|
564
|
+
"nextra-theme-docs": "^4",
|
|
565
|
+
react: "^19",
|
|
566
|
+
"react-dom": "^19"
|
|
567
|
+
},
|
|
568
|
+
devDependencies: {},
|
|
569
|
+
scripts: {
|
|
570
|
+
"docs:dev": "next dev",
|
|
571
|
+
"docs:build": "next build",
|
|
572
|
+
"docs:serve": "next start"
|
|
573
|
+
},
|
|
574
|
+
instructions: [`Run \`npm run docs:dev\` inside ${context.outDir} to preview your docs`]
|
|
575
|
+
};
|
|
576
|
+
},
|
|
577
|
+
transformPages(pages, _context) {
|
|
578
|
+
return pages.map((page) => ({
|
|
579
|
+
path: page.path.replace(/\.md$/, ".mdx"),
|
|
580
|
+
content: addNextraFrontmatter(page),
|
|
581
|
+
stub: page.stub
|
|
582
|
+
}));
|
|
583
|
+
},
|
|
584
|
+
generateConfig(context) {
|
|
585
|
+
return buildMetaFiles(context.pages);
|
|
586
|
+
},
|
|
587
|
+
getDevCommand(outDir) {
|
|
588
|
+
return {
|
|
589
|
+
bin: "npx",
|
|
590
|
+
args: ["next", "dev"],
|
|
591
|
+
cwd: outDir,
|
|
592
|
+
label: "Nextra Dev Server (Next.js)",
|
|
593
|
+
url: "http://localhost:3000"
|
|
594
|
+
};
|
|
595
|
+
},
|
|
596
|
+
async detectExisting(outDir) {
|
|
597
|
+
const { existsSync: existsSync3 } = await import("fs");
|
|
598
|
+
const { join: join2 } = await import("path");
|
|
599
|
+
return existsSync3(join2(outDir, "content/_meta.js")) || existsSync3(join2(outDir, "next.config.ts")) || existsSync3(join2(outDir, "next.config.js"));
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
registerAdapter(nextraAdapter);
|
|
603
|
+
|
|
604
|
+
// src/adapters/vitepress.ts
|
|
605
|
+
var styleGuide4 = {
|
|
606
|
+
pageExtension: "md",
|
|
607
|
+
supportsMdx: false,
|
|
608
|
+
requiresFrontmatter: true,
|
|
609
|
+
maxHeadingDepth: 4,
|
|
610
|
+
defaultImports: [],
|
|
611
|
+
codeBlockLanguage: "typescript"
|
|
612
|
+
};
|
|
613
|
+
function pageSlug4(pagePath) {
|
|
614
|
+
return pagePath.replace(/\.[^.]+$/, "");
|
|
615
|
+
}
|
|
616
|
+
function slugToLabel2(slug) {
|
|
617
|
+
return slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
618
|
+
}
|
|
619
|
+
function buildSidebar(pages) {
|
|
620
|
+
const topLevel = [];
|
|
621
|
+
const byPackage = /* @__PURE__ */ new Map();
|
|
622
|
+
for (const page of pages) {
|
|
623
|
+
const slug = pageSlug4(page.path);
|
|
624
|
+
const packageMatch = /^packages\/([^/]+)\/(.+)$/.exec(slug);
|
|
625
|
+
if (packageMatch) {
|
|
626
|
+
const pkgName = packageMatch[1];
|
|
627
|
+
const list = byPackage.get(pkgName) ?? [];
|
|
628
|
+
list.push(slug);
|
|
629
|
+
byPackage.set(pkgName, list);
|
|
630
|
+
} else {
|
|
631
|
+
topLevel.push(slug);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
const sidebar = [];
|
|
635
|
+
if (topLevel.length > 0) {
|
|
636
|
+
const items = topLevel.map((slug) => {
|
|
637
|
+
const segment = slug.split("/").pop() ?? slug;
|
|
638
|
+
const link = slug === "index" ? "/" : `/${slug}`;
|
|
639
|
+
return { text: slugToLabel2(segment), link };
|
|
640
|
+
});
|
|
641
|
+
sidebar.push({ text: "Getting Started", items });
|
|
642
|
+
}
|
|
643
|
+
for (const [pkgName, slugs] of byPackage) {
|
|
644
|
+
const items = slugs.map((slug) => {
|
|
645
|
+
const segment = slug.split("/").pop() ?? slug;
|
|
646
|
+
const isIndex = segment === "index";
|
|
647
|
+
const link = isIndex ? `/packages/${pkgName}/` : `/${slug}`;
|
|
648
|
+
return { text: slugToLabel2(segment), link };
|
|
649
|
+
});
|
|
650
|
+
sidebar.push({ text: pkgName, items });
|
|
651
|
+
}
|
|
652
|
+
return sidebar;
|
|
653
|
+
}
|
|
654
|
+
function buildVitePressConfig(context) {
|
|
655
|
+
const { projectName, projectDescription } = context;
|
|
656
|
+
const description = projectDescription ?? `${projectName} documentation`;
|
|
657
|
+
const sidebar = buildSidebar(context.pages);
|
|
658
|
+
const sidebarJson = JSON.stringify(sidebar, null, 4).split("\n").join("\n ");
|
|
659
|
+
return `import { defineConfig } from "vitepress";
|
|
660
|
+
|
|
661
|
+
// https://vitepress.dev/reference/site-config
|
|
662
|
+
export default defineConfig({
|
|
663
|
+
title: "${projectName}",
|
|
664
|
+
description: "${description}",
|
|
665
|
+
themeConfig: {
|
|
666
|
+
nav: [
|
|
667
|
+
{ text: "Home", link: "/" },
|
|
668
|
+
],
|
|
669
|
+
sidebar: ${sidebarJson},
|
|
670
|
+
socialLinks: [],
|
|
671
|
+
},
|
|
672
|
+
});
|
|
673
|
+
`;
|
|
674
|
+
}
|
|
675
|
+
function buildPackageJson3(context) {
|
|
676
|
+
const pkg = {
|
|
677
|
+
name: `${context.projectName}-docs`,
|
|
678
|
+
version: "0.0.0",
|
|
679
|
+
private: true,
|
|
680
|
+
type: "module",
|
|
681
|
+
scripts: {
|
|
682
|
+
"docs:dev": "vitepress dev",
|
|
683
|
+
"docs:build": "vitepress build",
|
|
684
|
+
"docs:preview": "vitepress preview"
|
|
685
|
+
},
|
|
686
|
+
devDependencies: {
|
|
687
|
+
vitepress: "^2.0.0"
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
return `${JSON.stringify(pkg, null, 2)}
|
|
691
|
+
`;
|
|
692
|
+
}
|
|
693
|
+
function addVitepressFrontmatter(page) {
|
|
694
|
+
const title = String(page.frontmatter.title ?? "");
|
|
695
|
+
const description = page.frontmatter.description ? String(page.frontmatter.description) : void 0;
|
|
696
|
+
const lines = ["---", `title: "${title}"`, "outline: deep"];
|
|
697
|
+
if (description) {
|
|
698
|
+
lines.push(`description: "${description}"`);
|
|
699
|
+
}
|
|
700
|
+
lines.push("---", "");
|
|
701
|
+
const body = page.content.replace(/^---[\s\S]*?---\n+/, "");
|
|
702
|
+
return lines.join("\n") + body;
|
|
703
|
+
}
|
|
704
|
+
var vitepressAdapter = {
|
|
705
|
+
target: "vitepress",
|
|
706
|
+
displayName: "VitePress",
|
|
707
|
+
styleGuide: styleGuide4,
|
|
708
|
+
scaffold(context) {
|
|
709
|
+
return {
|
|
710
|
+
target: "vitepress",
|
|
711
|
+
files: [
|
|
712
|
+
{ path: ".vitepress/config.mts", content: buildVitePressConfig(context) },
|
|
713
|
+
{ path: "package.json", content: buildPackageJson3(context) }
|
|
714
|
+
],
|
|
715
|
+
dependencies: {},
|
|
716
|
+
devDependencies: {
|
|
717
|
+
vitepress: "^2.0.0"
|
|
718
|
+
},
|
|
719
|
+
scripts: {
|
|
720
|
+
"docs:dev": "vitepress dev",
|
|
721
|
+
"docs:build": "vitepress build",
|
|
722
|
+
"docs:preview": "vitepress preview"
|
|
723
|
+
},
|
|
724
|
+
instructions: [`Run \`npm run docs:dev\` inside ${context.outDir} to preview your docs`]
|
|
725
|
+
};
|
|
726
|
+
},
|
|
727
|
+
transformPages(pages, _context) {
|
|
728
|
+
return pages.map((page) => ({
|
|
729
|
+
path: page.path.endsWith(".mdx") ? page.path.replace(/\.mdx$/, ".md") : page.path,
|
|
730
|
+
content: addVitepressFrontmatter(page),
|
|
731
|
+
stub: page.stub
|
|
732
|
+
}));
|
|
733
|
+
},
|
|
734
|
+
generateConfig(context) {
|
|
735
|
+
return [
|
|
736
|
+
{
|
|
737
|
+
path: ".vitepress/config.mts",
|
|
738
|
+
content: buildVitePressConfig(context)
|
|
739
|
+
}
|
|
740
|
+
];
|
|
741
|
+
},
|
|
742
|
+
getDevCommand(outDir) {
|
|
743
|
+
return {
|
|
744
|
+
bin: "npx",
|
|
745
|
+
args: ["vitepress", "dev"],
|
|
746
|
+
cwd: outDir,
|
|
747
|
+
label: "VitePress Dev Server",
|
|
748
|
+
url: "http://localhost:5173"
|
|
749
|
+
};
|
|
750
|
+
},
|
|
751
|
+
async detectExisting(outDir) {
|
|
752
|
+
const { existsSync: existsSync3 } = await import("fs");
|
|
753
|
+
const { join: join2 } = await import("path");
|
|
754
|
+
return existsSync3(join2(outDir, ".vitepress"));
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
registerAdapter(vitepressAdapter);
|
|
758
|
+
|
|
1
759
|
// src/llms.ts
|
|
2
760
|
function compactEntry(symbol) {
|
|
3
761
|
if (symbol.signature) {
|
|
@@ -313,7 +1071,7 @@ function generateMarkdown(symbols, config, options = {}) {
|
|
|
313
1071
|
}
|
|
314
1072
|
parts.push("# API Reference\n");
|
|
315
1073
|
parts.push(
|
|
316
|
-
`Generated by [forge-ts](https://github.com/
|
|
1074
|
+
`Generated by [forge-ts](https://github.com/kryptobaseddev/forge-ts) from \`${config.rootDir}\`.
|
|
317
1075
|
`
|
|
318
1076
|
);
|
|
319
1077
|
if (topLevel.length > 0) {
|
|
@@ -462,6 +1220,15 @@ function buildFrontmatterFields(title, description, ssgTarget, sidebarPosition)
|
|
|
462
1220
|
return {};
|
|
463
1221
|
}
|
|
464
1222
|
}
|
|
1223
|
+
function slugLink(path) {
|
|
1224
|
+
let slug = path.startsWith("./") ? path.slice(2) : path;
|
|
1225
|
+
slug = slug.replace(/\.(mdx?)$/, "");
|
|
1226
|
+
return `/${slug}`;
|
|
1227
|
+
}
|
|
1228
|
+
function truncate(text, maxLen = 80) {
|
|
1229
|
+
if (text.length <= maxLen) return text;
|
|
1230
|
+
return `${text.slice(0, maxLen - 3)}...`;
|
|
1231
|
+
}
|
|
465
1232
|
function groupSymbolsByPackage(symbols, rootDir) {
|
|
466
1233
|
const result = /* @__PURE__ */ new Map();
|
|
467
1234
|
for (const symbol of symbols) {
|
|
@@ -476,55 +1243,295 @@ function groupSymbolsByPackage(symbols, rootDir) {
|
|
|
476
1243
|
}
|
|
477
1244
|
var TYPE_KINDS = /* @__PURE__ */ new Set(["interface", "type", "enum"]);
|
|
478
1245
|
var FUNCTION_KINDS = /* @__PURE__ */ new Set(["function", "class"]);
|
|
479
|
-
function
|
|
480
|
-
const name = `\`${child.name}\``;
|
|
481
|
-
const type = child.signature ? `\`${escapePipe(child.signature)}\`` : "\u2014";
|
|
482
|
-
const optional = child.signature?.includes("?") || child.signature?.includes("undefined") ? "No" : "Yes";
|
|
483
|
-
const description = escapePipe(child.documentation?.summary ?? "");
|
|
484
|
-
return `| ${name} | ${type} | ${optional} | ${description} |`;
|
|
485
|
-
}
|
|
486
|
-
function renderOverviewPage(packageName, symbols, _options) {
|
|
487
|
-
const exported = symbols.filter(
|
|
488
|
-
(s) => s.exported && s.kind !== "method" && s.kind !== "property"
|
|
489
|
-
);
|
|
490
|
-
const pkgDoc = symbols.map((s) => s.documentation?.tags?.packageDocumentation?.[0]).find(Boolean);
|
|
1246
|
+
function renderProjectIndexPage(symbolsByPackage, options) {
|
|
491
1247
|
const lines = [];
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
lines.push(
|
|
496
|
-
|
|
1248
|
+
if (options.projectDescription) {
|
|
1249
|
+
lines.push(`**${options.projectName}** \u2014 ${options.projectDescription}`);
|
|
1250
|
+
} else {
|
|
1251
|
+
lines.push(
|
|
1252
|
+
`**${options.projectName}** is a TypeScript documentation toolkit that performs a single AST traversal of your project and produces API docs, OpenAPI specs, executable doctests, and AI context files in one pass.`
|
|
1253
|
+
);
|
|
497
1254
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
const summary = escapePipe(s.documentation?.summary ?? "");
|
|
507
|
-
lines.push(`| ${name} | ${s.kind} | ${summary} |`);
|
|
508
|
-
}
|
|
509
|
-
lines.push("");
|
|
1255
|
+
lines.push("");
|
|
1256
|
+
lines.push("## Features");
|
|
1257
|
+
lines.push("");
|
|
1258
|
+
const pkgCount = symbolsByPackage.size;
|
|
1259
|
+
if (pkgCount > 1) {
|
|
1260
|
+
lines.push(`- ${pkgCount} packages with full TypeScript support`);
|
|
1261
|
+
} else {
|
|
1262
|
+
lines.push("- Full TypeScript support with TSDoc extraction");
|
|
510
1263
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
const typeSymbols = symbols.filter((s) => s.exported && TYPE_KINDS.has(s.kind));
|
|
515
|
-
const lines = [];
|
|
516
|
-
lines.push(`# ${packageName} \u2014 Types`);
|
|
1264
|
+
lines.push("- Auto-generated API reference from source code");
|
|
1265
|
+
lines.push("- Executable `@example` blocks as doctests");
|
|
1266
|
+
lines.push("- AI-ready context files from a single build pass");
|
|
517
1267
|
lines.push("");
|
|
518
|
-
lines.push("
|
|
1268
|
+
lines.push("## Installation");
|
|
519
1269
|
lines.push("");
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
1270
|
+
lines.push("```bash");
|
|
1271
|
+
lines.push(`npm install -D ${options.packageName ?? "@forge-ts/cli"}`);
|
|
1272
|
+
lines.push("```");
|
|
1273
|
+
lines.push("");
|
|
1274
|
+
let firstExample;
|
|
1275
|
+
outer: for (const [, symbols] of symbolsByPackage) {
|
|
1276
|
+
for (const s of symbols) {
|
|
1277
|
+
if (!s.exported || s.kind !== "function") continue;
|
|
1278
|
+
const ex = s.documentation?.examples?.[0];
|
|
1279
|
+
if (ex) {
|
|
1280
|
+
firstExample = ex;
|
|
1281
|
+
break outer;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
if (firstExample) {
|
|
1286
|
+
lines.push("## Quick Example");
|
|
1287
|
+
lines.push("");
|
|
1288
|
+
lines.push(`\`\`\`${firstExample.language || "typescript"}`);
|
|
1289
|
+
lines.push(firstExample.code.trim());
|
|
1290
|
+
lines.push("```");
|
|
1291
|
+
lines.push("");
|
|
1292
|
+
}
|
|
1293
|
+
if (symbolsByPackage.size > 0) {
|
|
1294
|
+
lines.push("## Packages");
|
|
1295
|
+
lines.push("");
|
|
1296
|
+
lines.push("| Package | Description |");
|
|
1297
|
+
lines.push("|---------|-------------|");
|
|
1298
|
+
for (const [pkgName, symbols] of symbolsByPackage) {
|
|
1299
|
+
const pkgDoc = symbols.map((s) => s.documentation?.tags?.packageDocumentation?.[0]).find(Boolean);
|
|
1300
|
+
const exported = symbols.filter(
|
|
1301
|
+
(s) => s.exported && s.kind !== "method" && s.kind !== "property"
|
|
1302
|
+
);
|
|
1303
|
+
const rawDesc = pkgDoc ?? `${exported.length} exported symbol(s).`;
|
|
1304
|
+
const desc = escapePipe(truncate(rawDesc));
|
|
1305
|
+
const link = `[\`${pkgName}\`](${slugLink(`packages/${pkgName}/index`)})`;
|
|
1306
|
+
lines.push(`| ${link} | ${desc} |`);
|
|
1307
|
+
}
|
|
1308
|
+
lines.push("");
|
|
1309
|
+
}
|
|
1310
|
+
lines.push("## Next Steps");
|
|
1311
|
+
lines.push("");
|
|
1312
|
+
lines.push(`- [Getting Started](/getting-started) \u2014 Step-by-step guide`);
|
|
1313
|
+
lines.push(`- [API Reference](/packages) \u2014 Full API documentation`);
|
|
1314
|
+
lines.push(`- [Concepts](/concepts) \u2014 How it works`);
|
|
1315
|
+
return lines.join("\n");
|
|
1316
|
+
}
|
|
1317
|
+
function renderGettingStartedPage(symbolsByPackage, options) {
|
|
1318
|
+
let firstExample;
|
|
1319
|
+
outer: for (const [, symbols] of symbolsByPackage) {
|
|
1320
|
+
for (const s of symbols) {
|
|
1321
|
+
if (!s.exported || s.kind !== "function") continue;
|
|
1322
|
+
const ex = s.documentation?.examples?.[0];
|
|
1323
|
+
if (ex) {
|
|
1324
|
+
firstExample = ex;
|
|
1325
|
+
break outer;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
const lines = [];
|
|
1330
|
+
lines.push(`Get up and running with **${options.projectName}** in minutes.`);
|
|
1331
|
+
if (options.projectDescription) {
|
|
1332
|
+
lines.push("");
|
|
1333
|
+
lines.push(options.projectDescription);
|
|
1334
|
+
}
|
|
1335
|
+
lines.push("");
|
|
1336
|
+
lines.push("## Step 1: Install");
|
|
1337
|
+
lines.push("");
|
|
1338
|
+
lines.push("```bash");
|
|
1339
|
+
lines.push(`npm install -D ${options.packageName ?? "@forge-ts/cli"}`);
|
|
1340
|
+
lines.push("```");
|
|
1341
|
+
lines.push("");
|
|
1342
|
+
lines.push("## Step 2: Add TSDoc to your code");
|
|
1343
|
+
lines.push("");
|
|
1344
|
+
lines.push("Add TSDoc comments to your exported functions and types:");
|
|
1345
|
+
lines.push("");
|
|
1346
|
+
lines.push("```typescript");
|
|
1347
|
+
lines.push("/**");
|
|
1348
|
+
lines.push(" * Adds two numbers together.");
|
|
1349
|
+
lines.push(" * @param a - First number");
|
|
1350
|
+
lines.push(" * @param b - Second number");
|
|
1351
|
+
lines.push(" * @returns The sum of a and b");
|
|
1352
|
+
lines.push(" * @example");
|
|
1353
|
+
lines.push(" * ```typescript");
|
|
1354
|
+
lines.push(" * const result = add(1, 2); // => 3");
|
|
1355
|
+
lines.push(" * ```");
|
|
1356
|
+
lines.push(" */");
|
|
1357
|
+
lines.push("export function add(a: number, b: number): number {");
|
|
1358
|
+
lines.push(" return a + b;");
|
|
1359
|
+
lines.push("}");
|
|
1360
|
+
lines.push("```");
|
|
1361
|
+
lines.push("");
|
|
1362
|
+
lines.push("## Step 3: Run forge-ts check");
|
|
1363
|
+
lines.push("");
|
|
1364
|
+
lines.push("Lint your TSDoc coverage before generating docs:");
|
|
1365
|
+
lines.push("");
|
|
1366
|
+
lines.push("```bash");
|
|
1367
|
+
lines.push("npx forge-ts check");
|
|
1368
|
+
lines.push("```");
|
|
1369
|
+
lines.push("");
|
|
1370
|
+
lines.push("Expected output:");
|
|
1371
|
+
lines.push("");
|
|
1372
|
+
lines.push("```");
|
|
1373
|
+
lines.push("forge-ts: checking TSDoc coverage...");
|
|
1374
|
+
lines.push(" \u2713 All public symbols documented");
|
|
1375
|
+
lines.push("```");
|
|
1376
|
+
lines.push("");
|
|
1377
|
+
lines.push("## Step 4: Generate docs");
|
|
1378
|
+
lines.push("");
|
|
1379
|
+
lines.push("Build your documentation site:");
|
|
1380
|
+
lines.push("");
|
|
1381
|
+
lines.push("```bash");
|
|
1382
|
+
lines.push("npx forge-ts build");
|
|
1383
|
+
lines.push("```");
|
|
1384
|
+
lines.push("");
|
|
1385
|
+
if (firstExample) {
|
|
1386
|
+
lines.push("Your code examples become live documentation:");
|
|
1387
|
+
lines.push("");
|
|
1388
|
+
lines.push(`\`\`\`${firstExample.language || "typescript"}`);
|
|
1389
|
+
lines.push(firstExample.code.trim());
|
|
1390
|
+
lines.push("```");
|
|
1391
|
+
lines.push("");
|
|
1392
|
+
}
|
|
1393
|
+
lines.push("## What's Next?");
|
|
1394
|
+
lines.push("");
|
|
1395
|
+
lines.push("- [Concepts](/concepts) \u2014 Understand how forge-ts works");
|
|
1396
|
+
lines.push("- [API Reference](/packages) \u2014 Full API documentation");
|
|
1397
|
+
lines.push("- [Guides](/guides) \u2014 Practical how-to guides");
|
|
1398
|
+
return lines.join("\n");
|
|
1399
|
+
}
|
|
1400
|
+
function renderConceptsPage(symbolsByPackage, options) {
|
|
1401
|
+
const lines = [];
|
|
1402
|
+
lines.push(`This page explains the core concepts behind **${options.projectName}**.`);
|
|
1403
|
+
lines.push("");
|
|
1404
|
+
lines.push(
|
|
1405
|
+
"> This is a stub page. Edit this file to add your project's conceptual documentation."
|
|
1406
|
+
);
|
|
1407
|
+
lines.push("> Auto-generated sections below (inside FORGE:AUTO markers) update on every build.");
|
|
1408
|
+
lines.push("");
|
|
1409
|
+
const pkgDoc = [...symbolsByPackage.values()].flatMap((syms) => syms.map((s) => s.documentation?.tags?.packageDocumentation?.[0])).find(Boolean);
|
|
1410
|
+
lines.push("<!-- FORGE:AUTO-START how-it-works -->");
|
|
1411
|
+
lines.push("## How It Works");
|
|
1412
|
+
lines.push("");
|
|
1413
|
+
if (pkgDoc) {
|
|
1414
|
+
lines.push(pkgDoc);
|
|
1415
|
+
} else {
|
|
1416
|
+
lines.push(
|
|
1417
|
+
`${options.projectName} processes your TypeScript source with a single AST traversal, extracting TSDoc comments and type information to generate documentation.`
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
lines.push("");
|
|
1421
|
+
lines.push("<!-- FORGE:AUTO-END how-it-works -->");
|
|
1422
|
+
lines.push("");
|
|
1423
|
+
const allTypeSymbols = [...symbolsByPackage.values()].flat().filter((s) => s.exported && TYPE_KINDS.has(s.kind));
|
|
1424
|
+
if (allTypeSymbols.length > 0) {
|
|
1425
|
+
lines.push("<!-- FORGE:AUTO-START key-abstractions -->");
|
|
1426
|
+
lines.push("## Key Abstractions");
|
|
1427
|
+
lines.push("");
|
|
1428
|
+
for (const s of allTypeSymbols) {
|
|
1429
|
+
const desc = s.documentation?.summary ?? `The \`${s.name}\` ${s.kind}.`;
|
|
1430
|
+
lines.push(`- **\`${s.name}\`** \u2014 ${desc}`);
|
|
1431
|
+
}
|
|
1432
|
+
lines.push("");
|
|
1433
|
+
lines.push("<!-- FORGE:AUTO-END key-abstractions -->");
|
|
1434
|
+
lines.push("");
|
|
1435
|
+
}
|
|
1436
|
+
return lines.join("\n");
|
|
1437
|
+
}
|
|
1438
|
+
function renderGuidesIndexPage() {
|
|
1439
|
+
return [
|
|
1440
|
+
"Practical how-to guides for common tasks.",
|
|
1441
|
+
"",
|
|
1442
|
+
"> Add your guides to the `guides/` directory. Each `.md` or `.mdx` file will appear here automatically.",
|
|
1443
|
+
"",
|
|
1444
|
+
"## Getting Things Done",
|
|
1445
|
+
"",
|
|
1446
|
+
"Guides will appear here as you add them. Start by creating a file like `guides/my-guide.md`.",
|
|
1447
|
+
""
|
|
1448
|
+
].join("\n");
|
|
1449
|
+
}
|
|
1450
|
+
function renderApiIndexPage(pkgName, symbols) {
|
|
1451
|
+
const exported = symbols.filter(
|
|
1452
|
+
(s) => s.exported && s.kind !== "method" && s.kind !== "property"
|
|
1453
|
+
);
|
|
1454
|
+
const functions = exported.filter((s) => FUNCTION_KINDS.has(s.kind));
|
|
1455
|
+
const types = exported.filter((s) => TYPE_KINDS.has(s.kind));
|
|
1456
|
+
const others = exported.filter((s) => !FUNCTION_KINDS.has(s.kind) && !TYPE_KINDS.has(s.kind));
|
|
1457
|
+
const lines = [];
|
|
1458
|
+
const pkgDoc = symbols.map((s) => s.documentation?.tags?.packageDocumentation?.[0]).find(Boolean);
|
|
1459
|
+
if (pkgDoc) {
|
|
1460
|
+
lines.push(pkgDoc);
|
|
1461
|
+
lines.push("");
|
|
1462
|
+
} else {
|
|
1463
|
+
lines.push(`API reference for the \`${pkgName}\` package.`);
|
|
1464
|
+
lines.push("");
|
|
1465
|
+
}
|
|
1466
|
+
const renderGroup = (group, heading, pathSuffix) => {
|
|
1467
|
+
if (group.length === 0) return;
|
|
1468
|
+
lines.push(`## ${heading}`);
|
|
1469
|
+
lines.push("");
|
|
1470
|
+
lines.push("| Symbol | Kind | Description |");
|
|
1471
|
+
lines.push("|--------|------|-------------|");
|
|
1472
|
+
for (const s of group) {
|
|
1473
|
+
const ext = s.kind === "function" ? "()" : "";
|
|
1474
|
+
const anchor = toAnchor2(`${s.name}${ext}`);
|
|
1475
|
+
const link = `[\`${s.name}${ext}\`](${slugLink(`packages/${pkgName}/${pathSuffix}`)}#${anchor})`;
|
|
1476
|
+
const rawSummary = s.documentation?.summary ?? "";
|
|
1477
|
+
const summary = escapePipe(truncate(rawSummary));
|
|
1478
|
+
lines.push(`| ${link} | ${s.kind} | ${summary} |`);
|
|
1479
|
+
}
|
|
1480
|
+
lines.push("");
|
|
1481
|
+
};
|
|
1482
|
+
renderGroup(functions, "Functions & Classes", "api/functions");
|
|
1483
|
+
renderGroup(types, "Types & Interfaces", "api/types");
|
|
1484
|
+
renderGroup(others, "Other Exports", "api/functions");
|
|
1485
|
+
return lines.join("\n");
|
|
1486
|
+
}
|
|
1487
|
+
function renderPackageOverviewPage(packageName, symbols, _options) {
|
|
1488
|
+
const exported = symbols.filter(
|
|
1489
|
+
(s) => s.exported && s.kind !== "method" && s.kind !== "property"
|
|
1490
|
+
);
|
|
1491
|
+
const pkgDoc = symbols.map((s) => s.documentation?.tags?.packageDocumentation?.[0]).find(Boolean);
|
|
1492
|
+
const lines = [];
|
|
1493
|
+
if (pkgDoc) {
|
|
1494
|
+
lines.push(pkgDoc);
|
|
1495
|
+
lines.push("");
|
|
1496
|
+
}
|
|
1497
|
+
if (exported.length > 0) {
|
|
1498
|
+
const functions = exported.filter((s) => FUNCTION_KINDS.has(s.kind));
|
|
1499
|
+
const types = exported.filter((s) => TYPE_KINDS.has(s.kind));
|
|
1500
|
+
const others = exported.filter((s) => !FUNCTION_KINDS.has(s.kind) && !TYPE_KINDS.has(s.kind));
|
|
1501
|
+
const renderGroup = (group, heading) => {
|
|
1502
|
+
if (group.length === 0) return;
|
|
1503
|
+
lines.push(`## ${heading}`);
|
|
1504
|
+
lines.push("");
|
|
1505
|
+
lines.push("| Symbol | Kind | Description |");
|
|
1506
|
+
lines.push("|--------|------|-------------|");
|
|
1507
|
+
for (const s of group) {
|
|
1508
|
+
const ext = s.kind === "function" ? "()" : "";
|
|
1509
|
+
const name = `[\`${s.name}${ext}\`](${slugLink(`packages/${packageName}/api/index`)}#${toAnchor2(`${s.name}${ext}`)})`;
|
|
1510
|
+
const rawSummary = s.documentation?.summary ?? "";
|
|
1511
|
+
const summary = escapePipe(truncate(rawSummary));
|
|
1512
|
+
lines.push(`| ${name} | ${s.kind} | ${summary} |`);
|
|
1513
|
+
}
|
|
1514
|
+
lines.push("");
|
|
1515
|
+
};
|
|
1516
|
+
renderGroup(functions, "Functions & Classes");
|
|
1517
|
+
renderGroup(types, "Types & Interfaces");
|
|
1518
|
+
renderGroup(others, "Other Exports");
|
|
1519
|
+
}
|
|
1520
|
+
return lines.join("\n");
|
|
1521
|
+
}
|
|
1522
|
+
function renderTypesPage(packageName, symbols, _options) {
|
|
1523
|
+
const typeSymbols = symbols.filter((s) => s.exported && TYPE_KINDS.has(s.kind));
|
|
1524
|
+
const lines = [];
|
|
1525
|
+
lines.push("Type contracts exported by this package: interfaces, type aliases, and enums.");
|
|
1526
|
+
lines.push("");
|
|
1527
|
+
for (const s of typeSymbols) {
|
|
1528
|
+
lines.push(`## ${s.name}`);
|
|
1529
|
+
lines.push("");
|
|
1530
|
+
if (s.documentation?.deprecated) {
|
|
1531
|
+
lines.push(`> **Deprecated**: ${s.documentation.deprecated}`);
|
|
1532
|
+
lines.push("");
|
|
1533
|
+
}
|
|
1534
|
+
if (s.documentation?.summary) {
|
|
528
1535
|
lines.push(s.documentation.summary);
|
|
529
1536
|
lines.push("");
|
|
530
1537
|
}
|
|
@@ -539,22 +1546,24 @@ function renderTypesPage(packageName, symbols, _options) {
|
|
|
539
1546
|
lines.push("| Property | Type | Required | Description |");
|
|
540
1547
|
lines.push("|----------|------|----------|-------------|");
|
|
541
1548
|
for (const child of children) {
|
|
542
|
-
|
|
1549
|
+
const name = `\`${child.name}\``;
|
|
1550
|
+
const type = child.signature ? `\`${escapePipe(child.signature)}\`` : "\u2014";
|
|
1551
|
+
const optional = child.signature?.includes("?") || child.signature?.includes("undefined") ? "No" : "Yes";
|
|
1552
|
+
const description = escapePipe(child.documentation?.summary || child.name);
|
|
1553
|
+
lines.push(`| ${name} | ${type} | ${optional} | ${description} |`);
|
|
543
1554
|
}
|
|
544
1555
|
lines.push("");
|
|
545
1556
|
}
|
|
546
1557
|
}
|
|
1558
|
+
void packageName;
|
|
547
1559
|
return lines.join("\n");
|
|
548
1560
|
}
|
|
549
1561
|
function renderFunctionsPage(packageName, symbols, _options) {
|
|
550
1562
|
const fnSymbols = symbols.filter((s) => s.exported && FUNCTION_KINDS.has(s.kind));
|
|
551
1563
|
const lines = [];
|
|
552
|
-
lines.push(`# ${packageName} \u2014 Functions & Classes`);
|
|
553
|
-
lines.push("");
|
|
554
1564
|
lines.push("Functions and classes exported by this package.");
|
|
555
1565
|
lines.push("");
|
|
556
1566
|
for (const s of fnSymbols) {
|
|
557
|
-
const _ext = s.kind === "function" ? "()" : "";
|
|
558
1567
|
const paramSig = s.kind === "function" && s.documentation?.params ? s.documentation.params.map((p) => p.name).join(", ") : "";
|
|
559
1568
|
const heading = s.kind === "function" ? `${s.name}(${paramSig})` : s.name;
|
|
560
1569
|
lines.push(`## ${heading}`);
|
|
@@ -582,7 +1591,14 @@ function renderFunctionsPage(packageName, symbols, _options) {
|
|
|
582
1591
|
lines.push("| Name | Type | Description |");
|
|
583
1592
|
lines.push("|------|------|-------------|");
|
|
584
1593
|
for (const p of params) {
|
|
585
|
-
|
|
1594
|
+
let resolvedType = p.type;
|
|
1595
|
+
if (!resolvedType && s.signature) {
|
|
1596
|
+
const typeMatch = new RegExp(`\\b${p.name}\\s*[?:]\\s*([^,)]+)`).exec(s.signature);
|
|
1597
|
+
if (typeMatch) {
|
|
1598
|
+
resolvedType = typeMatch[1].trim();
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
const type = resolvedType ? `\`${escapePipe(resolvedType)}\`` : "\u2014";
|
|
586
1602
|
lines.push(`| \`${p.name}\` | ${type} | ${escapePipe(p.description)} |`);
|
|
587
1603
|
}
|
|
588
1604
|
lines.push("");
|
|
@@ -607,7 +1623,7 @@ function renderFunctionsPage(packageName, symbols, _options) {
|
|
|
607
1623
|
const ex = examples[0];
|
|
608
1624
|
lines.push("**Example**");
|
|
609
1625
|
lines.push("");
|
|
610
|
-
lines.push(`\`\`\`${ex.language}`);
|
|
1626
|
+
lines.push(`\`\`\`${ex.language || "typescript"}`);
|
|
611
1627
|
lines.push(ex.code.trim());
|
|
612
1628
|
lines.push("```");
|
|
613
1629
|
lines.push("");
|
|
@@ -632,6 +1648,7 @@ function renderFunctionsPage(packageName, symbols, _options) {
|
|
|
632
1648
|
}
|
|
633
1649
|
}
|
|
634
1650
|
}
|
|
1651
|
+
void packageName;
|
|
635
1652
|
return lines.join("\n");
|
|
636
1653
|
}
|
|
637
1654
|
function renderExamplesPage(packageName, symbols, _options) {
|
|
@@ -639,8 +1656,6 @@ function renderExamplesPage(packageName, symbols, _options) {
|
|
|
639
1656
|
(s) => s.exported && s.kind !== "method" && s.kind !== "property"
|
|
640
1657
|
);
|
|
641
1658
|
const lines = [];
|
|
642
|
-
lines.push(`# ${packageName} \u2014 Examples`);
|
|
643
|
-
lines.push("");
|
|
644
1659
|
lines.push("All usage examples from the package, aggregated for quick reference.");
|
|
645
1660
|
lines.push("");
|
|
646
1661
|
let hasExamples = false;
|
|
@@ -655,10 +1670,12 @@ function renderExamplesPage(packageName, symbols, _options) {
|
|
|
655
1670
|
lines.push(`_${s.documentation.summary}_`);
|
|
656
1671
|
lines.push("");
|
|
657
1672
|
}
|
|
658
|
-
lines.push(
|
|
1673
|
+
lines.push(
|
|
1674
|
+
`[View in API reference](${slugLink(`packages/${packageName}/api/functions`)}#${toAnchor2(s.name)})`
|
|
1675
|
+
);
|
|
659
1676
|
lines.push("");
|
|
660
1677
|
for (const ex of examples) {
|
|
661
|
-
lines.push(`\`\`\`${ex.language}`);
|
|
1678
|
+
lines.push(`\`\`\`${ex.language || "typescript"}`);
|
|
662
1679
|
lines.push(ex.code.trim());
|
|
663
1680
|
lines.push("```");
|
|
664
1681
|
lines.push("");
|
|
@@ -670,185 +1687,95 @@ function renderExamplesPage(packageName, symbols, _options) {
|
|
|
670
1687
|
}
|
|
671
1688
|
return lines.join("\n");
|
|
672
1689
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
"class",
|
|
676
|
-
"interface",
|
|
677
|
-
"type",
|
|
678
|
-
"enum",
|
|
679
|
-
"variable"
|
|
680
|
-
];
|
|
681
|
-
var KIND_LABELS2 = {
|
|
682
|
-
function: "Functions",
|
|
683
|
-
class: "Classes",
|
|
684
|
-
interface: "Interfaces",
|
|
685
|
-
type: "Types",
|
|
686
|
-
enum: "Enums",
|
|
687
|
-
variable: "Variables"
|
|
688
|
-
};
|
|
689
|
-
function renderApiSymbol(symbol, depth) {
|
|
690
|
-
const hashes = "#".repeat(depth);
|
|
691
|
-
const ext = symbol.kind === "function" || symbol.kind === "method" ? "()" : "";
|
|
692
|
-
const lines = [];
|
|
693
|
-
lines.push(`${hashes} \`${symbol.name}${ext}\``);
|
|
694
|
-
lines.push("");
|
|
695
|
-
if (symbol.documentation?.deprecated) {
|
|
696
|
-
lines.push(`> **Deprecated**: ${symbol.documentation.deprecated}`);
|
|
697
|
-
lines.push("");
|
|
698
|
-
}
|
|
699
|
-
if (symbol.signature) {
|
|
700
|
-
lines.push("```typescript");
|
|
701
|
-
lines.push(symbol.signature);
|
|
702
|
-
lines.push("```");
|
|
703
|
-
lines.push("");
|
|
704
|
-
}
|
|
705
|
-
if (symbol.documentation?.summary) {
|
|
706
|
-
lines.push(symbol.documentation.summary);
|
|
707
|
-
lines.push("");
|
|
708
|
-
}
|
|
709
|
-
const params = symbol.documentation?.params ?? [];
|
|
710
|
-
if (params.length > 0) {
|
|
711
|
-
lines.push("**Parameters**");
|
|
712
|
-
lines.push("");
|
|
713
|
-
for (const p of params) {
|
|
714
|
-
const typeStr = p.type ? ` (\`${p.type}\`)` : "";
|
|
715
|
-
lines.push(`- \`${p.name}\`${typeStr} \u2014 ${p.description}`);
|
|
716
|
-
}
|
|
717
|
-
lines.push("");
|
|
718
|
-
}
|
|
719
|
-
if (symbol.documentation?.returns) {
|
|
720
|
-
const retType = symbol.documentation.returns.type ? ` (\`${symbol.documentation.returns.type}\`)` : "";
|
|
721
|
-
lines.push(`**Returns**${retType}: ${symbol.documentation.returns.description}`);
|
|
722
|
-
lines.push("");
|
|
723
|
-
}
|
|
724
|
-
const throws = symbol.documentation?.throws ?? [];
|
|
725
|
-
if (throws.length > 0) {
|
|
726
|
-
lines.push("**Throws**");
|
|
727
|
-
lines.push("");
|
|
728
|
-
for (const t of throws) {
|
|
729
|
-
const typeStr = t.type ? `\`${t.type}\` \u2014 ` : "";
|
|
730
|
-
lines.push(`- ${typeStr}${t.description}`);
|
|
731
|
-
}
|
|
732
|
-
lines.push("");
|
|
733
|
-
}
|
|
734
|
-
const examples = symbol.documentation?.examples ?? [];
|
|
735
|
-
if (examples.length > 0) {
|
|
736
|
-
lines.push("**Examples**");
|
|
737
|
-
lines.push("");
|
|
738
|
-
for (const ex of examples) {
|
|
739
|
-
lines.push(`\`\`\`${ex.language}`);
|
|
740
|
-
lines.push(ex.code.trim());
|
|
741
|
-
lines.push("```");
|
|
742
|
-
lines.push("");
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
const children = symbol.children ?? [];
|
|
746
|
-
if (children.length > 0 && depth < 5) {
|
|
747
|
-
for (const child of children) {
|
|
748
|
-
lines.push(renderApiSymbol(child, depth + 1));
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
return lines.join("\n");
|
|
752
|
-
}
|
|
753
|
-
function renderApiReferencePage(packageName, symbols) {
|
|
754
|
-
const exported = symbols.filter(
|
|
755
|
-
(s) => s.exported && s.kind !== "method" && s.kind !== "property"
|
|
756
|
-
);
|
|
757
|
-
const groups = /* @__PURE__ */ new Map();
|
|
758
|
-
for (const s of exported) {
|
|
759
|
-
const list = groups.get(s.kind) ?? [];
|
|
760
|
-
list.push(s);
|
|
761
|
-
groups.set(s.kind, list);
|
|
762
|
-
}
|
|
1690
|
+
function renderConfigurationPage(symbolsByPackage, options) {
|
|
1691
|
+
const configSymbol = [...symbolsByPackage.values()].flat().find((s) => s.exported && TYPE_KINDS.has(s.kind) && /config/i.test(s.name));
|
|
763
1692
|
const lines = [];
|
|
764
|
-
lines.push(
|
|
1693
|
+
lines.push(`Configuration reference for **${options.projectName}**.`);
|
|
765
1694
|
lines.push("");
|
|
766
|
-
|
|
767
|
-
const group = groups.get(kind);
|
|
768
|
-
if (!group || group.length === 0) continue;
|
|
769
|
-
lines.push(`## ${KIND_LABELS2[kind]}`);
|
|
770
|
-
lines.push("");
|
|
771
|
-
for (const s of group) {
|
|
772
|
-
lines.push(renderApiSymbol(s, 3));
|
|
773
|
-
lines.push("");
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
return lines.join("\n");
|
|
777
|
-
}
|
|
778
|
-
function renderProjectIndexPage(symbolsByPackage, options) {
|
|
779
|
-
const lines = [];
|
|
780
|
-
lines.push(`# ${options.projectName}`);
|
|
781
|
-
lines.push("");
|
|
782
|
-
if (options.projectDescription) {
|
|
783
|
-
lines.push(options.projectDescription);
|
|
784
|
-
lines.push("");
|
|
785
|
-
}
|
|
786
|
-
lines.push("## Packages");
|
|
787
|
-
lines.push("");
|
|
788
|
-
for (const [pkgName, symbols] of symbolsByPackage) {
|
|
789
|
-
const exported = symbols.filter(
|
|
790
|
-
(s) => s.exported && s.kind !== "method" && s.kind !== "property"
|
|
791
|
-
);
|
|
792
|
-
const pkgDoc = symbols.map((s) => s.documentation?.tags?.packageDocumentation?.[0]).find(Boolean);
|
|
793
|
-
const summary = pkgDoc ?? `${exported.length} exported symbol(s).`;
|
|
794
|
-
lines.push(`### [${pkgName}](./packages/${pkgName}/index.md)`);
|
|
795
|
-
lines.push("");
|
|
796
|
-
lines.push(summary);
|
|
797
|
-
lines.push("");
|
|
798
|
-
}
|
|
799
|
-
return lines.join("\n");
|
|
800
|
-
}
|
|
801
|
-
function renderGettingStartedPage(symbolsByPackage, options) {
|
|
802
|
-
let firstExample;
|
|
803
|
-
let firstSymbolName = "";
|
|
804
|
-
let firstPackageName = "";
|
|
805
|
-
outer: for (const [pkgName, symbols] of symbolsByPackage) {
|
|
806
|
-
for (const s of symbols) {
|
|
807
|
-
if (!s.exported || s.kind !== "function") continue;
|
|
808
|
-
const ex = s.documentation?.examples?.[0];
|
|
809
|
-
if (ex) {
|
|
810
|
-
firstExample = ex;
|
|
811
|
-
firstSymbolName = s.name;
|
|
812
|
-
firstPackageName = pkgName;
|
|
813
|
-
break outer;
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
const lines = [];
|
|
818
|
-
lines.push("# Getting Started");
|
|
1695
|
+
lines.push("## forge-ts.config.ts");
|
|
819
1696
|
lines.push("");
|
|
820
|
-
lines.push(`
|
|
821
|
-
if (options.projectDescription) {
|
|
822
|
-
lines.push("");
|
|
823
|
-
lines.push(options.projectDescription);
|
|
824
|
-
}
|
|
1697
|
+
lines.push("Create a `forge-ts.config.ts` file in your project root:");
|
|
825
1698
|
lines.push("");
|
|
826
|
-
lines.push("
|
|
1699
|
+
lines.push("```typescript");
|
|
1700
|
+
lines.push('import { defineConfig } from "@forge-ts/core";');
|
|
827
1701
|
lines.push("");
|
|
828
|
-
lines.push("
|
|
829
|
-
lines.push(
|
|
1702
|
+
lines.push("export default defineConfig({");
|
|
1703
|
+
lines.push(' rootDir: ".",');
|
|
1704
|
+
lines.push(' outDir: "docs/generated",');
|
|
1705
|
+
lines.push("});");
|
|
830
1706
|
lines.push("```");
|
|
831
1707
|
lines.push("");
|
|
832
|
-
if (
|
|
833
|
-
lines.push(
|
|
1708
|
+
if (configSymbol) {
|
|
1709
|
+
lines.push(`## \`${configSymbol.name}\``);
|
|
834
1710
|
lines.push("");
|
|
835
|
-
|
|
836
|
-
|
|
1711
|
+
if (configSymbol.documentation?.summary) {
|
|
1712
|
+
lines.push(configSymbol.documentation.summary);
|
|
1713
|
+
lines.push("");
|
|
1714
|
+
}
|
|
1715
|
+
const children = (configSymbol.children ?? []).filter(
|
|
1716
|
+
(c) => c.kind === "property" || c.kind === "method"
|
|
837
1717
|
);
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
1718
|
+
if (children.length > 0) {
|
|
1719
|
+
lines.push("| Property | Type | Required | Description |");
|
|
1720
|
+
lines.push("|----------|------|----------|-------------|");
|
|
1721
|
+
for (const child of children) {
|
|
1722
|
+
const name = `\`${child.name}\``;
|
|
1723
|
+
const type = child.signature ? `\`${escapePipe(child.signature)}\`` : "\u2014";
|
|
1724
|
+
const optional = child.signature?.includes("?") || child.signature?.includes("undefined") ? "No" : "Yes";
|
|
1725
|
+
const description = escapePipe(child.documentation?.summary || child.name);
|
|
1726
|
+
lines.push(`| ${name} | ${type} | ${optional} | ${description} |`);
|
|
1727
|
+
}
|
|
1728
|
+
lines.push("");
|
|
1729
|
+
}
|
|
849
1730
|
}
|
|
850
1731
|
return lines.join("\n");
|
|
851
1732
|
}
|
|
1733
|
+
function renderChangelogPage(options) {
|
|
1734
|
+
const repoUrl = options.repositoryUrl ?? "";
|
|
1735
|
+
const changelogLink = repoUrl ? `See [CHANGELOG.md](${repoUrl}/blob/main/CHANGELOG.md) for the full release history.` : "See your project's `CHANGELOG.md` for the full release history.";
|
|
1736
|
+
return [
|
|
1737
|
+
`Release history for **${options.projectName}**.`,
|
|
1738
|
+
"",
|
|
1739
|
+
"> This is a stub page. Link to or embed your `CHANGELOG.md` here.",
|
|
1740
|
+
"",
|
|
1741
|
+
changelogLink,
|
|
1742
|
+
""
|
|
1743
|
+
].join("\n");
|
|
1744
|
+
}
|
|
1745
|
+
function renderFaqPage(options) {
|
|
1746
|
+
return [
|
|
1747
|
+
`Frequently asked questions about **${options.projectName}**.`,
|
|
1748
|
+
"",
|
|
1749
|
+
"> This is a stub page. Common questions will be added here as they arise.",
|
|
1750
|
+
"",
|
|
1751
|
+
"## How do I configure forge-ts?",
|
|
1752
|
+
"",
|
|
1753
|
+
"Create a `forge-ts.config.ts` file in your project root. See [Configuration](/configuration).",
|
|
1754
|
+
"",
|
|
1755
|
+
"## What TypeScript version is required?",
|
|
1756
|
+
"",
|
|
1757
|
+
"forge-ts requires TypeScript 5.0 or later.",
|
|
1758
|
+
"",
|
|
1759
|
+
"## How do I run @example blocks as tests?",
|
|
1760
|
+
"",
|
|
1761
|
+
"```bash",
|
|
1762
|
+
"npx forge-ts test",
|
|
1763
|
+
"```",
|
|
1764
|
+
""
|
|
1765
|
+
].join("\n");
|
|
1766
|
+
}
|
|
1767
|
+
function renderContributingPage(options) {
|
|
1768
|
+
const repoUrl = options.repositoryUrl ?? "";
|
|
1769
|
+
const contribLink = repoUrl ? `See [CONTRIBUTING.md](${repoUrl}/blob/main/CONTRIBUTING.md) for contribution guidelines.` : "See your project's `CONTRIBUTING.md` for contribution guidelines.";
|
|
1770
|
+
return [
|
|
1771
|
+
`Contributing to **${options.projectName}**.`,
|
|
1772
|
+
"",
|
|
1773
|
+
"> This is a stub page. Link to or embed your `CONTRIBUTING.md` here.",
|
|
1774
|
+
"",
|
|
1775
|
+
contribLink,
|
|
1776
|
+
""
|
|
1777
|
+
].join("\n");
|
|
1778
|
+
}
|
|
852
1779
|
function generateDocSite(symbolsByPackage, config, options) {
|
|
853
1780
|
const ext = options.format === "mdx" ? "mdx" : "md";
|
|
854
1781
|
const pages = [];
|
|
@@ -878,10 +1805,38 @@ function generateDocSite(symbolsByPackage, config, options) {
|
|
|
878
1805
|
`,
|
|
879
1806
|
frontmatter: gettingStartedFrontmatter
|
|
880
1807
|
});
|
|
1808
|
+
const conceptsContent = renderConceptsPage(symbolsByPackage, options);
|
|
1809
|
+
const conceptsFrontmatter = buildFrontmatterFields(
|
|
1810
|
+
"Concepts",
|
|
1811
|
+
`Core concepts behind ${options.projectName}`,
|
|
1812
|
+
options.ssgTarget,
|
|
1813
|
+
3
|
|
1814
|
+
);
|
|
1815
|
+
pages.push({
|
|
1816
|
+
path: `concepts.${ext}`,
|
|
1817
|
+
content: `${serializeFrontmatter(conceptsFrontmatter)}${conceptsContent.trimEnd()}
|
|
1818
|
+
`,
|
|
1819
|
+
frontmatter: conceptsFrontmatter,
|
|
1820
|
+
stub: true
|
|
1821
|
+
});
|
|
1822
|
+
const guidesContent = renderGuidesIndexPage();
|
|
1823
|
+
const guidesFrontmatter = buildFrontmatterFields(
|
|
1824
|
+
"Guides",
|
|
1825
|
+
`How-to guides for ${options.projectName}`,
|
|
1826
|
+
options.ssgTarget,
|
|
1827
|
+
4
|
|
1828
|
+
);
|
|
1829
|
+
pages.push({
|
|
1830
|
+
path: `guides/index.${ext}`,
|
|
1831
|
+
content: `${serializeFrontmatter(guidesFrontmatter)}${guidesContent.trimEnd()}
|
|
1832
|
+
`,
|
|
1833
|
+
frontmatter: guidesFrontmatter,
|
|
1834
|
+
stub: true
|
|
1835
|
+
});
|
|
881
1836
|
let pkgPosition = 1;
|
|
882
1837
|
for (const [pkgName, symbols] of symbolsByPackage) {
|
|
883
1838
|
const pkgBase = `packages/${pkgName}`;
|
|
884
|
-
const overviewContent =
|
|
1839
|
+
const overviewContent = renderPackageOverviewPage(pkgName, symbols, options);
|
|
885
1840
|
const overviewFrontmatter = buildFrontmatterFields(
|
|
886
1841
|
pkgName,
|
|
887
1842
|
`${pkgName} package overview`,
|
|
@@ -894,29 +1849,17 @@ function generateDocSite(symbolsByPackage, config, options) {
|
|
|
894
1849
|
`,
|
|
895
1850
|
frontmatter: overviewFrontmatter
|
|
896
1851
|
});
|
|
897
|
-
const
|
|
898
|
-
const
|
|
1852
|
+
const apiIndexContent = renderApiIndexPage(pkgName, symbols);
|
|
1853
|
+
const apiIndexFrontmatter = buildFrontmatterFields(
|
|
899
1854
|
`${pkgName} \u2014 API Reference`,
|
|
900
1855
|
`Full API reference for the ${pkgName} package`,
|
|
901
1856
|
options.ssgTarget
|
|
902
1857
|
);
|
|
903
1858
|
pages.push({
|
|
904
|
-
path: `${pkgBase}/api
|
|
905
|
-
content: `${serializeFrontmatter(
|
|
906
|
-
`,
|
|
907
|
-
frontmatter: apiFrontmatter
|
|
908
|
-
});
|
|
909
|
-
const typesContent = renderTypesPage(pkgName, symbols, options);
|
|
910
|
-
const typesFrontmatter = buildFrontmatterFields(
|
|
911
|
-
`${pkgName} \u2014 Types`,
|
|
912
|
-
`Type contracts for the ${pkgName} package`,
|
|
913
|
-
options.ssgTarget
|
|
914
|
-
);
|
|
915
|
-
pages.push({
|
|
916
|
-
path: `${pkgBase}/types.${ext}`,
|
|
917
|
-
content: `${serializeFrontmatter(typesFrontmatter)}${typesContent.trimEnd()}
|
|
1859
|
+
path: `${pkgBase}/api/index.${ext}`,
|
|
1860
|
+
content: `${serializeFrontmatter(apiIndexFrontmatter)}${apiIndexContent.trimEnd()}
|
|
918
1861
|
`,
|
|
919
|
-
frontmatter:
|
|
1862
|
+
frontmatter: apiIndexFrontmatter
|
|
920
1863
|
});
|
|
921
1864
|
const functionsContent = renderFunctionsPage(pkgName, symbols, options);
|
|
922
1865
|
const functionsFrontmatter = buildFrontmatterFields(
|
|
@@ -925,11 +1868,23 @@ function generateDocSite(symbolsByPackage, config, options) {
|
|
|
925
1868
|
options.ssgTarget
|
|
926
1869
|
);
|
|
927
1870
|
pages.push({
|
|
928
|
-
path: `${pkgBase}/functions.${ext}`,
|
|
1871
|
+
path: `${pkgBase}/api/functions.${ext}`,
|
|
929
1872
|
content: `${serializeFrontmatter(functionsFrontmatter)}${functionsContent.trimEnd()}
|
|
930
1873
|
`,
|
|
931
1874
|
frontmatter: functionsFrontmatter
|
|
932
1875
|
});
|
|
1876
|
+
const typesContent = renderTypesPage(pkgName, symbols, options);
|
|
1877
|
+
const typesFrontmatter = buildFrontmatterFields(
|
|
1878
|
+
`${pkgName} \u2014 Types`,
|
|
1879
|
+
`Type contracts for the ${pkgName} package`,
|
|
1880
|
+
options.ssgTarget
|
|
1881
|
+
);
|
|
1882
|
+
pages.push({
|
|
1883
|
+
path: `${pkgBase}/api/types.${ext}`,
|
|
1884
|
+
content: `${serializeFrontmatter(typesFrontmatter)}${typesContent.trimEnd()}
|
|
1885
|
+
`,
|
|
1886
|
+
frontmatter: typesFrontmatter
|
|
1887
|
+
});
|
|
933
1888
|
const examplesContent = renderExamplesPage(pkgName, symbols, options);
|
|
934
1889
|
const examplesFrontmatter = buildFrontmatterFields(
|
|
935
1890
|
`${pkgName} \u2014 Examples`,
|
|
@@ -937,26 +1892,604 @@ function generateDocSite(symbolsByPackage, config, options) {
|
|
|
937
1892
|
options.ssgTarget
|
|
938
1893
|
);
|
|
939
1894
|
pages.push({
|
|
940
|
-
path: `${pkgBase}/examples.${ext}`,
|
|
1895
|
+
path: `${pkgBase}/api/examples.${ext}`,
|
|
941
1896
|
content: `${serializeFrontmatter(examplesFrontmatter)}${examplesContent.trimEnd()}
|
|
942
1897
|
`,
|
|
943
1898
|
frontmatter: examplesFrontmatter
|
|
944
1899
|
});
|
|
945
1900
|
pkgPosition++;
|
|
946
1901
|
}
|
|
1902
|
+
const configContent = renderConfigurationPage(symbolsByPackage, options);
|
|
1903
|
+
const configFrontmatter = buildFrontmatterFields(
|
|
1904
|
+
"Configuration",
|
|
1905
|
+
`Configuration reference for ${options.projectName}`,
|
|
1906
|
+
options.ssgTarget
|
|
1907
|
+
);
|
|
1908
|
+
pages.push({
|
|
1909
|
+
path: `configuration.${ext}`,
|
|
1910
|
+
content: `${serializeFrontmatter(configFrontmatter)}${configContent.trimEnd()}
|
|
1911
|
+
`,
|
|
1912
|
+
frontmatter: configFrontmatter
|
|
1913
|
+
});
|
|
1914
|
+
const changelogContent = renderChangelogPage(options);
|
|
1915
|
+
const changelogFrontmatter = buildFrontmatterFields(
|
|
1916
|
+
"Changelog",
|
|
1917
|
+
`Release history for ${options.projectName}`,
|
|
1918
|
+
options.ssgTarget
|
|
1919
|
+
);
|
|
1920
|
+
pages.push({
|
|
1921
|
+
path: `changelog.${ext}`,
|
|
1922
|
+
content: `${serializeFrontmatter(changelogFrontmatter)}${changelogContent.trimEnd()}
|
|
1923
|
+
`,
|
|
1924
|
+
frontmatter: changelogFrontmatter,
|
|
1925
|
+
stub: true
|
|
1926
|
+
});
|
|
1927
|
+
const faqContent = renderFaqPage(options);
|
|
1928
|
+
const faqFrontmatter = buildFrontmatterFields(
|
|
1929
|
+
"FAQ",
|
|
1930
|
+
`Frequently asked questions about ${options.projectName}`,
|
|
1931
|
+
options.ssgTarget
|
|
1932
|
+
);
|
|
1933
|
+
pages.push({
|
|
1934
|
+
path: `faq.${ext}`,
|
|
1935
|
+
content: `${serializeFrontmatter(faqFrontmatter)}${faqContent.trimEnd()}
|
|
1936
|
+
`,
|
|
1937
|
+
frontmatter: faqFrontmatter,
|
|
1938
|
+
stub: true
|
|
1939
|
+
});
|
|
1940
|
+
const contributingContent = renderContributingPage(options);
|
|
1941
|
+
const contributingFrontmatter = buildFrontmatterFields(
|
|
1942
|
+
"Contributing",
|
|
1943
|
+
`How to contribute to ${options.projectName}`,
|
|
1944
|
+
options.ssgTarget
|
|
1945
|
+
);
|
|
1946
|
+
pages.push({
|
|
1947
|
+
path: `contributing.${ext}`,
|
|
1948
|
+
content: `${serializeFrontmatter(contributingFrontmatter)}${contributingContent.trimEnd()}
|
|
1949
|
+
`,
|
|
1950
|
+
frontmatter: contributingFrontmatter,
|
|
1951
|
+
stub: true
|
|
1952
|
+
});
|
|
947
1953
|
void config;
|
|
948
1954
|
return pages;
|
|
949
1955
|
}
|
|
950
1956
|
|
|
1957
|
+
// src/skill.ts
|
|
1958
|
+
function toDirectoryName(name) {
|
|
1959
|
+
return name.replace(/^@[^/]+\//, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
1960
|
+
}
|
|
1961
|
+
function isConceptKind(kind) {
|
|
1962
|
+
return kind === "interface" || kind === "type" || kind === "class" || kind === "enum";
|
|
1963
|
+
}
|
|
1964
|
+
function primaryBinName(config) {
|
|
1965
|
+
const bin = config.project.bin;
|
|
1966
|
+
if (!bin) return void 0;
|
|
1967
|
+
const keys = Object.keys(bin);
|
|
1968
|
+
if (keys.length === 0) return void 0;
|
|
1969
|
+
const pkgShort = config.project.packageName?.replace(/^@[^/]+\//, "");
|
|
1970
|
+
return keys.find((k) => k === pkgShort) ?? keys[0];
|
|
1971
|
+
}
|
|
1972
|
+
function buildDescription(symbols, config) {
|
|
1973
|
+
const exported = symbols.filter((s) => s.exported);
|
|
1974
|
+
const projectName = config.project.packageName ?? config.rootDir.split("/").pop() ?? "this project";
|
|
1975
|
+
const pkgDoc = symbols.find((s) => s.documentation?.tags?.packageDocumentation);
|
|
1976
|
+
const leadSentence = config.project.description ?? pkgDoc?.documentation?.summary ?? exported.find((s) => s.documentation?.summary)?.documentation?.summary;
|
|
1977
|
+
const triggers = [];
|
|
1978
|
+
let n = 1;
|
|
1979
|
+
const cliName = primaryBinName(config);
|
|
1980
|
+
if (cliName) {
|
|
1981
|
+
triggers.push(`(${n++}) running ${cliName} CLI commands`);
|
|
1982
|
+
}
|
|
1983
|
+
const functionCount = exported.filter((s) => s.kind === "function").length;
|
|
1984
|
+
if (functionCount > 0) {
|
|
1985
|
+
triggers.push(`(${n++}) calling its ${functionCount} API functions`);
|
|
1986
|
+
}
|
|
1987
|
+
const hasRoutes = exported.some((s) => s.documentation?.tags?.route !== void 0);
|
|
1988
|
+
if (hasRoutes) {
|
|
1989
|
+
triggers.push(`(${n++}) building HTTP API routes`);
|
|
1990
|
+
}
|
|
1991
|
+
const configSym = exported.find(
|
|
1992
|
+
(s) => (s.kind === "interface" || s.kind === "type") && /config/i.test(s.name) && (s.children?.length ?? 0) > 0
|
|
1993
|
+
);
|
|
1994
|
+
if (configSym) {
|
|
1995
|
+
triggers.push(`(${n++}) configuring ${projectName}`);
|
|
1996
|
+
}
|
|
1997
|
+
const typeCount = exported.filter(
|
|
1998
|
+
(s) => s.kind === "interface" || s.kind === "type" || s.kind === "enum"
|
|
1999
|
+
).length;
|
|
2000
|
+
if (typeCount > 0) {
|
|
2001
|
+
triggers.push(`(${n++}) understanding its ${typeCount} type definitions`);
|
|
2002
|
+
}
|
|
2003
|
+
const classCount = exported.filter((s) => s.kind === "class").length;
|
|
2004
|
+
if (classCount > 0) {
|
|
2005
|
+
triggers.push(`(${n++}) working with its ${classCount} classes`);
|
|
2006
|
+
}
|
|
2007
|
+
const keywords = config.project.keywords ?? [];
|
|
2008
|
+
if (keywords.length > 0) {
|
|
2009
|
+
const kwStr = keywords.slice(0, 5).join('", "');
|
|
2010
|
+
triggers.push(`(${n++}) user mentions "${kwStr}"`);
|
|
2011
|
+
}
|
|
2012
|
+
triggers.push(`(${n}) user mentions "${projectName}" or asks about its API`);
|
|
2013
|
+
let description = "";
|
|
2014
|
+
if (leadSentence) {
|
|
2015
|
+
description += `${leadSentence} `;
|
|
2016
|
+
}
|
|
2017
|
+
description += `Use when: ${triggers.join(", ")}.`;
|
|
2018
|
+
if (description.length > 1024) {
|
|
2019
|
+
description = `${description.slice(0, 1021)}...`;
|
|
2020
|
+
}
|
|
2021
|
+
return description;
|
|
2022
|
+
}
|
|
2023
|
+
function renderQuickStart(symbols, config) {
|
|
2024
|
+
const lines = [];
|
|
2025
|
+
const projectName = config.project.packageName ?? "project";
|
|
2026
|
+
const cliName = primaryBinName(config);
|
|
2027
|
+
lines.push("## Quick Start");
|
|
2028
|
+
lines.push("");
|
|
2029
|
+
lines.push("```bash");
|
|
2030
|
+
lines.push(`npm install ${cliName ? "-D " : ""}${projectName}`);
|
|
2031
|
+
lines.push("```");
|
|
2032
|
+
lines.push("");
|
|
2033
|
+
if (cliName && config.project.scripts) {
|
|
2034
|
+
const scripts = config.project.scripts;
|
|
2035
|
+
const relevantKeys = ["check", "test", "build", "lint", "dev", "start", "generate"];
|
|
2036
|
+
const cliCommands = [];
|
|
2037
|
+
for (const key of relevantKeys) {
|
|
2038
|
+
const script = scripts[key];
|
|
2039
|
+
if (script && script.includes(cliName)) {
|
|
2040
|
+
cliCommands.push(`npx ${cliName} ${key}`);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
if (cliCommands.length === 0) {
|
|
2044
|
+
cliCommands.push(`npx ${cliName} --help`);
|
|
2045
|
+
}
|
|
2046
|
+
if (cliCommands.length > 0) {
|
|
2047
|
+
lines.push("```bash");
|
|
2048
|
+
for (const cmd of cliCommands.slice(0, 5)) {
|
|
2049
|
+
lines.push(cmd);
|
|
2050
|
+
}
|
|
2051
|
+
lines.push("```");
|
|
2052
|
+
lines.push("");
|
|
2053
|
+
}
|
|
2054
|
+
} else {
|
|
2055
|
+
const exported = symbols.filter((s) => s.exported);
|
|
2056
|
+
for (const sym of exported) {
|
|
2057
|
+
if (sym.kind !== "function" && sym.kind !== "class") continue;
|
|
2058
|
+
const ex = sym.documentation?.examples?.[0];
|
|
2059
|
+
if (ex) {
|
|
2060
|
+
lines.push(`\`\`\`${ex.language || "typescript"}`);
|
|
2061
|
+
lines.push(ex.code.trim());
|
|
2062
|
+
lines.push("```");
|
|
2063
|
+
lines.push("");
|
|
2064
|
+
break;
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
return lines;
|
|
2069
|
+
}
|
|
2070
|
+
function renderApiSummaryTable(symbols) {
|
|
2071
|
+
const exported = symbols.filter((s) => s.exported);
|
|
2072
|
+
const functions = exported.filter((s) => s.kind === "function");
|
|
2073
|
+
if (functions.length === 0) return [];
|
|
2074
|
+
const lines = [];
|
|
2075
|
+
lines.push("## API");
|
|
2076
|
+
lines.push("");
|
|
2077
|
+
lines.push("| Function | Description |");
|
|
2078
|
+
lines.push("|----------|-------------|");
|
|
2079
|
+
for (const fn of functions.slice(0, 15)) {
|
|
2080
|
+
const desc = fn.documentation?.summary ?? "";
|
|
2081
|
+
lines.push(`| \`${fn.name}()\` | ${desc} |`);
|
|
2082
|
+
}
|
|
2083
|
+
if (functions.length > 15) {
|
|
2084
|
+
lines.push(`| ... | ${functions.length - 15} more \u2014 see API reference |`);
|
|
2085
|
+
}
|
|
2086
|
+
lines.push("");
|
|
2087
|
+
return lines;
|
|
2088
|
+
}
|
|
2089
|
+
function renderKeyTypes(symbols, max = 10) {
|
|
2090
|
+
const lines = [];
|
|
2091
|
+
let count = 0;
|
|
2092
|
+
for (const sym of symbols) {
|
|
2093
|
+
if (count >= max) break;
|
|
2094
|
+
if (!sym.exported) continue;
|
|
2095
|
+
if (!isConceptKind(sym.kind)) continue;
|
|
2096
|
+
const summary = sym.documentation?.summary ?? "";
|
|
2097
|
+
const label = summary ? `**\`${sym.name}\`** \u2014 ${summary}` : `**\`${sym.name}\`**`;
|
|
2098
|
+
lines.push(`- ${label}`);
|
|
2099
|
+
count++;
|
|
2100
|
+
}
|
|
2101
|
+
return lines;
|
|
2102
|
+
}
|
|
2103
|
+
function renderConfigSection(symbols, config) {
|
|
2104
|
+
const exported = symbols.filter((s) => s.exported);
|
|
2105
|
+
const configSymbol = exported.find(
|
|
2106
|
+
(s) => (s.kind === "interface" || s.kind === "type") && /config/i.test(s.name) && (s.children?.length ?? 0) > 0
|
|
2107
|
+
);
|
|
2108
|
+
if (!configSymbol) return [];
|
|
2109
|
+
const lines = [];
|
|
2110
|
+
lines.push("## Configuration");
|
|
2111
|
+
lines.push("");
|
|
2112
|
+
const children = configSymbol.children ?? [];
|
|
2113
|
+
if (children.length > 0) {
|
|
2114
|
+
const projectName = config.project.packageName ?? "project";
|
|
2115
|
+
const importSource = projectName.includes("/") ? projectName.split("/")[0] + "/" + projectName.split("/")[1] : projectName;
|
|
2116
|
+
lines.push("```typescript");
|
|
2117
|
+
lines.push(`import type { ${configSymbol.name} } from "${importSource}";`);
|
|
2118
|
+
lines.push("");
|
|
2119
|
+
lines.push(`const config: Partial<${configSymbol.name}> = {`);
|
|
2120
|
+
for (const child of children.slice(0, 10)) {
|
|
2121
|
+
const comment = child.documentation?.summary;
|
|
2122
|
+
if (comment) {
|
|
2123
|
+
lines.push(` // ${comment}`);
|
|
2124
|
+
}
|
|
2125
|
+
const type = extractType(child.signature);
|
|
2126
|
+
const defaultVal = inferDefaultValue(type, child.name);
|
|
2127
|
+
lines.push(` ${child.name}: ${defaultVal},`);
|
|
2128
|
+
}
|
|
2129
|
+
if (children.length > 10) {
|
|
2130
|
+
lines.push(` // ... ${children.length - 10} more options`);
|
|
2131
|
+
}
|
|
2132
|
+
lines.push("};");
|
|
2133
|
+
lines.push("```");
|
|
2134
|
+
lines.push("");
|
|
2135
|
+
}
|
|
2136
|
+
lines.push("See [references/CONFIGURATION.md](references/CONFIGURATION.md) for full details.");
|
|
2137
|
+
lines.push("");
|
|
2138
|
+
return lines;
|
|
2139
|
+
}
|
|
2140
|
+
function extractType(signature) {
|
|
2141
|
+
if (!signature) return "";
|
|
2142
|
+
const trimmed = signature.trim();
|
|
2143
|
+
if (/^[{(]/.test(trimmed) || /[|]/.test(trimmed.split(":")[0])) {
|
|
2144
|
+
return trimmed;
|
|
2145
|
+
}
|
|
2146
|
+
const colonIdx = trimmed.indexOf(":");
|
|
2147
|
+
if (colonIdx === -1) return trimmed;
|
|
2148
|
+
const left = trimmed.slice(0, colonIdx).trim();
|
|
2149
|
+
if (/^[a-zA-Z_$][a-zA-Z0-9_$?]*$/.test(left)) {
|
|
2150
|
+
return trimmed.slice(colonIdx + 1).trim();
|
|
2151
|
+
}
|
|
2152
|
+
return trimmed;
|
|
2153
|
+
}
|
|
2154
|
+
function inferDefaultValue(type, name) {
|
|
2155
|
+
const t = type.trim();
|
|
2156
|
+
if (t === "boolean" || t.startsWith("boolean")) return "true";
|
|
2157
|
+
if (t === "string" || t.startsWith("string")) return `"..."`;
|
|
2158
|
+
if (t === "number" || t.startsWith("number")) return "0";
|
|
2159
|
+
if (t.includes("[]") || t.startsWith("Array")) return "[]";
|
|
2160
|
+
if (t.startsWith("{") || /^[A-Z]/.test(t)) return "{ /* ... */ }";
|
|
2161
|
+
if (/dir|path/i.test(name)) return `"."`;
|
|
2162
|
+
return `undefined`;
|
|
2163
|
+
}
|
|
2164
|
+
function renderGotchas(symbols) {
|
|
2165
|
+
const lines = [];
|
|
2166
|
+
const exported = symbols.filter((s) => s.exported);
|
|
2167
|
+
const deprecated = exported.filter((s) => s.documentation?.deprecated);
|
|
2168
|
+
for (const sym of deprecated) {
|
|
2169
|
+
const reason = sym.documentation?.deprecated ?? "";
|
|
2170
|
+
const msg = reason && reason !== "true" ? `: ${reason}` : "";
|
|
2171
|
+
lines.push(`- \`${sym.name}\` is deprecated${msg}`);
|
|
2172
|
+
}
|
|
2173
|
+
const throwers = exported.filter(
|
|
2174
|
+
(s) => s.kind === "function" && (s.documentation?.throws?.length ?? 0) > 0
|
|
2175
|
+
);
|
|
2176
|
+
for (const sym of throwers) {
|
|
2177
|
+
for (const t of sym.documentation?.throws ?? []) {
|
|
2178
|
+
lines.push(
|
|
2179
|
+
`- \`${sym.name}()\` throws${t.type ? ` \`${t.type}\`` : ""}: ${t.description}`
|
|
2180
|
+
);
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
const enums = exported.filter((s) => s.kind === "enum" && (s.children?.length ?? 0) > 0);
|
|
2184
|
+
for (const sym of enums) {
|
|
2185
|
+
const values = (sym.children ?? []).map((c) => c.name).join(", ");
|
|
2186
|
+
lines.push(`- \`${sym.name}\` enum values: ${values}`);
|
|
2187
|
+
}
|
|
2188
|
+
return lines;
|
|
2189
|
+
}
|
|
2190
|
+
function buildSkillMd(symbols, config, directoryName) {
|
|
2191
|
+
const projectName = config.project.packageName ?? config.rootDir.split("/").pop() ?? "Project";
|
|
2192
|
+
const description = buildDescription(symbols, config);
|
|
2193
|
+
const lines = [];
|
|
2194
|
+
lines.push("---");
|
|
2195
|
+
lines.push(`name: ${directoryName}`);
|
|
2196
|
+
lines.push("description: >");
|
|
2197
|
+
for (const segment of description.split("\n")) {
|
|
2198
|
+
lines.push(` ${segment}`);
|
|
2199
|
+
}
|
|
2200
|
+
lines.push("---");
|
|
2201
|
+
lines.push("");
|
|
2202
|
+
lines.push(`# ${projectName}`);
|
|
2203
|
+
lines.push("");
|
|
2204
|
+
const pkgDoc = symbols.find((s) => s.documentation?.tags?.packageDocumentation);
|
|
2205
|
+
const overview = config.project.description ?? pkgDoc?.documentation?.summary ?? symbols.filter((s) => s.exported).find((s) => s.documentation?.summary)?.documentation?.summary;
|
|
2206
|
+
if (overview) {
|
|
2207
|
+
lines.push(overview);
|
|
2208
|
+
lines.push("");
|
|
2209
|
+
}
|
|
2210
|
+
lines.push(...renderQuickStart(symbols, config));
|
|
2211
|
+
lines.push(...renderApiSummaryTable(symbols));
|
|
2212
|
+
lines.push(...renderConfigSection(symbols, config));
|
|
2213
|
+
const customSections = config.skill.customSections ?? [];
|
|
2214
|
+
for (const section of customSections) {
|
|
2215
|
+
lines.push(`## ${section.heading}`);
|
|
2216
|
+
lines.push("");
|
|
2217
|
+
lines.push(section.content);
|
|
2218
|
+
lines.push("");
|
|
2219
|
+
}
|
|
2220
|
+
const gotchaLines = renderGotchas(symbols);
|
|
2221
|
+
const extraGotchas = config.skill.extraGotchas ?? [];
|
|
2222
|
+
for (const gotcha of extraGotchas) {
|
|
2223
|
+
gotchaLines.push(`- ${gotcha}`);
|
|
2224
|
+
}
|
|
2225
|
+
if (gotchaLines.length > 0) {
|
|
2226
|
+
lines.push("## Gotchas");
|
|
2227
|
+
lines.push("");
|
|
2228
|
+
lines.push(...gotchaLines);
|
|
2229
|
+
lines.push("");
|
|
2230
|
+
}
|
|
2231
|
+
const keyTypeLines = renderKeyTypes(symbols);
|
|
2232
|
+
if (keyTypeLines.length > 0) {
|
|
2233
|
+
lines.push("## Key Types");
|
|
2234
|
+
lines.push("");
|
|
2235
|
+
lines.push(...keyTypeLines);
|
|
2236
|
+
lines.push("");
|
|
2237
|
+
}
|
|
2238
|
+
lines.push("## References");
|
|
2239
|
+
lines.push("");
|
|
2240
|
+
lines.push("- [references/CONFIGURATION.md](references/CONFIGURATION.md) \u2014 Full config options");
|
|
2241
|
+
lines.push(
|
|
2242
|
+
"- [references/API-REFERENCE.md](references/API-REFERENCE.md) \u2014 Signatures, parameters, examples"
|
|
2243
|
+
);
|
|
2244
|
+
lines.push("");
|
|
2245
|
+
return `${lines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd()}
|
|
2246
|
+
`;
|
|
2247
|
+
}
|
|
2248
|
+
function buildApiReferenceMd(symbols, config) {
|
|
2249
|
+
const projectName = config.project.packageName ?? config.rootDir.split("/").pop() ?? "Project";
|
|
2250
|
+
const exported = symbols.filter((s) => s.exported);
|
|
2251
|
+
const lines = [];
|
|
2252
|
+
lines.push(`# ${projectName} \u2014 API Reference`);
|
|
2253
|
+
lines.push("");
|
|
2254
|
+
const functions = exported.filter((s) => s.kind === "function");
|
|
2255
|
+
const classes = exported.filter((s) => s.kind === "class");
|
|
2256
|
+
const types = exported.filter(
|
|
2257
|
+
(s) => s.kind === "interface" || s.kind === "type" || s.kind === "enum"
|
|
2258
|
+
);
|
|
2259
|
+
const variables = exported.filter((s) => s.kind === "variable");
|
|
2260
|
+
const tocEntries = [];
|
|
2261
|
+
if (functions.length > 0) tocEntries.push("- [Functions](#functions)");
|
|
2262
|
+
if (types.length > 0) tocEntries.push("- [Types](#types)");
|
|
2263
|
+
if (classes.length > 0) tocEntries.push("- [Classes](#classes)");
|
|
2264
|
+
if (variables.length > 0) tocEntries.push("- [Constants](#constants)");
|
|
2265
|
+
if (tocEntries.length > 0) {
|
|
2266
|
+
lines.push("## Table of Contents");
|
|
2267
|
+
lines.push("");
|
|
2268
|
+
lines.push(...tocEntries);
|
|
2269
|
+
lines.push("");
|
|
2270
|
+
}
|
|
2271
|
+
if (functions.length > 0) {
|
|
2272
|
+
lines.push("## Functions");
|
|
2273
|
+
lines.push("");
|
|
2274
|
+
for (const sym of functions) {
|
|
2275
|
+
lines.push(...renderSymbolDetail(sym));
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
if (types.length > 0) {
|
|
2279
|
+
lines.push("## Types");
|
|
2280
|
+
lines.push("");
|
|
2281
|
+
for (const sym of types) {
|
|
2282
|
+
lines.push(...renderSymbolDetail(sym));
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
if (classes.length > 0) {
|
|
2286
|
+
lines.push("## Classes");
|
|
2287
|
+
lines.push("");
|
|
2288
|
+
for (const sym of classes) {
|
|
2289
|
+
lines.push(...renderSymbolDetail(sym));
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
if (variables.length > 0) {
|
|
2293
|
+
lines.push("## Constants");
|
|
2294
|
+
lines.push("");
|
|
2295
|
+
for (const sym of variables) {
|
|
2296
|
+
lines.push(...renderSymbolDetail(sym));
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
return `${lines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd()}
|
|
2300
|
+
`;
|
|
2301
|
+
}
|
|
2302
|
+
function renderSymbolDetail(sym) {
|
|
2303
|
+
const lines = [];
|
|
2304
|
+
lines.push(`### \`${sym.name}\``);
|
|
2305
|
+
lines.push("");
|
|
2306
|
+
if (sym.documentation?.summary) {
|
|
2307
|
+
lines.push(sym.documentation.summary);
|
|
2308
|
+
lines.push("");
|
|
2309
|
+
}
|
|
2310
|
+
if (sym.signature) {
|
|
2311
|
+
lines.push("```typescript");
|
|
2312
|
+
lines.push(sym.signature);
|
|
2313
|
+
lines.push("```");
|
|
2314
|
+
lines.push("");
|
|
2315
|
+
}
|
|
2316
|
+
if (sym.documentation?.params && sym.documentation.params.length > 0) {
|
|
2317
|
+
lines.push("**Parameters:**");
|
|
2318
|
+
lines.push("");
|
|
2319
|
+
for (const p of sym.documentation.params) {
|
|
2320
|
+
lines.push(`- \`${p.name}\`${p.type ? ` (\`${p.type}\`)` : ""} \u2014 ${p.description}`);
|
|
2321
|
+
}
|
|
2322
|
+
lines.push("");
|
|
2323
|
+
}
|
|
2324
|
+
if (sym.documentation?.returns) {
|
|
2325
|
+
lines.push(`**Returns:** ${sym.documentation.returns.description}`);
|
|
2326
|
+
lines.push("");
|
|
2327
|
+
}
|
|
2328
|
+
if (sym.children && sym.children.length > 0) {
|
|
2329
|
+
lines.push("**Members:**");
|
|
2330
|
+
lines.push("");
|
|
2331
|
+
for (const child of sym.children) {
|
|
2332
|
+
const desc = child.documentation?.summary ?? "";
|
|
2333
|
+
lines.push(`- \`${child.name}\`${desc ? ` \u2014 ${desc}` : ""}`);
|
|
2334
|
+
}
|
|
2335
|
+
lines.push("");
|
|
2336
|
+
}
|
|
2337
|
+
for (const ex of sym.documentation?.examples ?? []) {
|
|
2338
|
+
const lang = ex.language || "typescript";
|
|
2339
|
+
lines.push(`\`\`\`${lang}`);
|
|
2340
|
+
lines.push(ex.code.trim());
|
|
2341
|
+
lines.push("```");
|
|
2342
|
+
lines.push("");
|
|
2343
|
+
}
|
|
2344
|
+
return lines;
|
|
2345
|
+
}
|
|
2346
|
+
function buildConfigurationMd(symbols, config) {
|
|
2347
|
+
const projectName = config.project.packageName ?? config.rootDir.split("/").pop() ?? "Project";
|
|
2348
|
+
const exported = symbols.filter((s) => s.exported);
|
|
2349
|
+
const lines = [];
|
|
2350
|
+
lines.push(`# ${projectName} \u2014 Configuration Reference`);
|
|
2351
|
+
lines.push("");
|
|
2352
|
+
const configSymbols = exported.filter(
|
|
2353
|
+
(s) => (s.kind === "interface" || s.kind === "type") && /config/i.test(s.name) && (s.children?.length ?? 0) > 0
|
|
2354
|
+
);
|
|
2355
|
+
for (const configSym of configSymbols) {
|
|
2356
|
+
lines.push(`## \`${configSym.name}\``);
|
|
2357
|
+
lines.push("");
|
|
2358
|
+
if (configSym.documentation?.summary) {
|
|
2359
|
+
lines.push(configSym.documentation.summary);
|
|
2360
|
+
lines.push("");
|
|
2361
|
+
}
|
|
2362
|
+
const children = configSym.children ?? [];
|
|
2363
|
+
if (children.length > 0) {
|
|
2364
|
+
const importSource = projectName.includes("/") ? projectName : projectName;
|
|
2365
|
+
lines.push("```typescript");
|
|
2366
|
+
lines.push(`import type { ${configSym.name} } from "${importSource}";`);
|
|
2367
|
+
lines.push("");
|
|
2368
|
+
lines.push(`const config: Partial<${configSym.name}> = {`);
|
|
2369
|
+
for (const child of children) {
|
|
2370
|
+
if (child.documentation?.summary) {
|
|
2371
|
+
lines.push(` // ${child.documentation.summary}`);
|
|
2372
|
+
}
|
|
2373
|
+
const type = extractType(child.signature);
|
|
2374
|
+
const defaultVal = inferDefaultValue(type, child.name);
|
|
2375
|
+
lines.push(` ${child.name}: ${defaultVal},`);
|
|
2376
|
+
}
|
|
2377
|
+
lines.push("};");
|
|
2378
|
+
lines.push("```");
|
|
2379
|
+
lines.push("");
|
|
2380
|
+
}
|
|
2381
|
+
lines.push("| Property | Type | Description |");
|
|
2382
|
+
lines.push("|----------|------|-------------|");
|
|
2383
|
+
for (const child of children) {
|
|
2384
|
+
const type = extractType(child.signature);
|
|
2385
|
+
const desc = child.documentation?.summary ?? "";
|
|
2386
|
+
lines.push(`| \`${child.name}\` | \`${type}\` | ${desc} |`);
|
|
2387
|
+
}
|
|
2388
|
+
lines.push("");
|
|
2389
|
+
}
|
|
2390
|
+
if (configSymbols.length === 0) {
|
|
2391
|
+
lines.push("No configuration types detected in this project.");
|
|
2392
|
+
lines.push("");
|
|
2393
|
+
}
|
|
2394
|
+
return `${lines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd()}
|
|
2395
|
+
`;
|
|
2396
|
+
}
|
|
2397
|
+
function buildScripts(config) {
|
|
2398
|
+
const scripts = [];
|
|
2399
|
+
const cliName = primaryBinName(config);
|
|
2400
|
+
const pkgScripts = config.project.scripts ?? {};
|
|
2401
|
+
if (cliName) {
|
|
2402
|
+
if (pkgScripts.build) {
|
|
2403
|
+
scripts.push({
|
|
2404
|
+
path: "scripts/build.sh",
|
|
2405
|
+
content: [
|
|
2406
|
+
"#!/usr/bin/env bash",
|
|
2407
|
+
`# Run ${cliName} build pipeline`,
|
|
2408
|
+
"set -euo pipefail",
|
|
2409
|
+
`npx ${cliName} build "$@"`,
|
|
2410
|
+
""
|
|
2411
|
+
].join("\n")
|
|
2412
|
+
});
|
|
2413
|
+
}
|
|
2414
|
+
const checkCmd = pkgScripts.check ?? pkgScripts.lint;
|
|
2415
|
+
if (checkCmd) {
|
|
2416
|
+
const subcommand = checkCmd.includes("check") ? "check" : "lint";
|
|
2417
|
+
scripts.push({
|
|
2418
|
+
path: "scripts/check.sh",
|
|
2419
|
+
content: [
|
|
2420
|
+
"#!/usr/bin/env bash",
|
|
2421
|
+
`# Run ${cliName} ${subcommand}`,
|
|
2422
|
+
"set -euo pipefail",
|
|
2423
|
+
`npx ${cliName} ${subcommand} "$@"`,
|
|
2424
|
+
""
|
|
2425
|
+
].join("\n")
|
|
2426
|
+
});
|
|
2427
|
+
}
|
|
2428
|
+
if (pkgScripts.test) {
|
|
2429
|
+
scripts.push({
|
|
2430
|
+
path: "scripts/test.sh",
|
|
2431
|
+
content: [
|
|
2432
|
+
"#!/usr/bin/env bash",
|
|
2433
|
+
`# Run ${cliName} test suite`,
|
|
2434
|
+
"set -euo pipefail",
|
|
2435
|
+
`npx ${cliName} test "$@"`,
|
|
2436
|
+
""
|
|
2437
|
+
].join("\n")
|
|
2438
|
+
});
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
if (scripts.length === 0) {
|
|
2442
|
+
scripts.push({
|
|
2443
|
+
path: "scripts/test.sh",
|
|
2444
|
+
content: [
|
|
2445
|
+
"#!/usr/bin/env bash",
|
|
2446
|
+
"# Run the project's test suite",
|
|
2447
|
+
"# Usage: ./scripts/test.sh [additional args]",
|
|
2448
|
+
"",
|
|
2449
|
+
"if [ -f package.json ]; then",
|
|
2450
|
+
' npm test "$@"',
|
|
2451
|
+
"else",
|
|
2452
|
+
' echo "No package.json found"',
|
|
2453
|
+
" exit 1",
|
|
2454
|
+
"fi",
|
|
2455
|
+
""
|
|
2456
|
+
].join("\n")
|
|
2457
|
+
});
|
|
2458
|
+
}
|
|
2459
|
+
return scripts;
|
|
2460
|
+
}
|
|
2461
|
+
function generateSkillPackage(symbols, config) {
|
|
2462
|
+
const projectName = config.project.packageName ?? config.rootDir.split("/").pop() ?? "Project";
|
|
2463
|
+
const directoryName = toDirectoryName(projectName);
|
|
2464
|
+
const skillMd = buildSkillMd(symbols, config, directoryName);
|
|
2465
|
+
const apiRefMd = buildApiReferenceMd(symbols, config);
|
|
2466
|
+
const configMd = buildConfigurationMd(symbols, config);
|
|
2467
|
+
const scripts = buildScripts(config);
|
|
2468
|
+
return {
|
|
2469
|
+
directoryName,
|
|
2470
|
+
files: [
|
|
2471
|
+
{ path: "SKILL.md", content: skillMd },
|
|
2472
|
+
{ path: "references/API-REFERENCE.md", content: apiRefMd },
|
|
2473
|
+
{ path: "references/CONFIGURATION.md", content: configMd },
|
|
2474
|
+
...scripts
|
|
2475
|
+
]
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
function generateSkillMd(symbols, config) {
|
|
2479
|
+
const projectName = config.project.packageName ?? config.rootDir.split("/").pop() ?? "Project";
|
|
2480
|
+
const directoryName = toDirectoryName(projectName);
|
|
2481
|
+
return buildSkillMd(symbols, config, directoryName);
|
|
2482
|
+
}
|
|
2483
|
+
|
|
951
2484
|
// src/ssg-config.ts
|
|
952
|
-
function
|
|
2485
|
+
function pageSlug5(pagePath) {
|
|
953
2486
|
return pagePath.replace(/\.[^.]+$/, "");
|
|
954
2487
|
}
|
|
955
2488
|
function partitionPages(pages) {
|
|
956
2489
|
const topLevel = [];
|
|
957
2490
|
const byPackage = /* @__PURE__ */ new Map();
|
|
958
2491
|
for (const page of pages) {
|
|
959
|
-
const slug =
|
|
2492
|
+
const slug = pageSlug5(page.path);
|
|
960
2493
|
const packageMatch = /^packages\/([^/]+)\/(.+)$/.exec(slug);
|
|
961
2494
|
if (packageMatch) {
|
|
962
2495
|
const pkgName = packageMatch[1];
|
|
@@ -969,7 +2502,7 @@ function partitionPages(pages) {
|
|
|
969
2502
|
}
|
|
970
2503
|
return { topLevel, byPackage };
|
|
971
2504
|
}
|
|
972
|
-
function
|
|
2505
|
+
function slugToLabel3(slug) {
|
|
973
2506
|
return slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
974
2507
|
}
|
|
975
2508
|
function generateMintlifyConfig(pages, projectName) {
|
|
@@ -1032,7 +2565,7 @@ function generateNextraConfigs(pages, _projectName) {
|
|
|
1032
2565
|
const rootMeta = {};
|
|
1033
2566
|
for (const slug of topLevel) {
|
|
1034
2567
|
const segment = slug.split("/").pop() ?? slug;
|
|
1035
|
-
rootMeta[segment] =
|
|
2568
|
+
rootMeta[segment] = slugToLabel3(segment);
|
|
1036
2569
|
}
|
|
1037
2570
|
if (byPackage.size > 0) {
|
|
1038
2571
|
rootMeta.packages = "Packages";
|
|
@@ -1046,7 +2579,7 @@ function generateNextraConfigs(pages, _projectName) {
|
|
|
1046
2579
|
const pkgMeta = {};
|
|
1047
2580
|
for (const slug of slugs) {
|
|
1048
2581
|
const segment = slug.split("/").pop() ?? slug;
|
|
1049
|
-
pkgMeta[segment] =
|
|
2582
|
+
pkgMeta[segment] = slugToLabel3(segment);
|
|
1050
2583
|
}
|
|
1051
2584
|
files.push({
|
|
1052
2585
|
path: `packages/${pkgName}/_meta.json`,
|
|
@@ -1074,7 +2607,7 @@ function generateVitePressConfig(pages, _projectName) {
|
|
|
1074
2607
|
const items = topLevel.map((slug) => {
|
|
1075
2608
|
const segment = slug.split("/").pop() ?? slug;
|
|
1076
2609
|
const link = slug === "index" ? "/" : `/${slug}`;
|
|
1077
|
-
return { text:
|
|
2610
|
+
return { text: slugToLabel3(segment), link };
|
|
1078
2611
|
});
|
|
1079
2612
|
sidebar.push({ text: "Getting Started", items });
|
|
1080
2613
|
}
|
|
@@ -1083,7 +2616,7 @@ function generateVitePressConfig(pages, _projectName) {
|
|
|
1083
2616
|
const segment = slug.split("/").pop() ?? slug;
|
|
1084
2617
|
const isIndex = segment === "index";
|
|
1085
2618
|
const link = isIndex ? `/packages/${pkgName}/` : `/${slug}`;
|
|
1086
|
-
return { text:
|
|
2619
|
+
return { text: slugToLabel3(segment), link };
|
|
1087
2620
|
});
|
|
1088
2621
|
sidebar.push({ text: pkgName, items });
|
|
1089
2622
|
}
|
|
@@ -1112,11 +2645,34 @@ function generateSSGConfigs(pages, target, projectName) {
|
|
|
1112
2645
|
}
|
|
1113
2646
|
|
|
1114
2647
|
// src/index.ts
|
|
1115
|
-
import {
|
|
1116
|
-
import {
|
|
2648
|
+
import { existsSync as existsSync2 } from "fs";
|
|
2649
|
+
import { mkdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
2650
|
+
import { dirname, join } from "path";
|
|
1117
2651
|
import { createWalker } from "@forge-ts/core";
|
|
1118
|
-
|
|
2652
|
+
function updateAutoSections(existing, generated) {
|
|
2653
|
+
const markerPattern = /<!-- FORGE:AUTO-START (\S+) -->([\s\S]*?)<!-- FORGE:AUTO-END \1 -->/g;
|
|
2654
|
+
const newSections = /* @__PURE__ */ new Map();
|
|
2655
|
+
let match;
|
|
2656
|
+
while ((match = markerPattern.exec(generated)) !== null) {
|
|
2657
|
+
newSections.set(match[1], match[0]);
|
|
2658
|
+
}
|
|
2659
|
+
if (newSections.size === 0) return null;
|
|
2660
|
+
let updated = existing;
|
|
2661
|
+
let changed = false;
|
|
2662
|
+
for (const [id, replacement] of newSections) {
|
|
2663
|
+
const sectionPattern = new RegExp(
|
|
2664
|
+
`<!-- FORGE:AUTO-START ${id} -->[\\s\\S]*?<!-- FORGE:AUTO-END ${id} -->`
|
|
2665
|
+
);
|
|
2666
|
+
if (sectionPattern.test(updated)) {
|
|
2667
|
+
updated = updated.replace(sectionPattern, replacement);
|
|
2668
|
+
changed = true;
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
return changed ? updated : null;
|
|
2672
|
+
}
|
|
2673
|
+
async function generate(config, options) {
|
|
1119
2674
|
const start = Date.now();
|
|
2675
|
+
const forceStubs = options?.forceStubs ?? false;
|
|
1120
2676
|
const walker = createWalker(config);
|
|
1121
2677
|
const symbols = walker.walk();
|
|
1122
2678
|
await mkdir(config.outDir, { recursive: true });
|
|
@@ -1127,27 +2683,46 @@ async function generate(config) {
|
|
|
1127
2683
|
const ext = format === "mdx" ? "mdx" : "md";
|
|
1128
2684
|
await writeFile2(join(config.outDir, `api-reference.${ext}`), content, "utf8");
|
|
1129
2685
|
}
|
|
1130
|
-
const
|
|
2686
|
+
const resolvedRoot = config.rootDir === "." ? process.cwd() : config.rootDir;
|
|
2687
|
+
const projectName = resolvedRoot.split("/").pop() ?? "Project";
|
|
1131
2688
|
const symbolsByPackage = groupSymbolsByPackage(symbols, config.rootDir);
|
|
2689
|
+
const target = config.gen.ssgTarget ?? DEFAULT_TARGET;
|
|
2690
|
+
const adapter = getAdapter(target);
|
|
1132
2691
|
for (const format of config.gen.formats) {
|
|
1133
2692
|
const pages = generateDocSite(symbolsByPackage, config, {
|
|
1134
2693
|
format,
|
|
1135
2694
|
ssgTarget: config.gen.ssgTarget,
|
|
1136
|
-
projectName
|
|
2695
|
+
projectName,
|
|
2696
|
+
repositoryUrl: config.project.repository,
|
|
2697
|
+
packageName: config.project.packageName
|
|
1137
2698
|
});
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
2699
|
+
const adapterContext = {
|
|
2700
|
+
config,
|
|
2701
|
+
projectName,
|
|
2702
|
+
pages,
|
|
2703
|
+
symbols,
|
|
2704
|
+
outDir: config.outDir
|
|
2705
|
+
};
|
|
2706
|
+
const transformedPages = adapter.transformPages(pages, adapterContext);
|
|
2707
|
+
for (const file of transformedPages) {
|
|
2708
|
+
const filePath = join(config.outDir, file.path);
|
|
2709
|
+
if (file.stub && existsSync2(filePath) && !forceStubs) {
|
|
2710
|
+
const existingContent = await readFile2(filePath, "utf8");
|
|
2711
|
+
const merged = updateAutoSections(existingContent, file.content);
|
|
2712
|
+
if (merged) {
|
|
2713
|
+
await writeFile2(filePath, merged, "utf8");
|
|
2714
|
+
}
|
|
2715
|
+
continue;
|
|
2716
|
+
}
|
|
2717
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
2718
|
+
await writeFile2(filePath, file.content, "utf8");
|
|
1143
2719
|
}
|
|
1144
2720
|
if (config.gen.ssgTarget) {
|
|
1145
|
-
const configFiles =
|
|
1146
|
-
for (const
|
|
1147
|
-
const
|
|
1148
|
-
|
|
1149
|
-
await
|
|
1150
|
-
await writeFile2(configPath, configFile.content, "utf8");
|
|
2721
|
+
const configFiles = adapter.generateConfig(adapterContext);
|
|
2722
|
+
for (const file of configFiles) {
|
|
2723
|
+
const filePath = join(config.outDir, file.path);
|
|
2724
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
2725
|
+
await writeFile2(filePath, file.content, "utf8");
|
|
1151
2726
|
}
|
|
1152
2727
|
}
|
|
1153
2728
|
}
|
|
@@ -1156,6 +2731,17 @@ async function generate(config) {
|
|
|
1156
2731
|
await writeFile2(join(config.outDir, "llms.txt"), llms, "utf8");
|
|
1157
2732
|
const llmsFull = generateLlmsFullTxt(symbols, config);
|
|
1158
2733
|
await writeFile2(join(config.outDir, "llms-full.txt"), llmsFull, "utf8");
|
|
2734
|
+
const skillEnabled = config.skill.enabled !== false;
|
|
2735
|
+
if (skillEnabled) {
|
|
2736
|
+
const skillPkg = generateSkillPackage(symbols, config);
|
|
2737
|
+
const skillDir = join(config.outDir, skillPkg.directoryName);
|
|
2738
|
+
await mkdir(skillDir, { recursive: true });
|
|
2739
|
+
for (const file of skillPkg.files) {
|
|
2740
|
+
const filePath = join(skillDir, file.path);
|
|
2741
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
2742
|
+
await writeFile2(filePath, file.content, "utf8");
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
1159
2745
|
}
|
|
1160
2746
|
if (config.gen.readmeSync) {
|
|
1161
2747
|
await syncReadme(join(config.rootDir, "README.md"), symbols);
|
|
@@ -1169,13 +2755,19 @@ async function generate(config) {
|
|
|
1169
2755
|
};
|
|
1170
2756
|
}
|
|
1171
2757
|
export {
|
|
2758
|
+
DEFAULT_TARGET,
|
|
1172
2759
|
generate,
|
|
1173
2760
|
generateDocSite,
|
|
1174
2761
|
generateLlmsFullTxt,
|
|
1175
2762
|
generateLlmsTxt,
|
|
1176
2763
|
generateMarkdown,
|
|
1177
2764
|
generateSSGConfigs,
|
|
2765
|
+
generateSkillMd,
|
|
2766
|
+
generateSkillPackage,
|
|
2767
|
+
getAdapter,
|
|
2768
|
+
getAvailableTargets,
|
|
1178
2769
|
groupSymbolsByPackage,
|
|
2770
|
+
registerAdapter,
|
|
1179
2771
|
syncReadme
|
|
1180
2772
|
};
|
|
1181
2773
|
//# sourceMappingURL=index.js.map
|