@forge-ts/gen 0.5.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 +133 -4
- package/dist/index.js +1165 -256
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -30,7 +30,6 @@ function pageSlug(pagePath) {
|
|
|
30
30
|
}
|
|
31
31
|
function buildDocsJson(context) {
|
|
32
32
|
const { pages, projectName } = context;
|
|
33
|
-
const topLevel = [];
|
|
34
33
|
const byPackage = /* @__PURE__ */ new Map();
|
|
35
34
|
for (const page of pages) {
|
|
36
35
|
const slug = pageSlug(page.path);
|
|
@@ -40,29 +39,56 @@ function buildDocsJson(context) {
|
|
|
40
39
|
const list = byPackage.get(pkgName) ?? [];
|
|
41
40
|
list.push(slug);
|
|
42
41
|
byPackage.set(pkgName, list);
|
|
43
|
-
} else {
|
|
44
|
-
topLevel.push(slug);
|
|
45
42
|
}
|
|
46
43
|
}
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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 });
|
|
51
63
|
if (byPackage.size > 0) {
|
|
52
|
-
const
|
|
64
|
+
const apiGroups = [];
|
|
53
65
|
for (const [pkgName, slugs] of byPackage) {
|
|
54
|
-
|
|
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 });
|
|
55
78
|
}
|
|
56
|
-
|
|
57
|
-
group: "Packages",
|
|
58
|
-
pages: packageGroups
|
|
59
|
-
});
|
|
79
|
+
tabs.push({ tab: "API Reference", groups: apiGroups });
|
|
60
80
|
}
|
|
61
81
|
return {
|
|
62
82
|
name: projectName,
|
|
63
|
-
theme: "
|
|
64
|
-
colors: { primary: "#0ea5e9" },
|
|
65
|
-
navigation
|
|
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
|
+
}
|
|
66
92
|
};
|
|
67
93
|
}
|
|
68
94
|
function addMintlifyFrontmatter(page) {
|
|
@@ -89,17 +115,39 @@ var mintlifyAdapter = {
|
|
|
89
115
|
devDependencies: {},
|
|
90
116
|
scripts: {},
|
|
91
117
|
instructions: [
|
|
92
|
-
"
|
|
93
|
-
"Preview locally: mint dev",
|
|
118
|
+
"Preview locally: npx @mintlify/cli dev",
|
|
94
119
|
"Deploy: Push to GitHub and connect at mintlify.com"
|
|
95
120
|
]
|
|
96
121
|
};
|
|
97
122
|
},
|
|
98
123
|
transformPages(pages, _context) {
|
|
99
|
-
return pages.map((page) =>
|
|
100
|
-
|
|
101
|
-
content
|
|
102
|
-
|
|
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
|
+
});
|
|
103
151
|
},
|
|
104
152
|
generateConfig(context) {
|
|
105
153
|
const config = buildDocsJson(context);
|
|
@@ -111,10 +159,19 @@ var mintlifyAdapter = {
|
|
|
111
159
|
}
|
|
112
160
|
];
|
|
113
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
|
+
},
|
|
114
171
|
async detectExisting(outDir) {
|
|
115
|
-
const { existsSync:
|
|
172
|
+
const { existsSync: existsSync3 } = await import("fs");
|
|
116
173
|
const { join: join2 } = await import("path");
|
|
117
|
-
return
|
|
174
|
+
return existsSync3(join2(outDir, "docs.json"));
|
|
118
175
|
}
|
|
119
176
|
};
|
|
120
177
|
registerAdapter(mintlifyAdapter);
|
|
@@ -282,7 +339,8 @@ var docusaurusAdapter = {
|
|
|
282
339
|
transformPages(pages, _context) {
|
|
283
340
|
return pages.map((page) => ({
|
|
284
341
|
path: page.path.replace(/\.md$/, ".mdx"),
|
|
285
|
-
content: addDocusaurusFrontmatter(page)
|
|
342
|
+
content: addDocusaurusFrontmatter(page),
|
|
343
|
+
stub: page.stub
|
|
286
344
|
}));
|
|
287
345
|
},
|
|
288
346
|
generateConfig(context) {
|
|
@@ -293,10 +351,19 @@ var docusaurusAdapter = {
|
|
|
293
351
|
}
|
|
294
352
|
];
|
|
295
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
|
+
},
|
|
296
363
|
async detectExisting(outDir) {
|
|
297
|
-
const { existsSync:
|
|
364
|
+
const { existsSync: existsSync3 } = await import("fs");
|
|
298
365
|
const { join: join2 } = await import("path");
|
|
299
|
-
return
|
|
366
|
+
return existsSync3(join2(outDir, "sidebars.ts")) || existsSync3(join2(outDir, "docusaurus.config.ts")) || existsSync3(join2(outDir, "docusaurus.config.js"));
|
|
300
367
|
}
|
|
301
368
|
};
|
|
302
369
|
registerAdapter(docusaurusAdapter);
|
|
@@ -510,16 +577,26 @@ var nextraAdapter = {
|
|
|
510
577
|
transformPages(pages, _context) {
|
|
511
578
|
return pages.map((page) => ({
|
|
512
579
|
path: page.path.replace(/\.md$/, ".mdx"),
|
|
513
|
-
content: addNextraFrontmatter(page)
|
|
580
|
+
content: addNextraFrontmatter(page),
|
|
581
|
+
stub: page.stub
|
|
514
582
|
}));
|
|
515
583
|
},
|
|
516
584
|
generateConfig(context) {
|
|
517
585
|
return buildMetaFiles(context.pages);
|
|
518
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
|
+
},
|
|
519
596
|
async detectExisting(outDir) {
|
|
520
|
-
const { existsSync:
|
|
597
|
+
const { existsSync: existsSync3 } = await import("fs");
|
|
521
598
|
const { join: join2 } = await import("path");
|
|
522
|
-
return
|
|
599
|
+
return existsSync3(join2(outDir, "content/_meta.js")) || existsSync3(join2(outDir, "next.config.ts")) || existsSync3(join2(outDir, "next.config.js"));
|
|
523
600
|
}
|
|
524
601
|
};
|
|
525
602
|
registerAdapter(nextraAdapter);
|
|
@@ -650,7 +727,8 @@ var vitepressAdapter = {
|
|
|
650
727
|
transformPages(pages, _context) {
|
|
651
728
|
return pages.map((page) => ({
|
|
652
729
|
path: page.path.endsWith(".mdx") ? page.path.replace(/\.mdx$/, ".md") : page.path,
|
|
653
|
-
content: addVitepressFrontmatter(page)
|
|
730
|
+
content: addVitepressFrontmatter(page),
|
|
731
|
+
stub: page.stub
|
|
654
732
|
}));
|
|
655
733
|
},
|
|
656
734
|
generateConfig(context) {
|
|
@@ -661,10 +739,19 @@ var vitepressAdapter = {
|
|
|
661
739
|
}
|
|
662
740
|
];
|
|
663
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
|
+
},
|
|
664
751
|
async detectExisting(outDir) {
|
|
665
|
-
const { existsSync:
|
|
752
|
+
const { existsSync: existsSync3 } = await import("fs");
|
|
666
753
|
const { join: join2 } = await import("path");
|
|
667
|
-
return
|
|
754
|
+
return existsSync3(join2(outDir, ".vitepress"));
|
|
668
755
|
}
|
|
669
756
|
};
|
|
670
757
|
registerAdapter(vitepressAdapter);
|
|
@@ -984,7 +1071,7 @@ function generateMarkdown(symbols, config, options = {}) {
|
|
|
984
1071
|
}
|
|
985
1072
|
parts.push("# API Reference\n");
|
|
986
1073
|
parts.push(
|
|
987
|
-
`Generated by [forge-ts](https://github.com/
|
|
1074
|
+
`Generated by [forge-ts](https://github.com/kryptobaseddev/forge-ts) from \`${config.rootDir}\`.
|
|
988
1075
|
`
|
|
989
1076
|
);
|
|
990
1077
|
if (topLevel.length > 0) {
|
|
@@ -1133,6 +1220,15 @@ function buildFrontmatterFields(title, description, ssgTarget, sidebarPosition)
|
|
|
1133
1220
|
return {};
|
|
1134
1221
|
}
|
|
1135
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
|
+
}
|
|
1136
1232
|
function groupSymbolsByPackage(symbols, rootDir) {
|
|
1137
1233
|
const result = /* @__PURE__ */ new Map();
|
|
1138
1234
|
for (const symbol of symbols) {
|
|
@@ -1147,45 +1243,285 @@ function groupSymbolsByPackage(symbols, rootDir) {
|
|
|
1147
1243
|
}
|
|
1148
1244
|
var TYPE_KINDS = /* @__PURE__ */ new Set(["interface", "type", "enum"]);
|
|
1149
1245
|
var FUNCTION_KINDS = /* @__PURE__ */ new Set(["function", "class"]);
|
|
1150
|
-
function
|
|
1151
|
-
const
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1246
|
+
function renderProjectIndexPage(symbolsByPackage, options) {
|
|
1247
|
+
const lines = [];
|
|
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
|
+
);
|
|
1254
|
+
}
|
|
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");
|
|
1263
|
+
}
|
|
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");
|
|
1267
|
+
lines.push("");
|
|
1268
|
+
lines.push("## Installation");
|
|
1269
|
+
lines.push("");
|
|
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) {
|
|
1158
1451
|
const exported = symbols.filter(
|
|
1159
1452
|
(s) => s.exported && s.kind !== "method" && s.kind !== "property"
|
|
1160
1453
|
);
|
|
1161
|
-
const
|
|
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));
|
|
1162
1457
|
const lines = [];
|
|
1163
|
-
|
|
1164
|
-
lines.push("");
|
|
1458
|
+
const pkgDoc = symbols.map((s) => s.documentation?.tags?.packageDocumentation?.[0]).find(Boolean);
|
|
1165
1459
|
if (pkgDoc) {
|
|
1166
1460
|
lines.push(pkgDoc);
|
|
1167
1461
|
lines.push("");
|
|
1462
|
+
} else {
|
|
1463
|
+
lines.push(`API reference for the \`${pkgName}\` package.`);
|
|
1464
|
+
lines.push("");
|
|
1168
1465
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1466
|
+
const renderGroup = (group, heading, pathSuffix) => {
|
|
1467
|
+
if (group.length === 0) return;
|
|
1468
|
+
lines.push(`## ${heading}`);
|
|
1171
1469
|
lines.push("");
|
|
1172
1470
|
lines.push("| Symbol | Kind | Description |");
|
|
1173
1471
|
lines.push("|--------|------|-------------|");
|
|
1174
|
-
for (const s of
|
|
1472
|
+
for (const s of group) {
|
|
1175
1473
|
const ext = s.kind === "function" ? "()" : "";
|
|
1176
|
-
const
|
|
1177
|
-
const
|
|
1178
|
-
|
|
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} |`);
|
|
1179
1479
|
}
|
|
1180
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");
|
|
1181
1519
|
}
|
|
1182
1520
|
return lines.join("\n");
|
|
1183
1521
|
}
|
|
1184
1522
|
function renderTypesPage(packageName, symbols, _options) {
|
|
1185
1523
|
const typeSymbols = symbols.filter((s) => s.exported && TYPE_KINDS.has(s.kind));
|
|
1186
1524
|
const lines = [];
|
|
1187
|
-
lines.push(`# ${packageName} \u2014 Types`);
|
|
1188
|
-
lines.push("");
|
|
1189
1525
|
lines.push("Type contracts exported by this package: interfaces, type aliases, and enums.");
|
|
1190
1526
|
lines.push("");
|
|
1191
1527
|
for (const s of typeSymbols) {
|
|
@@ -1210,22 +1546,24 @@ function renderTypesPage(packageName, symbols, _options) {
|
|
|
1210
1546
|
lines.push("| Property | Type | Required | Description |");
|
|
1211
1547
|
lines.push("|----------|------|----------|-------------|");
|
|
1212
1548
|
for (const child of children) {
|
|
1213
|
-
|
|
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} |`);
|
|
1214
1554
|
}
|
|
1215
1555
|
lines.push("");
|
|
1216
1556
|
}
|
|
1217
1557
|
}
|
|
1558
|
+
void packageName;
|
|
1218
1559
|
return lines.join("\n");
|
|
1219
1560
|
}
|
|
1220
1561
|
function renderFunctionsPage(packageName, symbols, _options) {
|
|
1221
1562
|
const fnSymbols = symbols.filter((s) => s.exported && FUNCTION_KINDS.has(s.kind));
|
|
1222
1563
|
const lines = [];
|
|
1223
|
-
lines.push(`# ${packageName} \u2014 Functions & Classes`);
|
|
1224
|
-
lines.push("");
|
|
1225
1564
|
lines.push("Functions and classes exported by this package.");
|
|
1226
1565
|
lines.push("");
|
|
1227
1566
|
for (const s of fnSymbols) {
|
|
1228
|
-
const _ext = s.kind === "function" ? "()" : "";
|
|
1229
1567
|
const paramSig = s.kind === "function" && s.documentation?.params ? s.documentation.params.map((p) => p.name).join(", ") : "";
|
|
1230
1568
|
const heading = s.kind === "function" ? `${s.name}(${paramSig})` : s.name;
|
|
1231
1569
|
lines.push(`## ${heading}`);
|
|
@@ -1253,7 +1591,14 @@ function renderFunctionsPage(packageName, symbols, _options) {
|
|
|
1253
1591
|
lines.push("| Name | Type | Description |");
|
|
1254
1592
|
lines.push("|------|------|-------------|");
|
|
1255
1593
|
for (const p of params) {
|
|
1256
|
-
|
|
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";
|
|
1257
1602
|
lines.push(`| \`${p.name}\` | ${type} | ${escapePipe(p.description)} |`);
|
|
1258
1603
|
}
|
|
1259
1604
|
lines.push("");
|
|
@@ -1278,7 +1623,7 @@ function renderFunctionsPage(packageName, symbols, _options) {
|
|
|
1278
1623
|
const ex = examples[0];
|
|
1279
1624
|
lines.push("**Example**");
|
|
1280
1625
|
lines.push("");
|
|
1281
|
-
lines.push(`\`\`\`${ex.language}`);
|
|
1626
|
+
lines.push(`\`\`\`${ex.language || "typescript"}`);
|
|
1282
1627
|
lines.push(ex.code.trim());
|
|
1283
1628
|
lines.push("```");
|
|
1284
1629
|
lines.push("");
|
|
@@ -1303,6 +1648,7 @@ function renderFunctionsPage(packageName, symbols, _options) {
|
|
|
1303
1648
|
}
|
|
1304
1649
|
}
|
|
1305
1650
|
}
|
|
1651
|
+
void packageName;
|
|
1306
1652
|
return lines.join("\n");
|
|
1307
1653
|
}
|
|
1308
1654
|
function renderExamplesPage(packageName, symbols, _options) {
|
|
@@ -1310,8 +1656,6 @@ function renderExamplesPage(packageName, symbols, _options) {
|
|
|
1310
1656
|
(s) => s.exported && s.kind !== "method" && s.kind !== "property"
|
|
1311
1657
|
);
|
|
1312
1658
|
const lines = [];
|
|
1313
|
-
lines.push(`# ${packageName} \u2014 Examples`);
|
|
1314
|
-
lines.push("");
|
|
1315
1659
|
lines.push("All usage examples from the package, aggregated for quick reference.");
|
|
1316
1660
|
lines.push("");
|
|
1317
1661
|
let hasExamples = false;
|
|
@@ -1326,10 +1670,12 @@ function renderExamplesPage(packageName, symbols, _options) {
|
|
|
1326
1670
|
lines.push(`_${s.documentation.summary}_`);
|
|
1327
1671
|
lines.push("");
|
|
1328
1672
|
}
|
|
1329
|
-
lines.push(
|
|
1673
|
+
lines.push(
|
|
1674
|
+
`[View in API reference](${slugLink(`packages/${packageName}/api/functions`)}#${toAnchor2(s.name)})`
|
|
1675
|
+
);
|
|
1330
1676
|
lines.push("");
|
|
1331
1677
|
for (const ex of examples) {
|
|
1332
|
-
lines.push(`\`\`\`${ex.language}`);
|
|
1678
|
+
lines.push(`\`\`\`${ex.language || "typescript"}`);
|
|
1333
1679
|
lines.push(ex.code.trim());
|
|
1334
1680
|
lines.push("```");
|
|
1335
1681
|
lines.push("");
|
|
@@ -1341,184 +1687,94 @@ function renderExamplesPage(packageName, symbols, _options) {
|
|
|
1341
1687
|
}
|
|
1342
1688
|
return lines.join("\n");
|
|
1343
1689
|
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
"class",
|
|
1347
|
-
"interface",
|
|
1348
|
-
"type",
|
|
1349
|
-
"enum",
|
|
1350
|
-
"variable"
|
|
1351
|
-
];
|
|
1352
|
-
var KIND_LABELS2 = {
|
|
1353
|
-
function: "Functions",
|
|
1354
|
-
class: "Classes",
|
|
1355
|
-
interface: "Interfaces",
|
|
1356
|
-
type: "Types",
|
|
1357
|
-
enum: "Enums",
|
|
1358
|
-
variable: "Variables"
|
|
1359
|
-
};
|
|
1360
|
-
function renderApiSymbol(symbol, depth) {
|
|
1361
|
-
const hashes = "#".repeat(depth);
|
|
1362
|
-
const ext = symbol.kind === "function" || symbol.kind === "method" ? "()" : "";
|
|
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));
|
|
1363
1692
|
const lines = [];
|
|
1364
|
-
lines.push(
|
|
1693
|
+
lines.push(`Configuration reference for **${options.projectName}**.`);
|
|
1365
1694
|
lines.push("");
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
if (params.length > 0) {
|
|
1382
|
-
lines.push("**Parameters**");
|
|
1383
|
-
lines.push("");
|
|
1384
|
-
for (const p of params) {
|
|
1385
|
-
const typeStr = p.type ? ` (\`${p.type}\`)` : "";
|
|
1386
|
-
lines.push(`- \`${p.name}\`${typeStr} \u2014 ${p.description}`);
|
|
1387
|
-
}
|
|
1388
|
-
lines.push("");
|
|
1389
|
-
}
|
|
1390
|
-
if (symbol.documentation?.returns) {
|
|
1391
|
-
const retType = symbol.documentation.returns.type ? ` (\`${symbol.documentation.returns.type}\`)` : "";
|
|
1392
|
-
lines.push(`**Returns**${retType}: ${symbol.documentation.returns.description}`);
|
|
1393
|
-
lines.push("");
|
|
1394
|
-
}
|
|
1395
|
-
const throws = symbol.documentation?.throws ?? [];
|
|
1396
|
-
if (throws.length > 0) {
|
|
1397
|
-
lines.push("**Throws**");
|
|
1695
|
+
lines.push("## forge-ts.config.ts");
|
|
1696
|
+
lines.push("");
|
|
1697
|
+
lines.push("Create a `forge-ts.config.ts` file in your project root:");
|
|
1698
|
+
lines.push("");
|
|
1699
|
+
lines.push("```typescript");
|
|
1700
|
+
lines.push('import { defineConfig } from "@forge-ts/core";');
|
|
1701
|
+
lines.push("");
|
|
1702
|
+
lines.push("export default defineConfig({");
|
|
1703
|
+
lines.push(' rootDir: ".",');
|
|
1704
|
+
lines.push(' outDir: "docs/generated",');
|
|
1705
|
+
lines.push("});");
|
|
1706
|
+
lines.push("```");
|
|
1707
|
+
lines.push("");
|
|
1708
|
+
if (configSymbol) {
|
|
1709
|
+
lines.push(`## \`${configSymbol.name}\``);
|
|
1398
1710
|
lines.push("");
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
lines.push(
|
|
1711
|
+
if (configSymbol.documentation?.summary) {
|
|
1712
|
+
lines.push(configSymbol.documentation.summary);
|
|
1713
|
+
lines.push("");
|
|
1402
1714
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1715
|
+
const children = (configSymbol.children ?? []).filter(
|
|
1716
|
+
(c) => c.kind === "property" || c.kind === "method"
|
|
1717
|
+
);
|
|
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
|
+
}
|
|
1413
1728
|
lines.push("");
|
|
1414
1729
|
}
|
|
1415
1730
|
}
|
|
1416
|
-
const children = symbol.children ?? [];
|
|
1417
|
-
if (children.length > 0 && depth < 5) {
|
|
1418
|
-
for (const child of children) {
|
|
1419
|
-
lines.push(renderApiSymbol(child, depth + 1));
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
1731
|
return lines.join("\n");
|
|
1423
1732
|
}
|
|
1424
|
-
function
|
|
1425
|
-
const
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
}
|
|
1470
|
-
return lines.join("\n");
|
|
1471
|
-
}
|
|
1472
|
-
function renderGettingStartedPage(symbolsByPackage, options) {
|
|
1473
|
-
let firstExample;
|
|
1474
|
-
let firstSymbolName = "";
|
|
1475
|
-
let firstPackageName = "";
|
|
1476
|
-
outer: for (const [pkgName, symbols] of symbolsByPackage) {
|
|
1477
|
-
for (const s of symbols) {
|
|
1478
|
-
if (!s.exported || s.kind !== "function") continue;
|
|
1479
|
-
const ex = s.documentation?.examples?.[0];
|
|
1480
|
-
if (ex) {
|
|
1481
|
-
firstExample = ex;
|
|
1482
|
-
firstSymbolName = s.name;
|
|
1483
|
-
firstPackageName = pkgName;
|
|
1484
|
-
break outer;
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
const lines = [];
|
|
1489
|
-
lines.push("# Getting Started");
|
|
1490
|
-
lines.push("");
|
|
1491
|
-
lines.push(`Welcome to **${options.projectName}**.`);
|
|
1492
|
-
if (options.projectDescription) {
|
|
1493
|
-
lines.push("");
|
|
1494
|
-
lines.push(options.projectDescription);
|
|
1495
|
-
}
|
|
1496
|
-
lines.push("");
|
|
1497
|
-
lines.push("## Installation");
|
|
1498
|
-
lines.push("");
|
|
1499
|
-
lines.push("```bash");
|
|
1500
|
-
lines.push(`npm install ${options.projectName}`);
|
|
1501
|
-
lines.push("```");
|
|
1502
|
-
lines.push("");
|
|
1503
|
-
if (firstExample) {
|
|
1504
|
-
lines.push("## Quick Start");
|
|
1505
|
-
lines.push("");
|
|
1506
|
-
lines.push(
|
|
1507
|
-
`The following example demonstrates \`${firstSymbolName}\` from the \`${firstPackageName}\` package.`
|
|
1508
|
-
);
|
|
1509
|
-
lines.push("");
|
|
1510
|
-
lines.push(`\`\`\`${firstExample.language}`);
|
|
1511
|
-
lines.push(firstExample.code.trim());
|
|
1512
|
-
lines.push("```");
|
|
1513
|
-
lines.push("");
|
|
1514
|
-
}
|
|
1515
|
-
lines.push("## Next Steps");
|
|
1516
|
-
lines.push("");
|
|
1517
|
-
lines.push("- Browse the [API Reference](./packages/)");
|
|
1518
|
-
for (const pkgName of symbolsByPackage.keys()) {
|
|
1519
|
-
lines.push(` - [${pkgName}](./packages/${pkgName}/api-reference.md)`);
|
|
1520
|
-
}
|
|
1521
|
-
return lines.join("\n");
|
|
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");
|
|
1522
1778
|
}
|
|
1523
1779
|
function generateDocSite(symbolsByPackage, config, options) {
|
|
1524
1780
|
const ext = options.format === "mdx" ? "mdx" : "md";
|
|
@@ -1549,10 +1805,38 @@ function generateDocSite(symbolsByPackage, config, options) {
|
|
|
1549
1805
|
`,
|
|
1550
1806
|
frontmatter: gettingStartedFrontmatter
|
|
1551
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
|
+
});
|
|
1552
1836
|
let pkgPosition = 1;
|
|
1553
1837
|
for (const [pkgName, symbols] of symbolsByPackage) {
|
|
1554
1838
|
const pkgBase = `packages/${pkgName}`;
|
|
1555
|
-
const overviewContent =
|
|
1839
|
+
const overviewContent = renderPackageOverviewPage(pkgName, symbols, options);
|
|
1556
1840
|
const overviewFrontmatter = buildFrontmatterFields(
|
|
1557
1841
|
pkgName,
|
|
1558
1842
|
`${pkgName} package overview`,
|
|
@@ -1565,29 +1849,17 @@ function generateDocSite(symbolsByPackage, config, options) {
|
|
|
1565
1849
|
`,
|
|
1566
1850
|
frontmatter: overviewFrontmatter
|
|
1567
1851
|
});
|
|
1568
|
-
const
|
|
1569
|
-
const
|
|
1852
|
+
const apiIndexContent = renderApiIndexPage(pkgName, symbols);
|
|
1853
|
+
const apiIndexFrontmatter = buildFrontmatterFields(
|
|
1570
1854
|
`${pkgName} \u2014 API Reference`,
|
|
1571
1855
|
`Full API reference for the ${pkgName} package`,
|
|
1572
1856
|
options.ssgTarget
|
|
1573
1857
|
);
|
|
1574
1858
|
pages.push({
|
|
1575
|
-
path: `${pkgBase}/api
|
|
1576
|
-
content: `${serializeFrontmatter(
|
|
1577
|
-
`,
|
|
1578
|
-
frontmatter: apiFrontmatter
|
|
1579
|
-
});
|
|
1580
|
-
const typesContent = renderTypesPage(pkgName, symbols, options);
|
|
1581
|
-
const typesFrontmatter = buildFrontmatterFields(
|
|
1582
|
-
`${pkgName} \u2014 Types`,
|
|
1583
|
-
`Type contracts for the ${pkgName} package`,
|
|
1584
|
-
options.ssgTarget
|
|
1585
|
-
);
|
|
1586
|
-
pages.push({
|
|
1587
|
-
path: `${pkgBase}/types.${ext}`,
|
|
1588
|
-
content: `${serializeFrontmatter(typesFrontmatter)}${typesContent.trimEnd()}
|
|
1859
|
+
path: `${pkgBase}/api/index.${ext}`,
|
|
1860
|
+
content: `${serializeFrontmatter(apiIndexFrontmatter)}${apiIndexContent.trimEnd()}
|
|
1589
1861
|
`,
|
|
1590
|
-
frontmatter:
|
|
1862
|
+
frontmatter: apiIndexFrontmatter
|
|
1591
1863
|
});
|
|
1592
1864
|
const functionsContent = renderFunctionsPage(pkgName, symbols, options);
|
|
1593
1865
|
const functionsFrontmatter = buildFrontmatterFields(
|
|
@@ -1596,11 +1868,23 @@ function generateDocSite(symbolsByPackage, config, options) {
|
|
|
1596
1868
|
options.ssgTarget
|
|
1597
1869
|
);
|
|
1598
1870
|
pages.push({
|
|
1599
|
-
path: `${pkgBase}/functions.${ext}`,
|
|
1871
|
+
path: `${pkgBase}/api/functions.${ext}`,
|
|
1600
1872
|
content: `${serializeFrontmatter(functionsFrontmatter)}${functionsContent.trimEnd()}
|
|
1601
1873
|
`,
|
|
1602
1874
|
frontmatter: functionsFrontmatter
|
|
1603
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
|
+
});
|
|
1604
1888
|
const examplesContent = renderExamplesPage(pkgName, symbols, options);
|
|
1605
1889
|
const examplesFrontmatter = buildFrontmatterFields(
|
|
1606
1890
|
`${pkgName} \u2014 Examples`,
|
|
@@ -1608,17 +1892,595 @@ function generateDocSite(symbolsByPackage, config, options) {
|
|
|
1608
1892
|
options.ssgTarget
|
|
1609
1893
|
);
|
|
1610
1894
|
pages.push({
|
|
1611
|
-
path: `${pkgBase}/examples.${ext}`,
|
|
1895
|
+
path: `${pkgBase}/api/examples.${ext}`,
|
|
1612
1896
|
content: `${serializeFrontmatter(examplesFrontmatter)}${examplesContent.trimEnd()}
|
|
1613
1897
|
`,
|
|
1614
1898
|
frontmatter: examplesFrontmatter
|
|
1615
1899
|
});
|
|
1616
1900
|
pkgPosition++;
|
|
1617
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
|
+
});
|
|
1618
1953
|
void config;
|
|
1619
1954
|
return pages;
|
|
1620
1955
|
}
|
|
1621
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
|
+
|
|
1622
2484
|
// src/ssg-config.ts
|
|
1623
2485
|
function pageSlug5(pagePath) {
|
|
1624
2486
|
return pagePath.replace(/\.[^.]+$/, "");
|
|
@@ -1783,11 +2645,34 @@ function generateSSGConfigs(pages, target, projectName) {
|
|
|
1783
2645
|
}
|
|
1784
2646
|
|
|
1785
2647
|
// src/index.ts
|
|
1786
|
-
import {
|
|
2648
|
+
import { existsSync as existsSync2 } from "fs";
|
|
2649
|
+
import { mkdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
1787
2650
|
import { dirname, join } from "path";
|
|
1788
2651
|
import { createWalker } from "@forge-ts/core";
|
|
1789
|
-
|
|
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) {
|
|
1790
2674
|
const start = Date.now();
|
|
2675
|
+
const forceStubs = options?.forceStubs ?? false;
|
|
1791
2676
|
const walker = createWalker(config);
|
|
1792
2677
|
const symbols = walker.walk();
|
|
1793
2678
|
await mkdir(config.outDir, { recursive: true });
|
|
@@ -1798,7 +2683,8 @@ async function generate(config) {
|
|
|
1798
2683
|
const ext = format === "mdx" ? "mdx" : "md";
|
|
1799
2684
|
await writeFile2(join(config.outDir, `api-reference.${ext}`), content, "utf8");
|
|
1800
2685
|
}
|
|
1801
|
-
const
|
|
2686
|
+
const resolvedRoot = config.rootDir === "." ? process.cwd() : config.rootDir;
|
|
2687
|
+
const projectName = resolvedRoot.split("/").pop() ?? "Project";
|
|
1802
2688
|
const symbolsByPackage = groupSymbolsByPackage(symbols, config.rootDir);
|
|
1803
2689
|
const target = config.gen.ssgTarget ?? DEFAULT_TARGET;
|
|
1804
2690
|
const adapter = getAdapter(target);
|
|
@@ -1806,7 +2692,9 @@ async function generate(config) {
|
|
|
1806
2692
|
const pages = generateDocSite(symbolsByPackage, config, {
|
|
1807
2693
|
format,
|
|
1808
2694
|
ssgTarget: config.gen.ssgTarget,
|
|
1809
|
-
projectName
|
|
2695
|
+
projectName,
|
|
2696
|
+
repositoryUrl: config.project.repository,
|
|
2697
|
+
packageName: config.project.packageName
|
|
1810
2698
|
});
|
|
1811
2699
|
const adapterContext = {
|
|
1812
2700
|
config,
|
|
@@ -1818,6 +2706,14 @@ async function generate(config) {
|
|
|
1818
2706
|
const transformedPages = adapter.transformPages(pages, adapterContext);
|
|
1819
2707
|
for (const file of transformedPages) {
|
|
1820
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
|
+
}
|
|
1821
2717
|
await mkdir(dirname(filePath), { recursive: true });
|
|
1822
2718
|
await writeFile2(filePath, file.content, "utf8");
|
|
1823
2719
|
}
|
|
@@ -1835,6 +2731,17 @@ async function generate(config) {
|
|
|
1835
2731
|
await writeFile2(join(config.outDir, "llms.txt"), llms, "utf8");
|
|
1836
2732
|
const llmsFull = generateLlmsFullTxt(symbols, config);
|
|
1837
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
|
+
}
|
|
1838
2745
|
}
|
|
1839
2746
|
if (config.gen.readmeSync) {
|
|
1840
2747
|
await syncReadme(join(config.rootDir, "README.md"), symbols);
|
|
@@ -1855,6 +2762,8 @@ export {
|
|
|
1855
2762
|
generateLlmsTxt,
|
|
1856
2763
|
generateMarkdown,
|
|
1857
2764
|
generateSSGConfigs,
|
|
2765
|
+
generateSkillMd,
|
|
2766
|
+
generateSkillPackage,
|
|
1858
2767
|
getAdapter,
|
|
1859
2768
|
getAvailableTargets,
|
|
1860
2769
|
groupSymbolsByPackage,
|