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.
Files changed (53) hide show
  1. package/README.md +11 -11
  2. package/api-extracted-package.js +1 -1
  3. package/build-program.js +3 -3
  4. package/build-stages.js +30 -31
  5. package/config-helpers.js +61 -21
  6. package/errors.js +1 -7
  7. package/frontmatter.js +149 -0
  8. package/index.d.ts +11 -2
  9. package/layers/ConfigServiceLive.js +53 -39
  10. package/layers/TypeRegistryServiceLive.js +11 -6
  11. package/markdown/helpers.js +40 -69
  12. package/markdown/index.js +2 -2
  13. package/markdown/page-generators/class-page.js +40 -42
  14. package/markdown/page-generators/enum-page.js +15 -15
  15. package/markdown/page-generators/function-page.js +18 -20
  16. package/markdown/page-generators/interface-page.js +41 -43
  17. package/markdown/page-generators/namespace-page.js +22 -24
  18. package/markdown/page-generators/type-alias-page.js +14 -16
  19. package/markdown/page-generators/variable-page.js +14 -16
  20. package/markdown/prose-linker.js +22 -0
  21. package/model-loader.js +59 -113
  22. package/package.json +14 -8
  23. package/plugin.js +1 -5
  24. package/runtime/components/EnumMembersTable/index.css +18 -18
  25. package/runtime/components/EnumMembersTable/index.module.js +3 -3
  26. package/runtime/components/ExampleBlock/index.css +2 -2
  27. package/runtime/components/ExampleBlock/index.module.js +2 -2
  28. package/runtime/components/MemberSignature/index.css +6 -6
  29. package/runtime/components/MemberSignature/index.module.js +4 -4
  30. package/runtime/components/ParametersTable/index.css +19 -19
  31. package/runtime/components/ParametersTable/index.module.js +3 -3
  32. package/runtime/components/SignatureBlock/index.css +6 -6
  33. package/runtime/components/SignatureBlock/index.module.js +4 -4
  34. package/runtime/components/SignatureCode/index.css +9 -9
  35. package/runtime/components/SignatureCode/index.module.js +3 -3
  36. package/runtime/components/SignatureToolbar/index.css +18 -18
  37. package/runtime/components/SignatureToolbar/index.module.js +7 -7
  38. package/runtime/components/buttons/index.css +5 -5
  39. package/runtime/components/buttons/index.module.js +2 -2
  40. package/shiki-transformer.js +3 -3
  41. package/sync-node-fs.js +80 -0
  42. package/tsdoc-metadata.json +1 -1
  43. package/twoslash-transformer.js +1 -1
  44. package/content-hash.js +0 -79
  45. package/formatter.js +0 -69
  46. package/layers/SnapshotServiceLive.js +0 -92
  47. package/loader.js +0 -200
  48. package/markdown/cross-linker.js +0 -157
  49. package/migrations/001_create_snapshots.js +0 -25
  50. package/multi-entry-resolver.js +0 -70
  51. package/route-collisions.js +0 -44
  52. package/services/SnapshotService.js +0 -7
  53. 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 { ApiModelLoader } from "../model-loader.js";
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.promise(async () => {
178
- const { apiPackage, source: loaderSource } = await ApiModelLoader.loadApiModel(model);
179
- const resolvedCategories = categoryResolver.resolveCategoryConfig(pluginDefaults, api.categories);
180
- const resolvedSource = categoryResolver.resolveSourceConfig(api.source, loaderSource);
181
- const resolvedLlms = mergeLlmsPluginConfig(options.llmsPlugin, api.llmsPlugin);
182
- const packageJson = api.packageJson ? await ApiModelLoader.loadPackageJson(api.packageJson) : void 0;
183
- validateExternalPackages(api.externalPackages, packageJson);
184
- const externalPackages = api.externalPackages || extractAutoDetectedPackages(packageJson, api.autoDetectDependencies);
185
- if (externalPackages && externalPackages.length > 0) Effect.runSync(Metric.update(BuildMetrics.externalPackagesTotal, externalPackages.length));
186
- const vfs = ApiExtractedPackage.fromPackage(apiPackage, api.packageName).generateVfs();
187
- const vfsPayloads = prependImportsToVfs(vfs, apiPackage, api.packageName, wantTrace);
188
- const resolvedOgImage = api.ogImage ?? options.ogImage;
189
- const resolvedTheme = normalizeThemeConfig(api.theme);
190
- return {
191
- vfs,
192
- vfsPayloads,
193
- externalPackages: externalPackages || [],
194
- config: {
195
- apiPackage,
196
- packageName: api.packageName,
197
- ...api.name != null ? { apiName: api.name } : {},
198
- outputDir,
199
- baseRoute: fullRoute,
200
- categories: resolvedCategories,
201
- ...resolvedSource != null ? { source: resolvedSource } : {},
202
- ...packageJson != null ? { packageJson } : {},
203
- ...resolvedLlms != null ? { llmsPlugin: resolvedLlms } : {},
204
- ...options.siteUrl != null ? { siteUrl: options.siteUrl } : {},
205
- ...resolvedOgImage != null ? { ogImage: resolvedOgImage } : {},
206
- docsDir: path.dirname(outputDir),
207
- ...docsRoot != null ? { docsRoot } : {},
208
- ...resolvedTheme != null ? { theme: resolvedTheme } : {}
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 ApiModelLoader.loadPackageJson(api.packageJson) : void 0);
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 "type-registry-effect";
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 type-registry-effect's typed `RegistryEvent`s to the plugin's Effect
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
- * type-registry-effect v2 composes at the edge: the library ships no platform
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
- /** XDG app directories under the library's shared namespace (cache is shared across consumers). */
114
- const AppDirsLive = AppDirs.layer({ namespace: "type-registry-effect" }).pipe(Layer.provide(Layer.mergeAll(Xdg.layer, PlatformLive)));
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 type-registry-effect Effect programs directly.
134
+ * TypeRegistryServiceLive: uses @tsdoctor/registry Effect programs directly.
130
135
  */
131
136
  const TypeRegistryServiceLive = Layer.succeed(TypeRegistryService, {
132
137
  resolveVersions: (packages) => Effect.gen(function* () {
@@ -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.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
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(["meta", {
176
- property: "og:url",
177
- content: `${ogMetadata.siteUrl}${ogMetadata.pageRoute}`
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(["meta", {
189
- property: "og:image",
190
- content: ogMetadata.ogImage.url
191
- }]);
192
- if (ogMetadata.ogImage.secureUrl) headEntries.push(["meta", {
193
- property: "og:image:secure_url",
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(["meta", {
214
- property: "article:published_time",
215
- content: ogMetadata.publishedTime
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
- let frontmatter = `---\ntitle: ${escapeYamlString(title)}\n`;
231
- frontmatter += `description: ${escapeYamlString(description)}\n`;
232
- if (headEntries.length > 0) {
233
- frontmatter += "head:\n";
234
- for (const [tag, attrs] of headEntries) {
235
- frontmatter += ` - - ${tag}\n`;
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 { MarkdownCrossLinker, markdownCrossLinker } from "./cross-linker.js";
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, markdownCrossLinker };
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 { markdownCrossLinker } from "../cross-linker.js";
5
- import { TypeSignatureFormatter } from "../../formatter.js";
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 {@link TypeSignatureFormatter} for formatting type signatures
36
- * - Uses {@link ApiParser} for extracting documentation from API models
37
- * - Uses {@link MarkdownCrossLinker} for adding type reference links
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 = ApiParser.getSummary(apiClass) || "No description available.";
69
- const releaseTag = ApiParser.getReleaseTag(apiClass);
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 = ApiParser.getDeprecation(apiClass);
73
+ const deprecation = Tsdoc.deprecation(apiClass);
76
74
  if (deprecation) {
77
- const message = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(deprecation.message));
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 = ApiParser.getSourceLink(apiClass, sourceConfig);
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 = ApiParser.getSummary(ctor);
101
+ const ctorSummary = Tsdoc.summary(ctor);
104
102
  const ctorId = sanitizeId("constructor");
105
103
  const ctorItem = ctor;
106
- const params = ApiParser.getParams(ctor);
104
+ const params = Tsdoc.params(ctor);
107
105
  const hasParameters = params.length > 0;
108
106
  if (ctorItem.excerpt?.text) {
109
- const memberSignature = this.typeFormatter.format(ctorItem.excerpt).trim();
107
+ const memberSignature = Signature.format(ctorItem.excerpt).trim();
110
108
  const skeletonWithContext = this.generateClassMemberWithContext(apiClass, ctor, packageName);
111
- const summaryMd = ctorSummary ? escapeMdxGenerics(markdownCrossLinker.addCrossLinks(ctorSummary)) : void 0;
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: markdownCrossLinker.addCrossLinks(p.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 = ApiParser.getSummary(prop);
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 = this.typeFormatter.format(propItem.excerpt).trim();
145
+ const memberSignature = Signature.format(propItem.excerpt).trim();
148
146
  const skeletonWithContext = this.generateClassMemberWithContext(apiClass, prop, packageName);
149
- const summaryMd = propSummary ? escapeMdxGenerics(markdownCrossLinker.addCrossLinks(propSummary)) : void 0;
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 = ApiParser.getSummary(method);
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 = ApiParser.getParams(method);
161
+ const params = Tsdoc.params(method);
164
162
  const hasParameters = params.length > 0;
165
163
  if (methodItem.excerpt?.text) {
166
- const memberSignature = this.typeFormatter.format(methodItem.excerpt).trim();
164
+ const memberSignature = Signature.format(methodItem.excerpt).trim();
167
165
  const skeletonWithContext = this.generateClassMemberWithContext(apiClass, method, packageName);
168
- const summaryMd = methodSummary ? escapeMdxGenerics(markdownCrossLinker.addCrossLinks(methodSummary)) : void 0;
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: markdownCrossLinker.addCrossLinks(p.description)
172
+ description: linkProse(p.description)
175
173
  })))}} />\n\n`;
176
- const returns = ApiParser.getReturns(method);
174
+ const returns = Tsdoc.returns(method);
177
175
  if (returns) {
178
- const description = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(returns.description));
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 = ApiParser.getExamples(apiClass);
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 = ApiParser.getSeeReferences(apiClass);
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(markdownCrossLinker.addCrossLinks(reference.text));
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
- * `synthetic-bases.ts`, which is where the cross-link route for the base
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 = this.typeFormatter.format(baseDecl.excerpt).trim();
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 = ApiParser.getInheritance(apiClass);
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 ? this.typeFormatter.format(memberItem.excerpt).trim() : "";
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 = ApiParser.getInheritance(apiClass);
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 = this.typeFormatter.format(ctorItem.excerpt).trim();
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 = this.typeFormatter.format(propItem.excerpt).trim();
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 = this.typeFormatter.format(methodItem.excerpt).trim();
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 = this.typeFormatter.format(propItem.excerpt).trim();
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 = this.typeFormatter.format(methodItem.excerpt).trim();
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 = this.typeFormatter.format(methodItem.excerpt).trim();
377
+ const signature = Signature.format(methodItem.excerpt).trim();
380
378
  lines.push(` ${signature}`);
381
379
  }
382
380
  }