rspress-plugin-api-extractor 0.8.8 → 0.9.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/README.md +11 -11
- package/api-extracted-package.js +1 -1
- package/build-program.js +3 -3
- package/build-stages.js +30 -31
- package/config-helpers.js +61 -21
- package/errors.js +1 -7
- package/frontmatter.js +149 -0
- package/index.d.ts +11 -2
- package/layers/ConfigServiceLive.js +53 -39
- package/layers/TypeRegistryServiceLive.js +11 -6
- package/markdown/helpers.js +40 -69
- package/markdown/index.js +2 -2
- package/markdown/page-generators/class-page.js +40 -42
- package/markdown/page-generators/enum-page.js +15 -15
- package/markdown/page-generators/function-page.js +18 -20
- package/markdown/page-generators/interface-page.js +41 -43
- package/markdown/page-generators/namespace-page.js +22 -24
- package/markdown/page-generators/type-alias-page.js +14 -16
- package/markdown/page-generators/variable-page.js +14 -16
- package/markdown/prose-linker.js +22 -0
- package/model-loader.js +59 -113
- package/package.json +14 -8
- package/plugin.js +1 -5
- package/runtime/components/EnumMembersTable/index.css +18 -18
- package/runtime/components/EnumMembersTable/index.module.js +3 -3
- package/runtime/components/ExampleBlock/index.css +2 -2
- package/runtime/components/ExampleBlock/index.module.js +2 -2
- package/runtime/components/MemberSignature/index.css +6 -6
- package/runtime/components/MemberSignature/index.module.js +4 -4
- package/runtime/components/ParametersTable/index.css +19 -19
- package/runtime/components/ParametersTable/index.module.js +3 -3
- package/runtime/components/SignatureBlock/index.css +6 -6
- package/runtime/components/SignatureBlock/index.module.js +4 -4
- package/runtime/components/SignatureCode/index.css +9 -9
- package/runtime/components/SignatureCode/index.module.js +3 -3
- package/runtime/components/SignatureToolbar/index.css +18 -18
- package/runtime/components/SignatureToolbar/index.module.js +7 -7
- package/runtime/components/buttons/index.css +5 -5
- package/runtime/components/buttons/index.module.js +2 -2
- package/shiki-transformer.js +3 -3
- package/sync-node-fs.js +80 -0
- package/tsdoc-metadata.json +1 -1
- package/twoslash-transformer.js +1 -1
- package/content-hash.js +0 -79
- package/formatter.js +0 -69
- package/layers/SnapshotServiceLive.js +0 -92
- package/loader.js +0 -200
- package/markdown/cross-linker.js +0 -157
- package/migrations/001_create_snapshots.js +0 -25
- package/multi-entry-resolver.js +0 -70
- package/route-collisions.js +0 -44
- package/services/SnapshotService.js +0 -7
- package/synthetic-bases.js +0 -74
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { hashContent } from "../content-hash.js";
|
|
2
1
|
import { PluginEvent } from "../observability/events.js";
|
|
3
2
|
import { emit, wantsLevel } from "../observability/EventBus.js";
|
|
4
3
|
import { BuildMetrics } from "./build-metrics.js";
|
|
@@ -14,13 +13,14 @@ import { CategoryResolver } from "../category-resolver.js";
|
|
|
14
13
|
import { ConfigValidationError } from "../errors.js";
|
|
15
14
|
import { HideCutLinesTransformer, MemberFormatTransformer } from "../hide-cut-transformer.js";
|
|
16
15
|
import { DEFAULT_SHIKI_THEMES } from "../markdown/shiki-utils.js";
|
|
17
|
-
import {
|
|
16
|
+
import { loadApiModel, loadPackageJson, loadVersionModel } from "../model-loader.js";
|
|
18
17
|
import { DEFAULT_CATEGORIES } from "../schemas/config.js";
|
|
19
18
|
import "../schemas/index.js";
|
|
20
19
|
import { ConfigService } from "../services/ConfigService.js";
|
|
21
20
|
import { PathDerivationService } from "../services/PathDerivationService.js";
|
|
22
21
|
import { TypeRegistryService } from "../services/TypeRegistryService.js";
|
|
23
22
|
import path from "node:path";
|
|
23
|
+
import { hashContent } from "@tsdoctor/snapshot";
|
|
24
24
|
import { Effect, Layer, Metric } from "effect";
|
|
25
25
|
import os from "node:os";
|
|
26
26
|
import { createHighlighter } from "shiki";
|
|
@@ -172,42 +172,56 @@ function ConfigServiceLive(options, shikiCrossLinker, buildId = "", resolvedThre
|
|
|
172
172
|
let firstApiTsconfig;
|
|
173
173
|
let firstApiCompilerOptions;
|
|
174
174
|
/**
|
|
175
|
+
* Emit a typed ModelLoadFailed event for a failed model load, then
|
|
176
|
+
* convert the typed failure to a defect — a missing or unparsable
|
|
177
|
+
* model remains fatal to the build, exactly as before, but the
|
|
178
|
+
* event now rides the error channel instead of a sync-island seam.
|
|
179
|
+
*/
|
|
180
|
+
const withModelLoadEvents = (self) => self.pipe(Effect.tapError((error) => emit(PluginEvent.ModelLoadFailed({
|
|
181
|
+
ctx: { buildId },
|
|
182
|
+
level: "error",
|
|
183
|
+
modelPath: "modelPath" in error ? error.modelPath : "<loader function>",
|
|
184
|
+
reason: error.message
|
|
185
|
+
}))), Effect.orDie);
|
|
186
|
+
/**
|
|
175
187
|
* Helper to process a single API model (shared by single and multi modes).
|
|
176
188
|
*/
|
|
177
|
-
const processSimpleApi = (api, model, outputDir, fullRoute, wantTrace) => Effect.
|
|
178
|
-
const { apiPackage, source: loaderSource } =
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
189
|
+
const processSimpleApi = (api, model, outputDir, fullRoute, wantTrace) => Effect.gen(function* () {
|
|
190
|
+
const { apiPackage, source: loaderSource } = yield* withModelLoadEvents(loadApiModel(model));
|
|
191
|
+
return yield* Effect.promise(async () => {
|
|
192
|
+
const resolvedCategories = categoryResolver.resolveCategoryConfig(pluginDefaults, api.categories);
|
|
193
|
+
const resolvedSource = categoryResolver.resolveSourceConfig(api.source, loaderSource);
|
|
194
|
+
const resolvedLlms = mergeLlmsPluginConfig(options.llmsPlugin, api.llmsPlugin);
|
|
195
|
+
const packageJson = api.packageJson ? await loadPackageJson(api.packageJson) : void 0;
|
|
196
|
+
validateExternalPackages(api.externalPackages, packageJson);
|
|
197
|
+
const externalPackages = api.externalPackages || extractAutoDetectedPackages(packageJson, api.autoDetectDependencies);
|
|
198
|
+
if (externalPackages && externalPackages.length > 0) Effect.runSync(Metric.update(BuildMetrics.externalPackagesTotal, externalPackages.length));
|
|
199
|
+
const vfs = ApiExtractedPackage.fromPackage(apiPackage, api.packageName).generateVfs();
|
|
200
|
+
const vfsPayloads = prependImportsToVfs(vfs, apiPackage, api.packageName, wantTrace);
|
|
201
|
+
const resolvedOgImage = api.ogImage ?? options.ogImage;
|
|
202
|
+
const resolvedTheme = normalizeThemeConfig(api.theme);
|
|
203
|
+
return {
|
|
204
|
+
vfs,
|
|
205
|
+
vfsPayloads,
|
|
206
|
+
externalPackages: externalPackages || [],
|
|
207
|
+
config: {
|
|
208
|
+
apiPackage,
|
|
209
|
+
packageName: api.packageName,
|
|
210
|
+
...api.name != null ? { apiName: api.name } : {},
|
|
211
|
+
outputDir,
|
|
212
|
+
baseRoute: fullRoute,
|
|
213
|
+
categories: resolvedCategories,
|
|
214
|
+
...resolvedSource != null ? { source: resolvedSource } : {},
|
|
215
|
+
...packageJson != null ? { packageJson } : {},
|
|
216
|
+
...resolvedLlms != null ? { llmsPlugin: resolvedLlms } : {},
|
|
217
|
+
...options.siteUrl != null ? { siteUrl: options.siteUrl } : {},
|
|
218
|
+
...resolvedOgImage != null ? { ogImage: resolvedOgImage } : {},
|
|
219
|
+
docsDir: path.dirname(outputDir),
|
|
220
|
+
...docsRoot != null ? { docsRoot } : {},
|
|
221
|
+
...resolvedTheme != null ? { theme: resolvedTheme } : {}
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
});
|
|
211
225
|
});
|
|
212
226
|
yield* withPhase("modelLoad", { buildId }, Effect.gen(function* () {
|
|
213
227
|
if (options.api) {
|
|
@@ -233,14 +247,14 @@ function ConfigServiceLive(options, shikiCrossLinker, buildId = "", resolvedThre
|
|
|
233
247
|
externalPackages: [],
|
|
234
248
|
config: null
|
|
235
249
|
};
|
|
250
|
+
const versionConfig = isVersionConfig(versionValue) ? versionValue : { model: versionValue };
|
|
251
|
+
const { apiPackage, packageJson: versionPackageJson, categories: versionCategories, source: versionSource, externalPackages: versionExternalPackages, autoDetectDependencies: versionAutoDetectDependencies, llmsPlugin: versionLlms, ogImage: versionOgImage } = yield* withModelLoadEvents(loadVersionModel(versionConfig));
|
|
236
252
|
return yield* Effect.promise(async () => {
|
|
237
|
-
const versionConfig = isVersionConfig(versionValue) ? versionValue : { model: versionValue };
|
|
238
|
-
const { apiPackage, packageJson: versionPackageJson, categories: versionCategories, source: versionSource, externalPackages: versionExternalPackages, autoDetectDependencies: versionAutoDetectDependencies, llmsPlugin: versionLlms, ogImage: versionOgImage } = await ApiModelLoader.loadVersionModel(versionConfig);
|
|
239
253
|
Effect.runSync(Metric.update(BuildMetrics.apiVersionsLoaded, 1));
|
|
240
254
|
const resolvedCategories = categoryResolver.resolveCategoryConfig(pluginDefaults, api.categories, versionCategories);
|
|
241
255
|
const resolvedSource = categoryResolver.resolveSourceConfig(api.source, versionSource);
|
|
242
256
|
const resolvedLlms = mergeLlmsPluginConfig(options.llmsPlugin, api.llmsPlugin, versionLlms);
|
|
243
|
-
const packageJson = versionPackageJson || (api.packageJson ? await
|
|
257
|
+
const packageJson = versionPackageJson || (api.packageJson ? await loadPackageJson(api.packageJson) : void 0);
|
|
244
258
|
validateExternalPackages(versionExternalPackages || api.externalPackages, packageJson);
|
|
245
259
|
const autoDetectOptions = versionAutoDetectDependencies || api.autoDetectDependencies;
|
|
246
260
|
const externalPackages = versionExternalPackages || api.externalPackages || extractAutoDetectedPackages(packageJson, autoDetectOptions);
|
|
@@ -5,13 +5,13 @@ import { TypeRegistryError } from "../errors.js";
|
|
|
5
5
|
import { TypeRegistryService } from "../services/TypeRegistryService.js";
|
|
6
6
|
import { NodeFileSystem, NodeHttpClient } from "@effect/platform-node";
|
|
7
7
|
import { Duration, Effect, Layer, Path } from "effect";
|
|
8
|
-
import { PackageFetcher, PackageSpec, RegistryObserver, TypeCache, TypeRegistry } from "
|
|
8
|
+
import { PackageFetcher, PackageSpec, RegistryObserver, TypeCache, TypeRegistry } from "@tsdoctor/registry";
|
|
9
9
|
import { Cache } from "@effected/store";
|
|
10
10
|
import { AppDirs, Xdg } from "@effected/xdg";
|
|
11
11
|
|
|
12
12
|
//#region src/layers/TypeRegistryServiceLive.ts
|
|
13
13
|
/**
|
|
14
|
-
* Forward
|
|
14
|
+
* Forward @tsdoctor/registry's typed `RegistryEvent`s to the plugin's Effect
|
|
15
15
|
* logger. Since v1 the library emits no logs of its own — observers are the only
|
|
16
16
|
* diagnostic surface — so this restores the build output and routes it through
|
|
17
17
|
* the plugin's configured log level/format (a single source, no duplication).
|
|
@@ -102,7 +102,7 @@ const RegistryObserverLayer = Layer.succeed(RegistryObserver, { emit: (event) =>
|
|
|
102
102
|
}
|
|
103
103
|
} });
|
|
104
104
|
/**
|
|
105
|
-
*
|
|
105
|
+
* @tsdoctor/registry composes at the edge: the library ships no platform
|
|
106
106
|
* layer of its own, so the plugin wires FileSystem/Path, the XDG directories,
|
|
107
107
|
* the sqlite metadata Cache and the HTTP client here.
|
|
108
108
|
*
|
|
@@ -110,8 +110,13 @@ const RegistryObserverLayer = Layer.succeed(RegistryObserver, { emit: (event) =>
|
|
|
110
110
|
* v4 layer memoization discipline.
|
|
111
111
|
*/
|
|
112
112
|
const PlatformLive = Layer.mergeAll(NodeFileSystem.layer, Path.layer);
|
|
113
|
-
/**
|
|
114
|
-
|
|
113
|
+
/**
|
|
114
|
+
* XDG app directories under the tsdoctor-wide namespace. Renamed from the
|
|
115
|
+
* legacy "type-registry-effect" namespace in phase 2 per the resolved identity
|
|
116
|
+
* decision (see tsdoctor-package-architecture.md) — a deliberate one-time
|
|
117
|
+
* on-disk cache invalidation: existing caches go cold and refetch.
|
|
118
|
+
*/
|
|
119
|
+
const AppDirsLive = AppDirs.layer({ namespace: "tsdoctor" }).pipe(Layer.provide(Layer.mergeAll(Xdg.layer, PlatformLive)));
|
|
115
120
|
/** Metadata plane: a sqlite-backed `@effected/store` Cache rooted in the XDG cache dir. */
|
|
116
121
|
const MetadataCacheLive = Layer.unwrap(Effect.gen(function* () {
|
|
117
122
|
const appDirs = yield* AppDirs;
|
|
@@ -126,7 +131,7 @@ const MetadataCacheLive = Layer.unwrap(Effect.gen(function* () {
|
|
|
126
131
|
*/
|
|
127
132
|
const RegistryLayer = TypeRegistry.layer.pipe(Layer.provideMerge(Layer.mergeAll(TypeCache.layerXdg(), PackageFetcher.layer)), Layer.provideMerge(RegistryObserverLayer), Layer.provide(Layer.mergeAll(MetadataCacheLive, AppDirsLive, PlatformLive, NodeHttpClient.layerUndici)));
|
|
128
133
|
/**
|
|
129
|
-
* TypeRegistryServiceLive: uses
|
|
134
|
+
* TypeRegistryServiceLive: uses @tsdoctor/registry Effect programs directly.
|
|
130
135
|
*/
|
|
131
136
|
const TypeRegistryServiceLive = Layer.succeed(TypeRegistryService, {
|
|
132
137
|
resolveVersions: (packages) => Effect.gen(function* () {
|
package/markdown/helpers.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { emitFrontmatterBlock } from "../frontmatter.js";
|
|
1
2
|
import { classifyCutDirective, isTwoslashDirective } from "../twoslash-patterns.js";
|
|
2
3
|
import { formatCode } from "../prettier-formatter.js";
|
|
3
4
|
import { TypeReferenceExtractor } from "../type-reference-extractor.js";
|
|
@@ -88,8 +89,21 @@ function sanitizeId(displayName, prefix = "") {
|
|
|
88
89
|
* escapeYamlString("@pkg/name。:"); // "\"@pkg/name。:\""
|
|
89
90
|
* ```
|
|
90
91
|
*/
|
|
92
|
+
/**
|
|
93
|
+
* Normalize a string for use as a YAML frontmatter value: collapse newlines
|
|
94
|
+
* and repeated whitespace to single spaces and trim.
|
|
95
|
+
*
|
|
96
|
+
* This is the cleaning half of the former hand-rolled YAML escaping. It is
|
|
97
|
+
* applied to every frontmatter value BEFORE serialization so the parsed data
|
|
98
|
+
* (and therefore the snapshot frontmatter hash — see `@tsdoctor/snapshot`)
|
|
99
|
+
* is byte-identical to what the previous emitter produced; the quoting half
|
|
100
|
+
* is now owned by the real YAML emitter in `../frontmatter.ts`.
|
|
101
|
+
*/
|
|
102
|
+
function cleanYamlValue(value) {
|
|
103
|
+
return value.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
104
|
+
}
|
|
91
105
|
function escapeYamlString(value) {
|
|
92
|
-
const cleaned = value
|
|
106
|
+
const cleaned = cleanYamlValue(value);
|
|
93
107
|
if (/["':#|>&*!%@`[\]{},?-]/.test(cleaned) || /[\u0080-\uFFFF]/.test(cleaned) || /^(true|false|null|~|yes|no|on|off)$/i.test(cleaned) || /^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(cleaned)) return `"${cleaned.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
94
108
|
return cleaned;
|
|
95
109
|
}
|
|
@@ -163,84 +177,41 @@ function buildPageTitle(entityName, singularName, apiName) {
|
|
|
163
177
|
* );
|
|
164
178
|
* // Returns:
|
|
165
179
|
* // ---
|
|
166
|
-
* // title: MyClass | Class | API | My Package
|
|
167
|
-
* // description: A utility class for...
|
|
180
|
+
* // title: "MyClass | Class | API | My Package"
|
|
181
|
+
* // description: "A utility class for..."
|
|
168
182
|
* // ---
|
|
169
183
|
* ```
|
|
170
184
|
*/
|
|
171
185
|
function generateFrontmatter(entityName, description, singularName, apiName, ogMetadata) {
|
|
172
186
|
const title = buildPageTitle(entityName, singularName, apiName);
|
|
187
|
+
const meta = (property, content) => ["meta", {
|
|
188
|
+
property,
|
|
189
|
+
content: cleanYamlValue(content)
|
|
190
|
+
}];
|
|
173
191
|
const headEntries = [];
|
|
174
192
|
if (ogMetadata) {
|
|
175
|
-
headEntries.push(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}]);
|
|
179
|
-
headEntries.push(["meta", {
|
|
180
|
-
property: "og:type",
|
|
181
|
-
content: ogMetadata.ogType
|
|
182
|
-
}]);
|
|
183
|
-
headEntries.push(["meta", {
|
|
184
|
-
property: "og:description",
|
|
185
|
-
content: ogMetadata.description
|
|
186
|
-
}]);
|
|
193
|
+
headEntries.push(meta("og:url", `${ogMetadata.siteUrl}${ogMetadata.pageRoute}`));
|
|
194
|
+
headEntries.push(meta("og:type", ogMetadata.ogType));
|
|
195
|
+
headEntries.push(meta("og:description", ogMetadata.description));
|
|
187
196
|
if (ogMetadata.ogImage) {
|
|
188
|
-
headEntries.push(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (ogMetadata.ogImage.
|
|
193
|
-
|
|
194
|
-
content: ogMetadata.ogImage.secureUrl
|
|
195
|
-
}]);
|
|
196
|
-
if (ogMetadata.ogImage.type) headEntries.push(["meta", {
|
|
197
|
-
property: "og:image:type",
|
|
198
|
-
content: ogMetadata.ogImage.type
|
|
199
|
-
}]);
|
|
200
|
-
if (ogMetadata.ogImage.width) headEntries.push(["meta", {
|
|
201
|
-
property: "og:image:width",
|
|
202
|
-
content: String(ogMetadata.ogImage.width)
|
|
203
|
-
}]);
|
|
204
|
-
if (ogMetadata.ogImage.height) headEntries.push(["meta", {
|
|
205
|
-
property: "og:image:height",
|
|
206
|
-
content: String(ogMetadata.ogImage.height)
|
|
207
|
-
}]);
|
|
208
|
-
if (ogMetadata.ogImage.alt) headEntries.push(["meta", {
|
|
209
|
-
property: "og:image:alt",
|
|
210
|
-
content: ogMetadata.ogImage.alt
|
|
211
|
-
}]);
|
|
197
|
+
headEntries.push(meta("og:image", ogMetadata.ogImage.url));
|
|
198
|
+
if (ogMetadata.ogImage.secureUrl) headEntries.push(meta("og:image:secure_url", ogMetadata.ogImage.secureUrl));
|
|
199
|
+
if (ogMetadata.ogImage.type) headEntries.push(meta("og:image:type", ogMetadata.ogImage.type));
|
|
200
|
+
if (ogMetadata.ogImage.width) headEntries.push(meta("og:image:width", String(ogMetadata.ogImage.width)));
|
|
201
|
+
if (ogMetadata.ogImage.height) headEntries.push(meta("og:image:height", String(ogMetadata.ogImage.height)));
|
|
202
|
+
if (ogMetadata.ogImage.alt) headEntries.push(meta("og:image:alt", ogMetadata.ogImage.alt));
|
|
212
203
|
}
|
|
213
|
-
headEntries.push(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
headEntries.push(["meta", {
|
|
218
|
-
property: "article:modified_time",
|
|
219
|
-
content: ogMetadata.modifiedTime
|
|
220
|
-
}]);
|
|
221
|
-
headEntries.push(["meta", {
|
|
222
|
-
property: "article:section",
|
|
223
|
-
content: ogMetadata.section
|
|
224
|
-
}]);
|
|
225
|
-
for (const tag of ogMetadata.tags) headEntries.push(["meta", {
|
|
226
|
-
property: "article:tag",
|
|
227
|
-
content: tag
|
|
228
|
-
}]);
|
|
204
|
+
headEntries.push(meta("article:published_time", ogMetadata.publishedTime));
|
|
205
|
+
headEntries.push(meta("article:modified_time", ogMetadata.modifiedTime));
|
|
206
|
+
headEntries.push(meta("article:section", ogMetadata.section));
|
|
207
|
+
for (const tag of ogMetadata.tags) headEntries.push(meta("article:tag", tag));
|
|
229
208
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
frontmatter += " - ";
|
|
237
|
-
const attrPairs = Object.entries(attrs).map(([key, value]) => `${key}: ${escapeYamlString(value)}`);
|
|
238
|
-
frontmatter += attrPairs.join("\n ");
|
|
239
|
-
frontmatter += "\n";
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
frontmatter += "---\n\n";
|
|
243
|
-
return frontmatter;
|
|
209
|
+
const data = {
|
|
210
|
+
title: cleanYamlValue(title),
|
|
211
|
+
description: cleanYamlValue(description)
|
|
212
|
+
};
|
|
213
|
+
if (headEntries.length > 0) data.head = headEntries;
|
|
214
|
+
return emitFrontmatterBlock(data);
|
|
244
215
|
}
|
|
245
216
|
/**
|
|
246
217
|
* Strip Twoslash directives from code for display purposes.
|
package/markdown/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { linkProse, setProseLinker } from "./prose-linker.js";
|
|
2
2
|
import { ClassPageGenerator } from "./page-generators/class-page.js";
|
|
3
3
|
import { EnumPageGenerator } from "./page-generators/enum-page.js";
|
|
4
4
|
import { FunctionPageGenerator } from "./page-generators/function-page.js";
|
|
@@ -8,4 +8,4 @@ import { NamespacePageGenerator } from "./page-generators/namespace-page.js";
|
|
|
8
8
|
import { TypeAliasPageGenerator } from "./page-generators/type-alias-page.js";
|
|
9
9
|
import { VariablePageGenerator } from "./page-generators/variable-page.js";
|
|
10
10
|
|
|
11
|
-
export { ClassPageGenerator, EnumPageGenerator, FunctionPageGenerator, InterfacePageGenerator, MainIndexPageGenerator, NamespacePageGenerator, TypeAliasPageGenerator, VariablePageGenerator,
|
|
11
|
+
export { ClassPageGenerator, EnumPageGenerator, FunctionPageGenerator, InterfacePageGenerator, MainIndexPageGenerator, NamespacePageGenerator, TypeAliasPageGenerator, VariablePageGenerator, setProseLinker };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { ApiParser } from "../../loader.js";
|
|
2
1
|
import { TypeReferenceExtractor } from "../../type-reference-extractor.js";
|
|
3
2
|
import { escapeMdxGenerics, formatExampleCode, generateAvailableFrom, generateFrontmatter, prepareExampleCode, prependHiddenImports, sanitizeId, stripTwoslashDirectives } from "../helpers.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { linkProse } from "../prose-linker.js";
|
|
4
|
+
import { ApiItems, Signature, Tsdoc } from "@tsdoctor/model";
|
|
6
5
|
|
|
7
6
|
//#region src/markdown/page-generators/class-page.ts
|
|
8
7
|
/**
|
|
@@ -32,9 +31,9 @@ import { TypeSignatureFormatter } from "../../formatter.js";
|
|
|
32
31
|
*
|
|
33
32
|
* **Relationships:**
|
|
34
33
|
* - Created and invoked by {@link ApiExtractorPlugin} during page generation
|
|
35
|
-
* - Uses
|
|
36
|
-
* - Uses
|
|
37
|
-
* - Uses
|
|
34
|
+
* - Uses `Signature.format` from `@tsdoctor/model` for formatting type signatures
|
|
35
|
+
* - Uses the `Tsdoc` / `ApiItems` modules from `@tsdoctor/model` for extracting documentation
|
|
36
|
+
* - Uses the per-build prose linker (`linkProse`) for adding type reference links
|
|
38
37
|
*
|
|
39
38
|
* @example
|
|
40
39
|
* ```ts
|
|
@@ -56,7 +55,6 @@ import { TypeSignatureFormatter } from "../../formatter.js";
|
|
|
56
55
|
* @see {@link FunctionPageGenerator} for function documentation
|
|
57
56
|
*/
|
|
58
57
|
var ClassPageGenerator = class {
|
|
59
|
-
typeFormatter = new TypeSignatureFormatter();
|
|
60
58
|
/**
|
|
61
59
|
* Generate a markdown page for a class
|
|
62
60
|
*
|
|
@@ -65,22 +63,22 @@ var ClassPageGenerator = class {
|
|
|
65
63
|
async generate(apiClass, baseRoute, packageName, singularName, apiScope, apiName, sourceConfig, suppressExampleErrors, llmsPlugin, availableFrom, syntheticBase) {
|
|
66
64
|
const shouldSuppressErrors = suppressExampleErrors ?? true;
|
|
67
65
|
const name = apiClass.displayName;
|
|
68
|
-
const summary =
|
|
69
|
-
const releaseTag =
|
|
66
|
+
const summary = Tsdoc.summary(apiClass) || "No description available.";
|
|
67
|
+
const releaseTag = Tsdoc.releaseTag(apiClass);
|
|
70
68
|
let content = generateFrontmatter(name, summary, singularName, apiName);
|
|
71
69
|
content += `import { SourceCode } from "@rspress/core/theme";\n`;
|
|
72
70
|
content += `import { ParametersTable } from "rspress-plugin-api-extractor/runtime";\n`;
|
|
73
71
|
content += `import { ApiSignature, ApiMember, ApiExample } from "rspress-plugin-api-extractor/runtime";\n\n`;
|
|
74
72
|
content += `# ${name}\n\n`;
|
|
75
|
-
const deprecation =
|
|
73
|
+
const deprecation = Tsdoc.deprecation(apiClass);
|
|
76
74
|
if (deprecation) {
|
|
77
|
-
const message = escapeMdxGenerics(
|
|
75
|
+
const message = escapeMdxGenerics(linkProse(deprecation.message));
|
|
78
76
|
content += `> ⚠️ **Deprecated:** ${message}\n\n`;
|
|
79
77
|
}
|
|
80
78
|
if (releaseTag !== "Public") content += `\`${releaseTag}\`\n\n`;
|
|
81
79
|
content += `${summary}\n\n`;
|
|
82
80
|
content += generateAvailableFrom(packageName, availableFrom);
|
|
83
|
-
const sourceLink =
|
|
81
|
+
const sourceLink = ApiItems.sourceLink(apiClass, sourceConfig);
|
|
84
82
|
if (sourceLink) {
|
|
85
83
|
content += `<div className="api-docs-toolbar">\n`;
|
|
86
84
|
content += ` <div className="api-docs-toolbar-left">\n`;
|
|
@@ -100,21 +98,21 @@ var ClassPageGenerator = class {
|
|
|
100
98
|
if (constructors.length > 0) {
|
|
101
99
|
content += `## Constructors\n\n`;
|
|
102
100
|
for (const ctor of constructors) {
|
|
103
|
-
const ctorSummary =
|
|
101
|
+
const ctorSummary = Tsdoc.summary(ctor);
|
|
104
102
|
const ctorId = sanitizeId("constructor");
|
|
105
103
|
const ctorItem = ctor;
|
|
106
|
-
const params =
|
|
104
|
+
const params = Tsdoc.params(ctor);
|
|
107
105
|
const hasParameters = params.length > 0;
|
|
108
106
|
if (ctorItem.excerpt?.text) {
|
|
109
|
-
const memberSignature =
|
|
107
|
+
const memberSignature = Signature.format(ctorItem.excerpt).trim();
|
|
110
108
|
const skeletonWithContext = this.generateClassMemberWithContext(apiClass, ctor, packageName);
|
|
111
|
-
const summaryMd = ctorSummary ? escapeMdxGenerics(
|
|
109
|
+
const summaryMd = ctorSummary ? escapeMdxGenerics(linkProse(ctorSummary)) : void 0;
|
|
112
110
|
content += `<ApiMember code={${JSON.stringify(memberSignature)}} source={${JSON.stringify(skeletonWithContext)}} apiScope={${JSON.stringify(apiScope)}} memberName="constructor"${summaryMd ? ` summary={${JSON.stringify(summaryMd)}}` : ""} id={${JSON.stringify(ctorId)}} hasParameters={${hasParameters}} />\n\n`;
|
|
113
111
|
}
|
|
114
112
|
if (hasParameters) content += `<ParametersTable parameters={${JSON.stringify(params.map((p) => ({
|
|
115
113
|
name: p.name,
|
|
116
114
|
type: p.type,
|
|
117
|
-
description:
|
|
115
|
+
description: linkProse(p.description)
|
|
118
116
|
})))}} />\n\n`;
|
|
119
117
|
}
|
|
120
118
|
}
|
|
@@ -138,15 +136,15 @@ var ClassPageGenerator = class {
|
|
|
138
136
|
if (propList.length === 0) return;
|
|
139
137
|
content += `## ${title}\n\n`;
|
|
140
138
|
for (const prop of propList) {
|
|
141
|
-
const propSummary =
|
|
139
|
+
const propSummary = Tsdoc.summary(prop);
|
|
142
140
|
const baseName = sanitizeId(prop.displayName);
|
|
143
141
|
const prefix = prefixMap.get(baseName) || "";
|
|
144
142
|
const propId = sanitizeId(prop.displayName, prefix);
|
|
145
143
|
const propItem = prop;
|
|
146
144
|
if (propItem.excerpt?.text) {
|
|
147
|
-
const memberSignature =
|
|
145
|
+
const memberSignature = Signature.format(propItem.excerpt).trim();
|
|
148
146
|
const skeletonWithContext = this.generateClassMemberWithContext(apiClass, prop, packageName);
|
|
149
|
-
const summaryMd = propSummary ? escapeMdxGenerics(
|
|
147
|
+
const summaryMd = propSummary ? escapeMdxGenerics(linkProse(propSummary)) : void 0;
|
|
150
148
|
content += `<ApiMember code={${JSON.stringify(memberSignature)}} source={${JSON.stringify(skeletonWithContext)}} apiScope={${JSON.stringify(apiScope)}} memberName={${JSON.stringify(prop.displayName)}}${summaryMd ? ` summary={${JSON.stringify(summaryMd)}}` : ""} id={${JSON.stringify(propId)}} />\n\n`;
|
|
151
149
|
}
|
|
152
150
|
}
|
|
@@ -155,27 +153,27 @@ var ClassPageGenerator = class {
|
|
|
155
153
|
if (methodList.length === 0) return;
|
|
156
154
|
content += `## ${title}\n\n`;
|
|
157
155
|
for (const method of methodList) {
|
|
158
|
-
const methodSummary =
|
|
156
|
+
const methodSummary = Tsdoc.summary(method);
|
|
159
157
|
const baseName = sanitizeId(method.displayName);
|
|
160
158
|
const prefix = prefixMap.get(baseName) || "";
|
|
161
159
|
const methodId = sanitizeId(method.displayName, prefix);
|
|
162
160
|
const methodItem = method;
|
|
163
|
-
const params =
|
|
161
|
+
const params = Tsdoc.params(method);
|
|
164
162
|
const hasParameters = params.length > 0;
|
|
165
163
|
if (methodItem.excerpt?.text) {
|
|
166
|
-
const memberSignature =
|
|
164
|
+
const memberSignature = Signature.format(methodItem.excerpt).trim();
|
|
167
165
|
const skeletonWithContext = this.generateClassMemberWithContext(apiClass, method, packageName);
|
|
168
|
-
const summaryMd = methodSummary ? escapeMdxGenerics(
|
|
166
|
+
const summaryMd = methodSummary ? escapeMdxGenerics(linkProse(methodSummary)) : void 0;
|
|
169
167
|
content += `<ApiMember code={${JSON.stringify(memberSignature)}} source={${JSON.stringify(skeletonWithContext)}} apiScope={${JSON.stringify(apiScope)}} memberName={${JSON.stringify(method.displayName)}}${summaryMd ? ` summary={${JSON.stringify(summaryMd)}}` : ""} id={${JSON.stringify(methodId)}} hasParameters={${hasParameters}} />\n\n`;
|
|
170
168
|
}
|
|
171
169
|
if (hasParameters) content += `<ParametersTable parameters={${JSON.stringify(params.map((p) => ({
|
|
172
170
|
name: p.name,
|
|
173
171
|
type: p.type,
|
|
174
|
-
description:
|
|
172
|
+
description: linkProse(p.description)
|
|
175
173
|
})))}} />\n\n`;
|
|
176
|
-
const returns =
|
|
174
|
+
const returns = Tsdoc.returns(method);
|
|
177
175
|
if (returns) {
|
|
178
|
-
const description = escapeMdxGenerics(
|
|
176
|
+
const description = escapeMdxGenerics(linkProse(returns.description));
|
|
179
177
|
content += `**Returns:** ${description}\n\n`;
|
|
180
178
|
}
|
|
181
179
|
}
|
|
@@ -185,7 +183,7 @@ var ClassPageGenerator = class {
|
|
|
185
183
|
await renderProperties("Properties", instanceProperties);
|
|
186
184
|
await renderMethods("Getters & Setters", grouped.getters);
|
|
187
185
|
await renderMethods("Methods", grouped.instanceMethods);
|
|
188
|
-
const examples =
|
|
186
|
+
const examples = Tsdoc.examples(apiClass);
|
|
189
187
|
if (examples.length > 0) {
|
|
190
188
|
content += `## Examples\n\n`;
|
|
191
189
|
for (const example of examples) {
|
|
@@ -200,11 +198,11 @@ var ClassPageGenerator = class {
|
|
|
200
198
|
} else content += `\`\`\`${prepared.language}\n${formattedCode}\n\`\`\`\n\n`;
|
|
201
199
|
}
|
|
202
200
|
}
|
|
203
|
-
const seeReferences =
|
|
201
|
+
const seeReferences = Tsdoc.seeReferences(apiClass);
|
|
204
202
|
if (seeReferences.length > 0) {
|
|
205
203
|
content += `## See Also\n\n`;
|
|
206
204
|
for (const reference of seeReferences) {
|
|
207
|
-
const refText = escapeMdxGenerics(
|
|
205
|
+
const refText = escapeMdxGenerics(linkProse(reference.text));
|
|
208
206
|
content += `- ${refText}\n`;
|
|
209
207
|
}
|
|
210
208
|
content += `\n`;
|
|
@@ -220,15 +218,15 @@ var ClassPageGenerator = class {
|
|
|
220
218
|
* `Foo_base` variable TypeScript emits for `Schema.Class`-style patterns).
|
|
221
219
|
*
|
|
222
220
|
* The `## Base Class` heading slugs to `BASE_CLASS_ANCHOR` from
|
|
223
|
-
* `
|
|
224
|
-
* name points.
|
|
221
|
+
* `@tsdoctor/model`'s `SyntheticBases`, which is where the cross-link
|
|
222
|
+
* route for the base name points.
|
|
225
223
|
*/
|
|
226
224
|
generateBaseClassSection(apiClass, syntheticBase, packageName, apiScope) {
|
|
227
225
|
const baseDecl = syntheticBase;
|
|
228
226
|
if (!baseDecl?.excerpt?.text) return "";
|
|
229
227
|
let section = `## Base Class\n\n`;
|
|
230
228
|
section += `\`${apiClass.displayName}\` extends \`${baseDecl.displayName}\`, a compiler-generated declaration that is not exported from \`${packageName}\`.\n\n`;
|
|
231
|
-
const signature =
|
|
229
|
+
const signature = Signature.format(baseDecl.excerpt).trim();
|
|
232
230
|
let source = signature;
|
|
233
231
|
const apiPackage = apiClass.getAssociatedPackage?.();
|
|
234
232
|
if (apiPackage) {
|
|
@@ -286,13 +284,13 @@ var ClassPageGenerator = class {
|
|
|
286
284
|
*/
|
|
287
285
|
generateClassMemberWithContext(apiClass, targetMember, packageName) {
|
|
288
286
|
const className = apiClass.displayName;
|
|
289
|
-
const inheritance =
|
|
287
|
+
const inheritance = ApiItems.inheritance(apiClass);
|
|
290
288
|
let declaration = `class ${className}`;
|
|
291
289
|
if (inheritance.extends && inheritance.extends.length > 0) declaration += ` extends ${inheritance.extends.join(", ")}`;
|
|
292
290
|
if (inheritance.implements && inheritance.implements.length > 0) declaration += ` implements ${inheritance.implements.join(", ")}`;
|
|
293
291
|
declaration += " {";
|
|
294
292
|
const memberItem = targetMember;
|
|
295
|
-
const memberSignature = memberItem.excerpt?.text ?
|
|
293
|
+
const memberSignature = memberItem.excerpt?.text ? Signature.format(memberItem.excerpt).trim() : "";
|
|
296
294
|
const skeleton = `${declaration}\n${memberSignature}\n}`;
|
|
297
295
|
const apiPackage = apiClass.getAssociatedPackage?.();
|
|
298
296
|
if (apiPackage) {
|
|
@@ -320,7 +318,7 @@ var ClassPageGenerator = class {
|
|
|
320
318
|
generateClassSkeleton(apiClass) {
|
|
321
319
|
const lines = [];
|
|
322
320
|
const className = apiClass.displayName;
|
|
323
|
-
const inheritance =
|
|
321
|
+
const inheritance = ApiItems.inheritance(apiClass);
|
|
324
322
|
let declaration = `class ${className}`;
|
|
325
323
|
if (inheritance.extends && inheritance.extends.length > 0) declaration += ` extends ${inheritance.extends.join(", ")}`;
|
|
326
324
|
if (inheritance.implements && inheritance.implements.length > 0) declaration += ` implements ${inheritance.implements.join(", ")}`;
|
|
@@ -330,7 +328,7 @@ var ClassPageGenerator = class {
|
|
|
330
328
|
if (constructors.length > 0) for (const ctor of constructors) {
|
|
331
329
|
const ctorItem = ctor;
|
|
332
330
|
if (ctorItem.excerpt?.text) {
|
|
333
|
-
const signature =
|
|
331
|
+
const signature = Signature.format(ctorItem.excerpt).trim();
|
|
334
332
|
lines.push(` ${signature}`);
|
|
335
333
|
}
|
|
336
334
|
}
|
|
@@ -343,14 +341,14 @@ var ClassPageGenerator = class {
|
|
|
343
341
|
if (staticProperties.length > 0) for (const prop of staticProperties) {
|
|
344
342
|
const propItem = prop;
|
|
345
343
|
if (propItem.excerpt?.text) {
|
|
346
|
-
const signature =
|
|
344
|
+
const signature = Signature.format(propItem.excerpt).trim();
|
|
347
345
|
lines.push(` ${signature}`);
|
|
348
346
|
}
|
|
349
347
|
}
|
|
350
348
|
if (grouped.staticMethods.length > 0) for (const method of grouped.staticMethods) {
|
|
351
349
|
const methodItem = method;
|
|
352
350
|
if (methodItem.excerpt?.text) {
|
|
353
|
-
const signature =
|
|
351
|
+
const signature = Signature.format(methodItem.excerpt).trim();
|
|
354
352
|
lines.push(` ${signature}`);
|
|
355
353
|
}
|
|
356
354
|
}
|
|
@@ -362,21 +360,21 @@ var ClassPageGenerator = class {
|
|
|
362
360
|
if (instanceProperties.length > 0) for (const prop of instanceProperties) {
|
|
363
361
|
const propItem = prop;
|
|
364
362
|
if (propItem.excerpt?.text) {
|
|
365
|
-
const signature =
|
|
363
|
+
const signature = Signature.format(propItem.excerpt).trim();
|
|
366
364
|
lines.push(` ${signature}`);
|
|
367
365
|
}
|
|
368
366
|
}
|
|
369
367
|
if (grouped.getters.length > 0) for (const method of grouped.getters) {
|
|
370
368
|
const methodItem = method;
|
|
371
369
|
if (methodItem.excerpt?.text) {
|
|
372
|
-
const signature =
|
|
370
|
+
const signature = Signature.format(methodItem.excerpt).trim();
|
|
373
371
|
lines.push(` ${signature}`);
|
|
374
372
|
}
|
|
375
373
|
}
|
|
376
374
|
if (grouped.instanceMethods.length > 0) for (const method of grouped.instanceMethods) {
|
|
377
375
|
const methodItem = method;
|
|
378
376
|
if (methodItem.excerpt?.text) {
|
|
379
|
-
const signature =
|
|
377
|
+
const signature = Signature.format(methodItem.excerpt).trim();
|
|
380
378
|
lines.push(` ${signature}`);
|
|
381
379
|
}
|
|
382
380
|
}
|