rspress-plugin-api-extractor 0.14.0 → 0.15.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/BuildEnv.js +0 -1
- package/build-program.js +4 -4
- package/build-stages.js +98 -281
- package/config-helpers.js +1 -1
- package/emit/mdx.js +311 -0
- package/emit/meta.js +62 -0
- package/index.d.ts +1 -42
- package/layers/build-metrics.js +1 -2
- package/layers/config-resolution.js +3 -5
- package/layers/type-environment.js +1 -2
- package/llms-program.js +3 -3
- package/markdown/helpers.js +10 -176
- package/observability/sinks/console-sink.js +0 -1
- package/observability/sinks/metrics-sink.js +0 -2
- package/package.json +6 -5
- package/path-derivation.js +1 -29
- package/plugin.js +2 -1
- package/prettier-formatter.js +27 -59
- package/remark-with-api.js +3 -2
- package/schemas/config.js +1 -18
- package/schemas/observability.js +0 -2
- package/schemas/performance.js +0 -1
- package/services/TwoslashCacheService.js +1 -1
- package/twoslash-transformer.js +14 -2
- package/code-post-processor.js +0 -38
- package/llms-processing.js +0 -270
- package/markdown/page-generators/class-page.js +0 -363
- package/markdown/page-generators/enum-page.js +0 -152
- package/markdown/page-generators/function-page.js +0 -127
- package/markdown/page-generators/index-pages.js +0 -25
- package/markdown/page-generators/interface-page.js +0 -310
- package/markdown/page-generators/namespace-page.js +0 -277
- package/markdown/page-generators/type-alias-page.js +0 -110
- package/markdown/page-generators/variable-page.js +0 -110
- package/markdown/prose-linker.js +0 -22
- package/twoslash-cache.js +0 -174
- package/twoslash-patterns.js +0 -87
package/BuildEnv.js
CHANGED
package/build-program.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { BuildId, PageConcurrency, SuppressExampleErrors } from "./BuildEnv.js";
|
|
2
|
-
import { setProseLinker } from "./markdown/prose-linker.js";
|
|
3
2
|
import { buildPipelineForApi, cleanupAndCommit, prepareWorkItems, writeMetadata } from "./build-stages.js";
|
|
4
3
|
import { HideCutLinesTransformer, MemberFormatTransformer } from "./hide-cut-transformer.js";
|
|
5
4
|
import { withPhase } from "./observability/spans.js";
|
|
@@ -10,6 +9,7 @@ import { ShikiCrossLinker } from "./shiki-transformer.js";
|
|
|
10
9
|
import { VfsRegistry } from "./vfs-registry.js";
|
|
11
10
|
import path from "node:path";
|
|
12
11
|
import { Effect, FileSystem } from "effect";
|
|
12
|
+
import { CrossLinker } from "@tsdoctor/model";
|
|
13
13
|
import { attributionFacts, packageContext } from "@tsdoctor/seo";
|
|
14
14
|
import { SnapshotService } from "@tsdoctor/snapshot";
|
|
15
15
|
|
|
@@ -54,10 +54,9 @@ function generateApiDocs(apiConfig, fileContextMap) {
|
|
|
54
54
|
const { workItems, crossLinkData } = yield* withPhase("resolve", phaseCtx, Effect.sync(() => prepareWorkItems({
|
|
55
55
|
apiPackage,
|
|
56
56
|
categories,
|
|
57
|
-
baseRoute
|
|
58
|
-
packageName
|
|
57
|
+
baseRoute
|
|
59
58
|
})));
|
|
60
|
-
|
|
59
|
+
const linker = CrossLinker.fromRoutes(crossLinkData.routes);
|
|
61
60
|
const apiScope = baseRoute.replace(/^\//, "").split("/")[0] || packageName;
|
|
62
61
|
const shikiCrossLinker = ShikiCrossLinker.fromRoutes(crossLinkData.routes, apiScope);
|
|
63
62
|
addTypeRoutes(crossLinkData.routes);
|
|
@@ -88,6 +87,7 @@ function generateApiDocs(apiConfig, fileContextMap) {
|
|
|
88
87
|
existingSnapshots,
|
|
89
88
|
...suppressExampleErrors != null ? { suppressExampleErrors } : {},
|
|
90
89
|
...llmsPlugin != null ? { llmsPlugin } : {},
|
|
90
|
+
linker,
|
|
91
91
|
...apiConfig.docsRoot != null ? { docsRoot: apiConfig.docsRoot } : {},
|
|
92
92
|
...siteUrl != null ? { siteUrl } : {},
|
|
93
93
|
...ogImage != null ? { ogImage } : {},
|
package/build-stages.js
CHANGED
|
@@ -1,102 +1,50 @@
|
|
|
1
|
+
import { emitMdxBody } from "./emit/mdx.js";
|
|
2
|
+
import { emitIndexPage, renderCategoryMeta, renderRootMeta } from "./emit/meta.js";
|
|
1
3
|
import { BuildMetrics } from "./layers/build-metrics.js";
|
|
4
|
+
import { generateFrontmatter } from "./markdown/helpers.js";
|
|
2
5
|
import { PluginEvent } from "./observability/events.js";
|
|
3
6
|
import { emit } from "./observability/EventBus.js";
|
|
4
7
|
import { emitSync, syncBuildId } from "./observability/sync-emitter.js";
|
|
5
|
-
import { generateFrontmatter } from "./markdown/helpers.js";
|
|
6
|
-
import { ClassPageGenerator } from "./markdown/page-generators/class-page.js";
|
|
7
|
-
import { EnumPageGenerator } from "./markdown/page-generators/enum-page.js";
|
|
8
|
-
import { FunctionPageGenerator } from "./markdown/page-generators/function-page.js";
|
|
9
|
-
import { MainIndexPageGenerator } from "./markdown/page-generators/index-pages.js";
|
|
10
|
-
import { InterfacePageGenerator } from "./markdown/page-generators/interface-page.js";
|
|
11
|
-
import { NamespacePageGenerator } from "./markdown/page-generators/namespace-page.js";
|
|
12
|
-
import { TypeAliasPageGenerator } from "./markdown/page-generators/type-alias-page.js";
|
|
13
|
-
import { VariablePageGenerator } from "./markdown/page-generators/variable-page.js";
|
|
14
8
|
import { OgService } from "./services/OgService.js";
|
|
15
9
|
import path from "node:path";
|
|
10
|
+
import { NavEntry, buildIndexPage, buildNav, buildPage, prepareWorkItems } from "@tsdoctor/pages";
|
|
16
11
|
import { Effect, FileSystem, Metric, Option, Stream } from "effect";
|
|
12
|
+
import { Routes, parseFrontmatter, stringifyFrontmatter } from "@tsdoctor/model";
|
|
17
13
|
import { deriveScriptBody, headTags } from "@tsdoctor/seo";
|
|
18
14
|
import { SnapshotService, hashContent, hashFrontmatter } from "@tsdoctor/snapshot";
|
|
19
|
-
import { ApiItemKind } from "@microsoft/api-extractor-model";
|
|
20
|
-
import { ApiItems, EntryPoints, Routes, SyntheticBases, parseFrontmatter, stringifyFrontmatter } from "@tsdoctor/model";
|
|
21
15
|
|
|
22
16
|
//#region src/build-stages.ts
|
|
23
17
|
/**
|
|
24
|
-
* Cross-link priority by API item kind (lower = higher priority). When a bare
|
|
25
|
-
* name maps to multiple pages (the const+type companion pattern), the bare
|
|
26
|
-
* cross-link resolves to the higher-priority kind — value declarations win over
|
|
27
|
-
* type-only declarations, so `Foo` links to the importable schema, not the type.
|
|
28
|
-
*/
|
|
29
|
-
const CROSS_LINK_KIND_PRIORITY = {
|
|
30
|
-
Class: 0,
|
|
31
|
-
Function: 1,
|
|
32
|
-
Variable: 2,
|
|
33
|
-
Enum: 3,
|
|
34
|
-
Interface: 4,
|
|
35
|
-
TypeAlias: 5,
|
|
36
|
-
Namespace: 6
|
|
37
|
-
};
|
|
38
|
-
/** Lower number = higher priority for which page a bare cross-link name resolves to. */
|
|
39
|
-
function crossLinkKindPriority(kind) {
|
|
40
|
-
return CROSS_LINK_KIND_PRIORITY[kind] ?? 100;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
18
|
* Prepare the flat list of WorkItems to process and the cross-link data maps.
|
|
44
19
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
20
|
+
* The computation lives in `@tsdoctor/pages`' `prepareWorkItems`; this is
|
|
21
|
+
* the adapter's reporting over its result: an `ItemSkipped` warning per
|
|
22
|
+
* uncategorized item through the sync-island seam, and a typed
|
|
23
|
+
* `RouteCollisionDetected` event per collision before the fatal
|
|
24
|
+
* `Routes.RouteCollisionError` — so the fatal build path still surfaces the
|
|
25
|
+
* collision in .api-docs/build/issues.json (see plugin.ts's config() catch).
|
|
50
26
|
*
|
|
51
27
|
* NOTE: This function does NOT install the prose linker. The caller
|
|
52
28
|
* is responsible for passing the returned crossLinkData to the cross-linker and
|
|
53
29
|
* Shiki cross-linker as needed.
|
|
54
30
|
*/
|
|
55
|
-
function prepareWorkItems(input) {
|
|
31
|
+
function prepareWorkItems$1(input) {
|
|
56
32
|
const { apiPackage, categories, baseRoute } = input;
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
resolvedLookup.set(key, resolved);
|
|
64
|
-
}
|
|
65
|
-
const { items, uncategorized } = ApiItems.categorize(docItems, categories);
|
|
66
|
-
for (const skipped of uncategorized) emitSync(PluginEvent.ItemSkipped({
|
|
33
|
+
const prepared = prepareWorkItems({
|
|
34
|
+
apiPackage,
|
|
35
|
+
categories,
|
|
36
|
+
baseRoute
|
|
37
|
+
});
|
|
38
|
+
for (const skipped of prepared.uncategorized) emitSync(PluginEvent.ItemSkipped({
|
|
67
39
|
ctx: { buildId: syncBuildId() },
|
|
68
40
|
item: skipped.displayName,
|
|
69
41
|
kind: String(skipped.kind),
|
|
70
42
|
reason: "uncategorized",
|
|
71
43
|
level: "warn"
|
|
72
44
|
}));
|
|
73
|
-
|
|
74
|
-
const candidates = [];
|
|
75
|
-
for (const [categoryKey, categoryConfig] of Object.entries(categories)) for (const item of items[categoryKey] || []) candidates.push(new Routes.RouteCandidate({
|
|
76
|
-
id: `${item.displayName}::${item.kind}`,
|
|
77
|
-
displayName: item.displayName,
|
|
78
|
-
folder: categoryConfig.folderName,
|
|
79
|
-
baseName: item.displayName.toLowerCase(),
|
|
80
|
-
kind: String(item.kind),
|
|
81
|
-
canonicalRef: item.canonicalReference?.toString() ?? item.displayName
|
|
82
|
-
}));
|
|
83
|
-
for (const nsMember of namespaceMembers) {
|
|
84
|
-
const nsCategoryEntry = Object.entries(categories).find(([, config]) => config.itemKinds?.includes(nsMember.item.kind));
|
|
85
|
-
if (!nsCategoryEntry) continue;
|
|
86
|
-
const [, nsCategoryConfig] = nsCategoryEntry;
|
|
87
|
-
candidates.push(new Routes.RouteCandidate({
|
|
88
|
-
id: nsMember.qualifiedName,
|
|
89
|
-
displayName: nsMember.qualifiedName,
|
|
90
|
-
folder: nsCategoryConfig.folderName,
|
|
91
|
-
baseName: nsMember.qualifiedName.toLowerCase(),
|
|
92
|
-
kind: String(nsMember.item.kind),
|
|
93
|
-
canonicalRef: nsMember.item.canonicalReference?.toString() ?? nsMember.qualifiedName
|
|
94
|
-
}));
|
|
95
|
-
}
|
|
96
|
-
const collisions = Routes.detectCollisions(candidates);
|
|
97
|
-
if (collisions.length > 0) {
|
|
45
|
+
if (prepared.collisions.length > 0) {
|
|
98
46
|
try {
|
|
99
|
-
for (const collision of collisions) emitSync(PluginEvent.RouteCollisionDetected({
|
|
47
|
+
for (const collision of prepared.collisions) emitSync(PluginEvent.RouteCollisionDetected({
|
|
100
48
|
ctx: {
|
|
101
49
|
buildId: syncBuildId(),
|
|
102
50
|
route: collision.route
|
|
@@ -107,100 +55,12 @@ function prepareWorkItems(input) {
|
|
|
107
55
|
} catch {}
|
|
108
56
|
throw new Routes.RouteCollisionError({
|
|
109
57
|
baseRoute,
|
|
110
|
-
collisions
|
|
58
|
+
collisions: prepared.collisions
|
|
111
59
|
});
|
|
112
60
|
}
|
|
113
|
-
const routes = /* @__PURE__ */ new Map();
|
|
114
|
-
const kinds = /* @__PURE__ */ new Map();
|
|
115
|
-
const routeOwnerPriority = /* @__PURE__ */ new Map();
|
|
116
|
-
for (const [categoryKey, categoryConfig] of Object.entries(categories)) {
|
|
117
|
-
const categoryItems = items[categoryKey] || [];
|
|
118
|
-
for (const item of categoryItems) {
|
|
119
|
-
const itemRoute = `${baseRoute}/${categoryConfig.folderName}/${item.displayName.toLowerCase()}`;
|
|
120
|
-
const priority = crossLinkKindPriority(String(item.kind));
|
|
121
|
-
const existingPriority = routeOwnerPriority.get(item.displayName);
|
|
122
|
-
if (existingPriority === void 0 || priority < existingPriority) {
|
|
123
|
-
routes.set(item.displayName, itemRoute);
|
|
124
|
-
kinds.set(item.displayName, item.kind);
|
|
125
|
-
routeOwnerPriority.set(item.displayName, priority);
|
|
126
|
-
}
|
|
127
|
-
if (item.kind === "Class" || item.kind === "Interface") {
|
|
128
|
-
const itemWithMembers = item;
|
|
129
|
-
const anchors = ApiItems.memberAnchors(itemWithMembers);
|
|
130
|
-
const byCanonicalRef = new Map(itemWithMembers.members.map((member) => [member.canonicalReference?.toString() ?? member.displayName, member]));
|
|
131
|
-
for (const [routeKey, memberId] of ApiItems.memberRouteKeys(itemWithMembers)) {
|
|
132
|
-
const member = byCanonicalRef.get(memberId);
|
|
133
|
-
if (!member) continue;
|
|
134
|
-
const anchor = anchors.get(memberId) ?? Routes.memberAnchor(member.displayName);
|
|
135
|
-
routes.set(routeKey, `${itemRoute}#${anchor}`);
|
|
136
|
-
kinds.set(routeKey, member.kind);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
const unqualifiedNameCounts = /* @__PURE__ */ new Map();
|
|
142
|
-
for (const nsMember of namespaceMembers) {
|
|
143
|
-
const name = nsMember.item.displayName;
|
|
144
|
-
unqualifiedNameCounts.set(name, (unqualifiedNameCounts.get(name) || 0) + 1);
|
|
145
|
-
}
|
|
146
|
-
for (const nsMember of namespaceMembers) {
|
|
147
|
-
const categoryEntry = Object.entries(categories).find(([, config]) => config.itemKinds?.includes(nsMember.item.kind));
|
|
148
|
-
if (!categoryEntry) continue;
|
|
149
|
-
const [, categoryConfig] = categoryEntry;
|
|
150
|
-
const qualifiedRoute = `${baseRoute}/${categoryConfig.folderName}/${nsMember.qualifiedName.toLowerCase()}`;
|
|
151
|
-
routes.set(nsMember.qualifiedName, qualifiedRoute);
|
|
152
|
-
kinds.set(nsMember.qualifiedName, nsMember.item.kind);
|
|
153
|
-
const displayName = nsMember.item.displayName;
|
|
154
|
-
if (/^[A-Z]/.test(displayName) && (unqualifiedNameCounts.get(displayName) || 0) <= 1 && !routes.has(displayName)) {
|
|
155
|
-
routes.set(displayName, qualifiedRoute);
|
|
156
|
-
kinds.set(displayName, nsMember.item.kind);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
for (const [baseItem, syntheticBase] of syntheticBases.bases) {
|
|
160
|
-
const baseName = baseItem.displayName;
|
|
161
|
-
if (routes.has(baseName)) continue;
|
|
162
|
-
const owner = syntheticBase.ownerClasses[0];
|
|
163
|
-
const ownerRoute = owner ? routes.get(owner.displayName) : void 0;
|
|
164
|
-
if (!ownerRoute) continue;
|
|
165
|
-
routes.set(baseName, `${ownerRoute}#${SyntheticBases.BASE_CLASS_ANCHOR}`);
|
|
166
|
-
kinds.set(baseName, baseItem.kind);
|
|
167
|
-
}
|
|
168
|
-
const workItems = [];
|
|
169
|
-
for (const [categoryKey, categoryConfig] of Object.entries(categories)) {
|
|
170
|
-
const categoryItems = items[categoryKey] || [];
|
|
171
|
-
for (const item of categoryItems) {
|
|
172
|
-
const lookupKey = `${item.displayName}::${item.kind}`;
|
|
173
|
-
const resolved = resolvedLookup.get(lookupKey);
|
|
174
|
-
const syntheticBase = syntheticBases.baseByOwner.get(item);
|
|
175
|
-
const memberAnchors = item.kind === "Class" || item.kind === "Interface" ? ApiItems.memberAnchors(item) : void 0;
|
|
176
|
-
workItems.push({
|
|
177
|
-
item,
|
|
178
|
-
categoryKey,
|
|
179
|
-
categoryConfig,
|
|
180
|
-
...resolved?.availableFrom != null ? { availableFrom: resolved.availableFrom } : {},
|
|
181
|
-
...syntheticBase != null ? { syntheticBase } : {},
|
|
182
|
-
...memberAnchors != null ? { memberAnchors } : {}
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
for (const nsMember of namespaceMembers) {
|
|
187
|
-
const categoryEntry = Object.entries(categories).find(([, config]) => config.itemKinds?.includes(nsMember.item.kind));
|
|
188
|
-
if (categoryEntry) {
|
|
189
|
-
const [categoryKey, categoryConfig] = categoryEntry;
|
|
190
|
-
workItems.push({
|
|
191
|
-
item: nsMember.item,
|
|
192
|
-
categoryKey,
|
|
193
|
-
categoryConfig,
|
|
194
|
-
namespaceMember: nsMember
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
61
|
return {
|
|
199
|
-
workItems,
|
|
200
|
-
crossLinkData:
|
|
201
|
-
routes,
|
|
202
|
-
kinds
|
|
203
|
-
}
|
|
62
|
+
workItems: prepared.workItems,
|
|
63
|
+
crossLinkData: prepared.crossLinkData
|
|
204
64
|
};
|
|
205
65
|
}
|
|
206
66
|
/**
|
|
@@ -220,94 +80,58 @@ function generateSinglePage(workItem, ctx) {
|
|
|
220
80
|
const { buildId, existingSnapshots, baseRoute, packageName, apiScope, apiName, source, buildTime, resolvedOutputDir, suppressExampleErrors, llmsPlugin } = ctx;
|
|
221
81
|
const { item, categoryConfig, namespaceMember } = workItem;
|
|
222
82
|
const pageGenStart = performance.now();
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
break;
|
|
242
|
-
}
|
|
243
|
-
case ApiItemKind.Function: {
|
|
244
|
-
const generator = new FunctionPageGenerator();
|
|
245
|
-
page = yield* Effect.promise(() => generator.generate(item, baseRoute, packageName, categoryConfig.singularName, apiScope, apiName, source, suppressExampleErrors, llmsPlugin, workItem.availableFrom));
|
|
246
|
-
page = {
|
|
247
|
-
routePath: page.routePath.replace("/function/", `/${categoryConfig.folderName}/`),
|
|
248
|
-
content: page.content
|
|
249
|
-
};
|
|
250
|
-
break;
|
|
251
|
-
}
|
|
252
|
-
case ApiItemKind.TypeAlias: {
|
|
253
|
-
const generator = new TypeAliasPageGenerator();
|
|
254
|
-
page = yield* Effect.promise(() => generator.generate(item, baseRoute, packageName, categoryConfig.singularName, apiScope, apiName, source, suppressExampleErrors, llmsPlugin, workItem.availableFrom));
|
|
255
|
-
page = {
|
|
256
|
-
routePath: page.routePath.replace("/type/", `/${categoryConfig.folderName}/`),
|
|
257
|
-
content: page.content
|
|
258
|
-
};
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
261
|
-
case ApiItemKind.Enum: {
|
|
262
|
-
const generator = new EnumPageGenerator();
|
|
263
|
-
page = yield* Effect.promise(() => generator.generate(item, baseRoute, packageName, categoryConfig.singularName, apiScope, apiName, source, suppressExampleErrors, llmsPlugin, workItem.availableFrom));
|
|
264
|
-
page = {
|
|
265
|
-
routePath: page.routePath.replace("/enum/", `/${categoryConfig.folderName}/`),
|
|
266
|
-
content: page.content
|
|
267
|
-
};
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
case ApiItemKind.Variable: {
|
|
271
|
-
const generator = new VariablePageGenerator();
|
|
272
|
-
page = yield* Effect.promise(() => generator.generate(item, baseRoute, packageName, categoryConfig.singularName, apiScope, apiName, source, suppressExampleErrors, llmsPlugin, workItem.availableFrom));
|
|
273
|
-
page = {
|
|
274
|
-
routePath: page.routePath.replace("/variable/", `/${categoryConfig.folderName}/`),
|
|
275
|
-
content: page.content
|
|
276
|
-
};
|
|
277
|
-
break;
|
|
278
|
-
}
|
|
279
|
-
case ApiItemKind.Namespace: {
|
|
280
|
-
const generator = new NamespacePageGenerator();
|
|
281
|
-
page = yield* Effect.promise(() => generator.generate(item, baseRoute, packageName, categoryConfig.singularName, apiScope, apiName, source, suppressExampleErrors, llmsPlugin, workItem.availableFrom));
|
|
282
|
-
page = {
|
|
283
|
-
routePath: page.routePath.replace("/namespace/", `/${categoryConfig.folderName}/`),
|
|
284
|
-
content: page.content
|
|
285
|
-
};
|
|
286
|
-
break;
|
|
287
|
-
}
|
|
288
|
-
default:
|
|
289
|
-
yield* emit(PluginEvent.ItemSkipped({
|
|
83
|
+
const built = yield* buildPage({
|
|
84
|
+
item,
|
|
85
|
+
categoryKey: workItem.categoryKey,
|
|
86
|
+
singularName: categoryConfig.singularName,
|
|
87
|
+
folderName: categoryConfig.folderName,
|
|
88
|
+
baseRoute,
|
|
89
|
+
packageName,
|
|
90
|
+
apiName,
|
|
91
|
+
namespaceMember,
|
|
92
|
+
availableFrom: workItem.availableFrom,
|
|
93
|
+
syntheticBase: workItem.syntheticBase,
|
|
94
|
+
memberAnchors: workItem.memberAnchors,
|
|
95
|
+
source,
|
|
96
|
+
suppressExampleErrors,
|
|
97
|
+
linker: ctx.linker,
|
|
98
|
+
onExampleFormatError: (error) => {
|
|
99
|
+
const cause = error.cause;
|
|
100
|
+
return emit(PluginEvent.PrettierError({
|
|
290
101
|
ctx: {
|
|
291
102
|
buildId,
|
|
292
103
|
packageName,
|
|
293
104
|
apiScope
|
|
294
105
|
},
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
level: "trace"
|
|
106
|
+
file: "unknown",
|
|
107
|
+
reason: cause instanceof Error ? cause.message : String(cause),
|
|
108
|
+
level: "warn"
|
|
299
109
|
}));
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
if (
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
if (Option.isNone(built)) {
|
|
113
|
+
yield* emit(PluginEvent.ItemSkipped({
|
|
114
|
+
ctx: {
|
|
115
|
+
buildId,
|
|
116
|
+
packageName,
|
|
117
|
+
apiScope
|
|
118
|
+
},
|
|
119
|
+
item: item.displayName,
|
|
120
|
+
kind: String(item.kind),
|
|
121
|
+
reason: "unsupported kind",
|
|
122
|
+
level: "trace"
|
|
123
|
+
}));
|
|
124
|
+
return null;
|
|
310
125
|
}
|
|
126
|
+
const irPage = built.value;
|
|
127
|
+
const body = yield* Effect.fromResult(emitMdxBody(irPage, {
|
|
128
|
+
apiScope,
|
|
129
|
+
llmsEnabled: llmsPlugin?.enabled === true
|
|
130
|
+
})).pipe(Effect.orDie);
|
|
131
|
+
const page = {
|
|
132
|
+
routePath: irPage.route,
|
|
133
|
+
content: generateFrontmatter(irPage.entityName, irPage.description, irPage.singularName, apiName) + body
|
|
134
|
+
};
|
|
311
135
|
const codeblockCount = (page.content.match(/<(ApiSignature|ApiMember|ApiExample)\b/g) ?? []).length;
|
|
312
136
|
yield* emit(PluginEvent.PageGenerated({
|
|
313
137
|
ctx: {
|
|
@@ -543,20 +367,27 @@ function writeMetadata(input) {
|
|
|
543
367
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
544
368
|
const snapshotSvc = yield* SnapshotService;
|
|
545
369
|
const { buildId, fileResults, categories, resolvedOutputDir, existingSnapshots, buildTime, baseRoute, packageName, generatedFiles } = input;
|
|
546
|
-
const
|
|
547
|
-
for (const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
370
|
+
const navCategories = {};
|
|
371
|
+
for (const [categoryKey, categoryConfig] of Object.entries(categories)) navCategories[categoryKey] = {
|
|
372
|
+
displayName: categoryConfig.displayName,
|
|
373
|
+
folderName: categoryConfig.folderName,
|
|
374
|
+
...categoryConfig.collapsible !== void 0 ? { collapsible: categoryConfig.collapsible } : {},
|
|
375
|
+
...categoryConfig.collapsed !== void 0 ? { collapsed: categoryConfig.collapsed } : {},
|
|
376
|
+
...categoryConfig.overviewHeaders !== void 0 ? { overviewHeaders: categoryConfig.overviewHeaders } : {}
|
|
377
|
+
};
|
|
378
|
+
const navTree = buildNav({
|
|
379
|
+
baseRoute,
|
|
380
|
+
categories: navCategories,
|
|
381
|
+
entries: fileResults.map((result) => NavEntry.make({
|
|
382
|
+
categoryKey: result.categoryKey,
|
|
383
|
+
label: result.label,
|
|
384
|
+
name: path.basename(result.relativePathWithExt, ".mdx"),
|
|
385
|
+
route: result.routePath
|
|
386
|
+
}))
|
|
556
387
|
});
|
|
557
388
|
const apiMetaJsonPath = path.join(resolvedOutputDir, "_meta.json");
|
|
558
389
|
const apiMetaJsonRelPath = "_meta.json";
|
|
559
|
-
const apiMetaJsonContent =
|
|
390
|
+
const apiMetaJsonContent = renderRootMeta(navTree);
|
|
560
391
|
const apiMetaContentHash = hashContent(apiMetaJsonContent);
|
|
561
392
|
const apiMetaOldSnapshot = existingSnapshots.get(apiMetaJsonRelPath);
|
|
562
393
|
let apiMetaUnchanged = false;
|
|
@@ -621,15 +452,17 @@ function writeMetadata(input) {
|
|
|
621
452
|
buildTime
|
|
622
453
|
}).pipe(Effect.ignore);
|
|
623
454
|
generatedFiles.add(apiMetaJsonRelPath);
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
455
|
+
const mainIndex = buildIndexPage({
|
|
456
|
+
packageName,
|
|
457
|
+
baseRoute
|
|
458
|
+
});
|
|
459
|
+
const mainIndexContent = emitIndexPage(mainIndex);
|
|
460
|
+
const indexRelativePath = `${mainIndex.route.replace(baseRoute, "").replace(/^\//, "")}.mdx`;
|
|
628
461
|
const indexAbsolutePath = path.join(resolvedOutputDir, indexRelativePath);
|
|
629
462
|
if (!(yield* fileSystem.exists(indexAbsolutePath).pipe(Effect.orElseSucceed(() => false)))) {
|
|
630
463
|
const indexDirPath = path.dirname(indexAbsolutePath);
|
|
631
464
|
yield* fileSystem.makeDirectory(indexDirPath, { recursive: true }).pipe(Effect.orDie);
|
|
632
|
-
yield* fileSystem.writeFileString(indexAbsolutePath,
|
|
465
|
+
yield* fileSystem.writeFileString(indexAbsolutePath, mainIndexContent).pipe(Effect.orDie);
|
|
633
466
|
yield* Metric.update(BuildMetrics.filesTotal, 1);
|
|
634
467
|
yield* Metric.update(BuildMetrics.filesNew, 1);
|
|
635
468
|
} else {
|
|
@@ -637,28 +470,11 @@ function writeMetadata(input) {
|
|
|
637
470
|
yield* Metric.update(BuildMetrics.filesUnchanged, 1);
|
|
638
471
|
}
|
|
639
472
|
generatedFiles.add("index.mdx");
|
|
640
|
-
const
|
|
641
|
-
|
|
642
|
-
const baseName = path.basename(result.relativePathWithExt, ".mdx");
|
|
643
|
-
const entries = categoryMetaEntriesMap.get(result.categoryKey) || [];
|
|
644
|
-
entries.push({
|
|
645
|
-
name: baseName,
|
|
646
|
-
label: result.label
|
|
647
|
-
});
|
|
648
|
-
categoryMetaEntriesMap.set(result.categoryKey, entries);
|
|
649
|
-
}
|
|
650
|
-
const metaSnapshotsToUpdate = (yield* Effect.forEach(Array.from(categoryMetaEntriesMap.entries()), ([categoryKey, entries]) => Effect.gen(function* () {
|
|
651
|
-
const categoryConfig = categories[categoryKey];
|
|
652
|
-
if (!categoryConfig || entries.length === 0) return null;
|
|
653
|
-
entries.sort((a, b) => a.label.localeCompare(b.label));
|
|
654
|
-
const categoryMeta = entries.map((entry) => ({
|
|
655
|
-
type: "file",
|
|
656
|
-
name: entry.name,
|
|
657
|
-
label: entry.label
|
|
658
|
-
}));
|
|
473
|
+
const metaSnapshotsToUpdate = (yield* Effect.forEach(navTree.groups, (group) => Effect.gen(function* () {
|
|
474
|
+
const categoryConfig = group.category;
|
|
659
475
|
const categoryMetaPath = path.join(resolvedOutputDir, categoryConfig.folderName, "_meta.json");
|
|
660
476
|
const relPath = path.join(categoryConfig.folderName, "_meta.json");
|
|
661
|
-
const content =
|
|
477
|
+
const content = renderCategoryMeta(group);
|
|
662
478
|
const contentHash = hashContent(content);
|
|
663
479
|
const oldSnapshot = existingSnapshots.get(relPath);
|
|
664
480
|
let isUnchanged = false;
|
|
@@ -829,6 +645,7 @@ function buildPipelineForApi(input) {
|
|
|
829
645
|
resolvedOutputDir: input.resolvedOutputDir,
|
|
830
646
|
...input.suppressExampleErrors != null ? { suppressExampleErrors: input.suppressExampleErrors } : {},
|
|
831
647
|
...input.llmsPlugin != null ? { llmsPlugin: input.llmsPlugin } : {},
|
|
648
|
+
linker: input.linker,
|
|
832
649
|
...input.docsRoot !== void 0 ? { docsRoot: input.docsRoot } : {},
|
|
833
650
|
...input.siteUrl != null ? { siteUrl: input.siteUrl } : {},
|
|
834
651
|
...input.ogImage != null ? { ogImage: input.ogImage } : {},
|
|
@@ -849,4 +666,4 @@ function buildPipelineForApi(input) {
|
|
|
849
666
|
}
|
|
850
667
|
|
|
851
668
|
//#endregion
|
|
852
|
-
export { buildPipelineForApi, cleanupAndCommit,
|
|
669
|
+
export { buildPipelineForApi, cleanupAndCommit, generateSinglePage, prepareWorkItems$1 as prepareWorkItems, writeMetadata, writeSingleFile };
|
package/config-helpers.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { normalizeBaseRoute } from "./path-derivation.js";
|
|
2
1
|
import { SyncDiscoveryLayer } from "./sync-node-fs.js";
|
|
3
2
|
import fsSync from "node:fs";
|
|
4
3
|
import path from "node:path";
|
|
4
|
+
import { normalizeBaseRoute } from "@tsdoctor/pages";
|
|
5
5
|
import { Effect, Result } from "effect";
|
|
6
6
|
import { discoverBundle } from "@tsdoctor/bundle";
|
|
7
7
|
|