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