rspress-plugin-api-extractor 0.10.0 → 0.12.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 (54) hide show
  1. package/BuildEnv.js +58 -0
  2. package/build-program.js +34 -33
  3. package/build-stages.js +48 -42
  4. package/config-helpers.js +7 -7
  5. package/errors.js +1 -6
  6. package/index.d.ts +84 -86
  7. package/layers/AppLayer.js +67 -0
  8. package/layers/api-results.js +83 -0
  9. package/layers/build-metrics.js +1 -1
  10. package/layers/config-resolution.js +407 -0
  11. package/layers/external-types.js +74 -0
  12. package/layers/{ObservabilityLive.js → observability.js} +3 -3
  13. package/layers/type-environment.js +109 -0
  14. package/layers/xdg.js +44 -0
  15. package/markdown/helpers.js +9 -55
  16. package/markdown/page-generators/class-page.js +8 -31
  17. package/markdown/page-generators/index-pages.js +6 -8
  18. package/markdown/page-generators/interface-page.js +7 -7
  19. package/markdown/shiki-utils.js +65 -10
  20. package/model-loader.js +3 -3
  21. package/observability/EventBus.js +29 -7
  22. package/observability/heartbeat.js +1 -1
  23. package/observability/sinks/metrics-sink.js +1 -1
  24. package/observability/sinks/trace-sink.js +4 -4
  25. package/observability/spans.js +3 -1
  26. package/observability/sync-emitter.js +78 -0
  27. package/og-resolver.js +74 -284
  28. package/package.json +3 -4
  29. package/path-derivation.js +19 -1
  30. package/plugin.js +63 -91
  31. package/prettier-formatter.js +5 -11
  32. package/remark-api-codeblocks.js +11 -19
  33. package/remark-with-api.js +11 -21
  34. package/schemas/config.js +0 -2
  35. package/services/ConfigService.js +37 -2
  36. package/services/HighlighterService.js +75 -0
  37. package/services/OgService.js +190 -0
  38. package/services/PluginConfig.js +26 -0
  39. package/services/TwoslashCacheService.js +128 -2
  40. package/services/TwoslashEnvironments.js +35 -0
  41. package/services/TypeRegistryService.js +178 -2
  42. package/shiki-transformer.js +53 -234
  43. package/sync-node-fs.js +6 -6
  44. package/tsconfig-parser.js +77 -95
  45. package/twoslash-access.js +48 -0
  46. package/twoslash-transformer.js +106 -83
  47. package/vfs-registry.js +1 -31
  48. package/layers/ConfigServiceLive.js +0 -600
  49. package/layers/PathDerivationServiceLive.js +0 -16
  50. package/layers/TwoslashCacheServiceLive.js +0 -53
  51. package/layers/TypeRegistryServiceLive.js +0 -155
  52. package/markdown/index.js +0 -11
  53. package/schemas/index.js +0 -6
  54. package/services/PathDerivationService.js +0 -7
package/BuildEnv.js ADDED
@@ -0,0 +1,58 @@
1
+ import { Context } from "effect";
2
+
3
+ //#region src/BuildEnv.ts
4
+ /**
5
+ * Per-build configuration, as `Context.Reference` values rather than threaded
6
+ * parameters.
7
+ *
8
+ * @remarks
9
+ * Each of these used to travel by hand. `thresholds` made a four-hop journey —
10
+ * `plugin.ts` → `ConfigService.layer`'s fourth constructor argument →
11
+ * a `ResolvedBuildContext` field → destructured in `build-program.ts` → every
12
+ * `withPhase` call — while `obs.thresholds` already held the same value one
13
+ * scope away. `buildId` was worse: 24 event emit sites wrote
14
+ * `ctx: { buildId: "" }` because the value was not reachable from where they
15
+ * stood, and in `TypeRegistryService.layer` it genuinely was not — that layer is
16
+ * module-level and has no build to name. A Reference is the fix precisely
17
+ * because it reaches module-level code that no parameter can.
18
+ *
19
+ * A `Context.Reference` carries a default, so nothing has to provide these for
20
+ * a program to run. That is convenient and it is also the hazard: a wiring
21
+ * mistake does not fail, it silently substitutes the default. The tests for
22
+ * this module provide NON-default values and assert the provided value is the
23
+ * one observed, which is the only way to tell the two apart.
24
+ *
25
+ * @packageDocumentation
26
+ */
27
+ /**
28
+ * Identifier correlating every event emitted by one build.
29
+ *
30
+ * @remarks
31
+ * The empty-string default is deliberate: an event emitted outside a build —
32
+ * a test, a stray sync callback — is better tagged empty than crashing. Every
33
+ * production path provides a real id.
34
+ */
35
+ const BuildId = Context.Reference("rspress-plugin-api-extractor/BuildId", { defaultValue: () => "" });
36
+ /** Duration thresholds above which an operation is reported as slow. */
37
+ const Thresholds = Context.Reference("rspress-plugin-api-extractor/Thresholds", { defaultValue: () => ({
38
+ slowCodeBlock: 100,
39
+ slowPageGeneration: 500,
40
+ slowApiLoad: 1e3,
41
+ slowFileOperation: 50,
42
+ slowHttpRequest: 2e3,
43
+ slowDbOperation: 100
44
+ }) });
45
+ /**
46
+ * How many pages the build pipeline generates concurrently.
47
+ *
48
+ * @remarks
49
+ * Defaults to the CPU count, which is what `ConfigService.layer` computed
50
+ * inline. Kept a Reference rather than a constant so a consumer with a
51
+ * constrained CI runner can lower it without a code change.
52
+ */
53
+ const PageConcurrency = Context.Reference("rspress-plugin-api-extractor/PageConcurrency", { defaultValue: () => 1 });
54
+ /** Whether Twoslash diagnostics inside `@example` blocks are suppressed. */
55
+ const SuppressExampleErrors = Context.Reference("rspress-plugin-api-extractor/SuppressExampleErrors", { defaultValue: () => true });
56
+
57
+ //#endregion
58
+ export { BuildId, PageConcurrency, SuppressExampleErrors, Thresholds };
package/build-program.js CHANGED
@@ -1,12 +1,16 @@
1
+ import { BuildId, PageConcurrency, SuppressExampleErrors } from "./BuildEnv.js";
1
2
  import { setProseLinker } from "./markdown/prose-linker.js";
2
- import "./markdown/index.js";
3
3
  import { buildPipelineForApi, cleanupAndCommit, prepareWorkItems, writeMetadata } from "./build-stages.js";
4
+ import { HideCutLinesTransformer, MemberFormatTransformer } from "./hide-cut-transformer.js";
4
5
  import { withPhase } from "./observability/spans.js";
5
- import { TwoslashManager } from "./twoslash-transformer.js";
6
+ import { HighlighterService } from "./services/HighlighterService.js";
7
+ import { addTypeRoutes } from "./twoslash-transformer.js";
8
+ import { TwoslashEnvironments } from "./services/TwoslashEnvironments.js";
9
+ import { ShikiCrossLinker } from "./shiki-transformer.js";
6
10
  import { VfsRegistry } from "./vfs-registry.js";
7
11
  import path from "node:path";
8
- import { SnapshotService } from "@tsdoctor/snapshot";
9
12
  import { Effect, FileSystem } from "effect";
13
+ import { SnapshotService } from "@tsdoctor/snapshot";
10
14
 
11
15
  //#region src/build-program.ts
12
16
  /**
@@ -22,17 +26,17 @@ import { Effect, FileSystem } from "effect";
22
26
  * Returns build result metadata including CrossLinkData for cross-link merging
23
27
  * and generated file paths for LLMs post-processing.
24
28
  */
25
- function generateApiDocs(apiConfig, buildContext, fileContextMap) {
29
+ function generateApiDocs(apiConfig, fileContextMap) {
26
30
  return Effect.gen(function* () {
27
31
  const fileSystem = yield* FileSystem.FileSystem;
32
+ const { highlighter } = yield* HighlighterService;
33
+ const environments = yield* TwoslashEnvironments;
34
+ const buildId = yield* BuildId;
35
+ const pageConcurrency = yield* PageConcurrency;
36
+ const suppressExampleErrors = yield* SuppressExampleErrors;
28
37
  const snapshotSvc = yield* SnapshotService;
29
38
  const { apiPackage, packageName, apiName, outputDir, baseRoute, categories, source, packageJson, llmsPlugin, siteUrl, ogImage } = apiConfig;
30
- const suppressExampleErrors = apiConfig.suppressExampleErrors ?? true;
31
- const { shikiCrossLinker, highlighter, hideCutTransformer, hideCutLinesTransformer, twoslashTransformer, ogResolver, pageConcurrency, thresholds, buildId } = buildContext;
32
- const phaseCtx = {
33
- buildId,
34
- packageName
35
- };
39
+ const phaseCtx = { packageName };
36
40
  const resolvedOutputDir = path.resolve(process.cwd(), outputDir);
37
41
  const buildTime = (/* @__PURE__ */ new Date()).toISOString();
38
42
  const allSnapshots = yield* snapshotSvc.getAllForDirectory(resolvedOutputDir).pipe(Effect.orDie);
@@ -43,29 +47,27 @@ function generateApiDocs(apiConfig, buildContext, fileContextMap) {
43
47
  categories,
44
48
  baseRoute,
45
49
  packageName
46
- })), thresholds);
50
+ })));
47
51
  setProseLinker(crossLinkData.routes);
48
52
  const apiScope = baseRoute.replace(/^\//, "").split("/")[0] || packageName;
49
- shikiCrossLinker.reinitialize(crossLinkData.routes, crossLinkData.kinds, apiScope);
50
- TwoslashManager.addTypeRoutes(crossLinkData.routes);
51
- if (highlighter) {
52
- const vfsConfig = {
53
- vfs: /* @__PURE__ */ new Map(),
54
- highlighter,
55
- crossLinker: shikiCrossLinker,
56
- packageName,
57
- apiScope
58
- };
59
- const scopeTransformer = TwoslashManager.getInstance().getTransformer(apiScope) ?? twoslashTransformer;
60
- if (scopeTransformer != null) vfsConfig.twoslashTransformer = scopeTransformer;
61
- if (hideCutTransformer != null) vfsConfig.hideCutTransformer = hideCutTransformer;
62
- if (hideCutLinesTransformer != null) vfsConfig.hideCutLinesTransformer = hideCutLinesTransformer;
63
- if (apiConfig.theme != null) vfsConfig.theme = apiConfig.theme;
64
- VfsRegistry.register(apiScope, vfsConfig);
65
- }
53
+ const shikiCrossLinker = ShikiCrossLinker.fromRoutes(crossLinkData.routes, crossLinkData.kinds, apiScope);
54
+ addTypeRoutes(crossLinkData.routes);
55
+ const vfsConfig = {
56
+ highlighter,
57
+ crossLinker: shikiCrossLinker,
58
+ packageName,
59
+ apiScope
60
+ };
61
+ const scopeTransformer = environments.transformerFor(apiScope);
62
+ if (scopeTransformer != null) vfsConfig.twoslashTransformer = scopeTransformer;
63
+ vfsConfig.hideCutTransformer = MemberFormatTransformer;
64
+ vfsConfig.hideCutLinesTransformer = HideCutLinesTransformer;
65
+ if (apiConfig.theme != null) vfsConfig.theme = apiConfig.theme;
66
+ VfsRegistry.register(apiScope, vfsConfig);
66
67
  yield* Effect.logDebug(`Generating ${workItems.length} pages across ${Object.keys(categories).length} categories in parallel`);
67
68
  const fileResults = yield* withPhase("generate", phaseCtx, buildPipelineForApi({
68
69
  buildId,
70
+ pageConcurrency,
69
71
  workItems,
70
72
  baseRoute,
71
73
  packageName,
@@ -74,14 +76,13 @@ function generateApiDocs(apiConfig, buildContext, fileContextMap) {
74
76
  ...source != null ? { source } : {},
75
77
  buildTime,
76
78
  resolvedOutputDir,
77
- pageConcurrency,
78
79
  existingSnapshots,
79
80
  ...suppressExampleErrors != null ? { suppressExampleErrors } : {},
80
81
  ...llmsPlugin != null ? { llmsPlugin } : {},
81
- ...ogResolver !== void 0 ? { ogResolver } : {},
82
+ ...apiConfig.docsRoot != null ? { docsRoot: apiConfig.docsRoot } : {},
82
83
  ...siteUrl != null ? { siteUrl } : {},
83
84
  ...ogImage != null ? { ogImage } : {}
84
- }), thresholds);
85
+ }));
85
86
  const changedCount = fileResults.filter((r) => r.status !== "unchanged").length;
86
87
  yield* Effect.logDebug(`Generated ${changedCount} pages`);
87
88
  const generatedFiles = /* @__PURE__ */ new Set();
@@ -103,13 +104,13 @@ function generateApiDocs(apiConfig, buildContext, fileContextMap) {
103
104
  packageName,
104
105
  ...apiName != null ? { apiName } : {},
105
106
  generatedFiles
106
- }), thresholds);
107
+ }));
107
108
  yield* withPhase("cleanup", phaseCtx, cleanupAndCommit({
108
109
  buildId,
109
110
  fileResults,
110
111
  resolvedOutputDir,
111
112
  generatedFiles
112
- }), thresholds);
113
+ }));
113
114
  yield* Effect.logDebug(`Generated ${changedCount} API documentation files for ${packageName}`);
114
115
  return {
115
116
  crossLinkData,
package/build-stages.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { parseFrontmatter, stringifyFrontmatter } from "./frontmatter.js";
2
+ import { BuildMetrics } from "./layers/build-metrics.js";
2
3
  import { PluginEvent } from "./observability/events.js";
3
4
  import { emit } from "./observability/EventBus.js";
4
- import { BuildMetrics } from "./layers/build-metrics.js";
5
- import "./layers/ObservabilityLive.js";
5
+ import { emitSync, syncBuildId } from "./observability/sync-emitter.js";
6
6
  import { generateFrontmatter } from "./markdown/helpers.js";
7
7
  import { ClassPageGenerator } from "./markdown/page-generators/class-page.js";
8
8
  import { EnumPageGenerator } from "./markdown/page-generators/enum-page.js";
@@ -12,11 +12,11 @@ import { InterfacePageGenerator } from "./markdown/page-generators/interface-pag
12
12
  import { NamespacePageGenerator } from "./markdown/page-generators/namespace-page.js";
13
13
  import { TypeAliasPageGenerator } from "./markdown/page-generators/type-alias-page.js";
14
14
  import { VariablePageGenerator } from "./markdown/page-generators/variable-page.js";
15
- import "./markdown/index.js";
16
- import { OpenGraphResolver } from "./og-resolver.js";
15
+ import { createPageMetadata } from "./og-resolver.js";
16
+ import { OgService } from "./services/OgService.js";
17
17
  import path from "node:path";
18
+ import { Effect, FileSystem, Metric, Option, Stream } from "effect";
18
19
  import { SnapshotService, hashContent, hashFrontmatter } from "@tsdoctor/snapshot";
19
- import { Effect, FileSystem, Metric, Stream } from "effect";
20
20
  import { ApiItemKind } from "@microsoft/api-extractor-model";
21
21
  import { ApiItems, EntryPoints, Routes, SyntheticBases } from "@tsdoctor/model";
22
22
 
@@ -41,24 +41,6 @@ function crossLinkKindPriority(kind) {
41
41
  return CROSS_LINK_KIND_PRIORITY[kind] ?? 100;
42
42
  }
43
43
  /**
44
- * Module-level emitter seam. `prepareWorkItems` runs synchronously outside any
45
- * Effect fiber, so a route collision cannot `yield* emit(...)` — it mirrors the
46
- * sync-island pattern used by `twoslash-transformer.ts` (`setEventEmitter`) and
47
- * `loader.ts` (`setLoaderEventEmitter`). Default is a no-op; wired in plugin.ts
48
- * via `setBuildStagesEventEmitter(emitSync, buildId)` right after the runtime
49
- * emitter is created.
50
- */
51
- let emitEvent = () => {};
52
- let currentBuildId = "";
53
- /**
54
- * Inject the runtime-bound emitter into the build-stages module.
55
- * Call this right after `makeRuntimeEmitter` in plugin.ts.
56
- */
57
- function setBuildStagesEventEmitter(fn, buildId = "") {
58
- emitEvent = fn;
59
- currentBuildId = buildId;
60
- }
61
- /**
62
44
  * Prepare the flat list of WorkItems to process and the cross-link data maps.
63
45
  *
64
46
  * This function:
@@ -82,8 +64,8 @@ function prepareWorkItems(input) {
82
64
  resolvedLookup.set(key, resolved);
83
65
  }
84
66
  const { items, uncategorized } = ApiItems.categorize(docItems, categories);
85
- for (const skipped of uncategorized) emitEvent(PluginEvent.ItemSkipped({
86
- ctx: { buildId: currentBuildId },
67
+ for (const skipped of uncategorized) emitSync(PluginEvent.ItemSkipped({
68
+ ctx: { buildId: syncBuildId() },
87
69
  item: skipped.displayName,
88
70
  kind: String(skipped.kind),
89
71
  reason: "uncategorized",
@@ -115,9 +97,9 @@ function prepareWorkItems(input) {
115
97
  const collisions = Routes.detectCollisions(candidates);
116
98
  if (collisions.length > 0) {
117
99
  try {
118
- for (const collision of collisions) emitEvent(PluginEvent.RouteCollisionDetected({
100
+ for (const collision of collisions) emitSync(PluginEvent.RouteCollisionDetected({
119
101
  ctx: {
120
- buildId: currentBuildId,
102
+ buildId: syncBuildId(),
121
103
  route: collision.route
122
104
  },
123
105
  level: "error",
@@ -145,13 +127,14 @@ function prepareWorkItems(input) {
145
127
  }
146
128
  if (item.kind === "Class" || item.kind === "Interface") {
147
129
  const itemWithMembers = item;
148
- for (const member of itemWithMembers.members) {
149
- const memberName = member.displayName;
150
- const memberId = Routes.sanitizeId(memberName);
151
- const fullMemberName = `${item.displayName}.${memberName}`;
152
- const memberRoute = `${itemRoute}#${memberId}`;
153
- routes.set(fullMemberName, memberRoute);
154
- kinds.set(fullMemberName, member.kind);
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);
155
138
  }
156
139
  }
157
140
  }
@@ -190,12 +173,14 @@ function prepareWorkItems(input) {
190
173
  const lookupKey = `${item.displayName}::${item.kind}`;
191
174
  const resolved = resolvedLookup.get(lookupKey);
192
175
  const syntheticBase = syntheticBases.baseByOwner.get(item);
176
+ const memberAnchors = item.kind === "Class" || item.kind === "Interface" ? ApiItems.memberAnchors(item) : void 0;
193
177
  workItems.push({
194
178
  item,
195
179
  categoryKey,
196
180
  categoryConfig,
197
181
  ...resolved?.availableFrom != null ? { availableFrom: resolved.availableFrom } : {},
198
- ...syntheticBase != null ? { syntheticBase } : {}
182
+ ...syntheticBase != null ? { syntheticBase } : {},
183
+ ...memberAnchors != null ? { memberAnchors } : {}
199
184
  });
200
185
  }
201
186
  }
@@ -240,7 +225,7 @@ function generateSinglePage(workItem, ctx) {
240
225
  switch (item.kind) {
241
226
  case ApiItemKind.Class: {
242
227
  const generator = new ClassPageGenerator();
243
- page = yield* Effect.promise(() => generator.generate(item, baseRoute, packageName, categoryConfig.singularName, apiScope, apiName, source, suppressExampleErrors, llmsPlugin, workItem.availableFrom, workItem.syntheticBase));
228
+ page = yield* Effect.promise(() => generator.generate(item, baseRoute, packageName, categoryConfig.singularName, apiScope, apiName, source, suppressExampleErrors, llmsPlugin, workItem.availableFrom, workItem.syntheticBase, workItem.memberAnchors));
244
229
  page = {
245
230
  routePath: page.routePath.replace("/class/", `/${categoryConfig.folderName}/`),
246
231
  content: page.content
@@ -402,7 +387,7 @@ function generateSinglePage(workItem, ctx) {
402
387
  function writeSingleFile(result, ctx) {
403
388
  return Effect.gen(function* () {
404
389
  const fileSystem = yield* FileSystem.FileSystem;
405
- const { buildId, resolvedOutputDir, buildTime, ogResolver, siteUrl, ogImage, packageName, apiName } = ctx;
390
+ const { buildId, resolvedOutputDir, buildTime, siteUrl, docsRoot, ogImage, packageName, apiName } = ctx;
406
391
  const { workItem, bodyContent, frontmatter, contentHash, frontmatterHash, publishedTime, modifiedTime, isUnchanged, routePath, relativePathWithExt } = result;
407
392
  const { item, categoryKey, categoryConfig, namespaceMember } = workItem;
408
393
  const absolutePath = path.join(resolvedOutputDir, relativePathWithExt);
@@ -440,8 +425,29 @@ function writeSingleFile(result, ctx) {
440
425
  };
441
426
  }
442
427
  let finalContent = stringifyFrontmatter(bodyContent, frontmatter);
443
- if (ogResolver && siteUrl && packageName) {
444
- const ogImageMetadata = yield* Effect.promise(() => ogResolver.resolve(ogImage, packageName, apiName));
428
+ if (siteUrl != null && packageName) {
429
+ const ogSvc = yield* OgService;
430
+ const ogImageResult = yield* Effect.result(ogSvc.resolveImage({
431
+ config: ogImage,
432
+ siteUrl,
433
+ docsRoot,
434
+ packageName,
435
+ ...apiName != null ? { apiName } : {}
436
+ }));
437
+ if (ogImageResult._tag === "Failure") {
438
+ const failure = ogImageResult.failure;
439
+ yield* emit(PluginEvent.ConfigValidationWarning({
440
+ ctx: {
441
+ buildId,
442
+ packageName
443
+ },
444
+ field: failure.field,
445
+ value: failure.value,
446
+ reason: failure.message,
447
+ level: "warn"
448
+ }));
449
+ }
450
+ const ogImageMetadata = ogImageResult._tag === "Success" && Option.isSome(ogImageResult.success) ? ogImageResult.success.value : void 0;
445
451
  const ogMetadataOptions = {
446
452
  siteUrl,
447
453
  pageRoute: routePath,
@@ -452,7 +458,7 @@ function writeSingleFile(result, ctx) {
452
458
  packageName
453
459
  };
454
460
  if (ogImageMetadata) ogMetadataOptions.ogImage = ogImageMetadata;
455
- const ogMetadata = OpenGraphResolver.createPageMetadata(ogMetadataOptions);
461
+ const ogMetadata = createPageMetadata(ogMetadataOptions);
456
462
  finalContent = generateFrontmatter(item.displayName, frontmatter.description, categoryConfig.singularName, apiName, ogMetadata) + bodyContent;
457
463
  }
458
464
  const fileExisted = yield* fileSystem.exists(absolutePath).pipe(Effect.orElseSucceed(() => false));
@@ -793,7 +799,7 @@ function buildPipelineForApi(input) {
793
799
  buildId: input.buildId,
794
800
  resolvedOutputDir: input.resolvedOutputDir,
795
801
  buildTime: input.buildTime,
796
- ...input.ogResolver !== void 0 ? { ogResolver: input.ogResolver } : {},
802
+ ...input.docsRoot !== void 0 ? { docsRoot: input.docsRoot } : {},
797
803
  ...input.siteUrl != null ? { siteUrl: input.siteUrl } : {},
798
804
  ...input.ogImage != null ? { ogImage: input.ogImage } : {},
799
805
  ...input.packageName != null ? { packageName: input.packageName } : {},
@@ -803,4 +809,4 @@ function buildPipelineForApi(input) {
803
809
  }
804
810
 
805
811
  //#endregion
806
- export { buildPipelineForApi, cleanupAndCommit, crossLinkKindPriority, generateSinglePage, prepareWorkItems, setBuildStagesEventEmitter, writeMetadata, writeSingleFile };
812
+ export { buildPipelineForApi, cleanupAndCommit, crossLinkKindPriority, generateSinglePage, prepareWorkItems, writeMetadata, writeSingleFile };
package/config-helpers.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { normalizeBaseRoute } from "./path-derivation.js";
2
2
  import { SyncDiscoveryLayer } from "./sync-node-fs.js";
3
- import fs from "node:fs";
3
+ import fsSync from "node:fs";
4
4
  import path from "node:path";
5
5
  import { Effect, Result } from "effect";
6
6
  import { discoverBundle } from "@tsdoctor/bundle";
@@ -18,14 +18,14 @@ const PREFIX = "[rspress-plugin-api-extractor]";
18
18
  function requirePackageJson(dir) {
19
19
  let stat;
20
20
  try {
21
- stat = fs.statSync(dir);
21
+ stat = fsSync.statSync(dir);
22
22
  } catch {
23
23
  throw new Error(`${PREFIX} api.fromDir: directory not found: ${dir}`);
24
24
  }
25
25
  if (!stat.isDirectory()) throw new Error(`${PREFIX} api.fromDir: not a directory: ${dir}`);
26
26
  let pkg;
27
27
  try {
28
- pkg = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf8"));
28
+ pkg = JSON.parse(fsSync.readFileSync(path.join(dir, "package.json"), "utf8"));
29
29
  } catch {
30
30
  throw new Error(`${PREFIX} api.fromDir: missing or unreadable package.json in ${dir}`);
31
31
  }
@@ -103,9 +103,9 @@ function fromDir(dir, overrides = {}) {
103
103
  };
104
104
  }
105
105
  function isModelFolder(dir) {
106
- if (!fs.existsSync(path.join(dir, "package.json"))) return false;
106
+ if (!fsSync.existsSync(path.join(dir, "package.json"))) return false;
107
107
  try {
108
- return fs.readdirSync(dir).some((f) => f.endsWith(".api.json"));
108
+ return fsSync.readdirSync(dir).some((f) => f.endsWith(".api.json"));
109
109
  } catch {
110
110
  return false;
111
111
  }
@@ -124,12 +124,12 @@ function fromParentDir(parentDir, options = {}) {
124
124
  const absParent = path.resolve(cwd ?? process.cwd(), parentDir);
125
125
  let stat;
126
126
  try {
127
- stat = fs.statSync(absParent);
127
+ stat = fsSync.statSync(absParent);
128
128
  } catch {
129
129
  throw new Error(`${PREFIX} apis.fromDir: directory not found: ${absParent}`);
130
130
  }
131
131
  if (!stat.isDirectory()) throw new Error(`${PREFIX} apis.fromDir: not a directory: ${absParent}`);
132
- const subdirs = fs.readdirSync(absParent, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort();
132
+ const subdirs = fsSync.readdirSync(absParent, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort();
133
133
  const configs = [];
134
134
  for (const name of subdirs) {
135
135
  const subdir = path.join(absParent, name);
package/errors.js CHANGED
@@ -7,17 +7,12 @@ var ConfigValidationError = class extends ConfigValidationErrorBase {
7
7
  return `Config validation failed for '${this.field}': ${this.reason}`;
8
8
  }
9
9
  };
10
- const ApiModelLoadErrorBase = Data.TaggedError("ApiModelLoadError");
11
- const PathDerivationErrorBase = Data.TaggedError("PathDerivationError");
12
10
  const TypeRegistryErrorBase = Data.TaggedError("TypeRegistryError");
13
11
  var TypeRegistryError = class extends TypeRegistryErrorBase {
14
12
  get message() {
15
13
  return `Type registry error for '${this.packageName}@${this.version}': ${this.reason}`;
16
14
  }
17
15
  };
18
- const PageGenerationErrorBase = Data.TaggedError("PageGenerationError");
19
- const TwoslashProcessingErrorBase = Data.TaggedError("TwoslashProcessingError");
20
- const PrettierFormatErrorBase = Data.TaggedError("PrettierFormatError");
21
16
 
22
17
  //#endregion
23
- export { ConfigValidationError, ConfigValidationErrorBase, TypeRegistryError, TypeRegistryErrorBase };
18
+ export { ConfigValidationError, TypeRegistryError };
package/index.d.ts CHANGED
@@ -669,8 +669,6 @@ declare const PluginOptions: Schema.Struct<{
669
669
  */
670
670
  readonly compilerOptions: Schema.optional<Schema.Unknown>;
671
671
  }>>>>>;
672
- /** Canonical site URL used for Open Graph absolute URLs. */
673
- readonly siteUrl: Schema.optional<Schema.String>;
674
672
  /** Global Open Graph image configuration (overridden per-API). */
675
673
  readonly ogImage: Schema.optional<Schema.Union<readonly [Schema.String, Schema.Struct<{
676
674
  readonly url: Schema.String;
@@ -746,90 +744,6 @@ declare const PluginOptions: Schema.Struct<{
746
744
  /** @public */
747
745
  type PluginOptions = typeof PluginOptions.Encoded;
748
746
  //#endregion
749
- //#region src/schemas/opengraph.d.ts
750
- /**
751
- * Structured Open Graph image metadata (alternative to a plain URL string).
752
- *
753
- * @public
754
- */
755
- declare const OpenGraphImageMetadata: Schema.Struct<{
756
- /** Absolute URL of the image. */
757
- readonly url: Schema.String;
758
- /** HTTPS URL of the image (for secure contexts). */
759
- readonly secureUrl: Schema.optional<Schema.String>;
760
- /** MIME type of the image (e.g. `"image/png"`). */
761
- readonly type: Schema.optional<Schema.String>;
762
- /** Image width in pixels. */
763
- readonly width: Schema.optional<Schema.Number>;
764
- /** Image height in pixels. */
765
- readonly height: Schema.optional<Schema.Number>;
766
- /** Alt text for the image. */
767
- readonly alt: Schema.optional<Schema.String>;
768
- }>;
769
- /** @public */
770
- type OpenGraphImageMetadata = typeof OpenGraphImageMetadata.Type;
771
- /**
772
- * Open Graph image: either a plain URL string or structured `OpenGraphImageMetadata`.
773
- *
774
- * @public
775
- */
776
- declare const OpenGraphImageConfig: Schema.Union<readonly [Schema.String, Schema.Struct<{
777
- /** Absolute URL of the image. */
778
- readonly url: Schema.String;
779
- /** HTTPS URL of the image (for secure contexts). */
780
- readonly secureUrl: Schema.optional<Schema.String>;
781
- /** MIME type of the image (e.g. `"image/png"`). */
782
- readonly type: Schema.optional<Schema.String>;
783
- /** Image width in pixels. */
784
- readonly width: Schema.optional<Schema.Number>;
785
- /** Image height in pixels. */
786
- readonly height: Schema.optional<Schema.Number>;
787
- /** Alt text for the image. */
788
- readonly alt: Schema.optional<Schema.String>;
789
- }>]>;
790
- /** @public */
791
- type OpenGraphImageConfig = typeof OpenGraphImageConfig.Type;
792
- /**
793
- * Resolved Open Graph metadata emitted into each generated page's frontmatter.
794
- *
795
- * @public
796
- */
797
- declare const OpenGraphMetadata: Schema.Struct<{
798
- /** Canonical site base URL. */
799
- readonly siteUrl: Schema.String;
800
- /** Page route path (e.g. `/api/classes/myclass`). */
801
- readonly pageRoute: Schema.String;
802
- /** Page description for the `og:description` tag. */
803
- readonly description: Schema.String;
804
- /** ISO 8601 date string for `article:published_time`. */
805
- readonly publishedTime: Schema.String;
806
- /** ISO 8601 date string for `article:modified_time`. */
807
- readonly modifiedTime: Schema.String;
808
- /** Article section label (e.g. `"API"`). */
809
- readonly section: Schema.String;
810
- /** Article tag keywords. */
811
- readonly tags: Schema.mutable<Schema.$Array<Schema.String>>;
812
- /** Optional structured image metadata. */
813
- readonly ogImage: Schema.optional<Schema.Struct<{
814
- /** Absolute URL of the image. */
815
- readonly url: Schema.String;
816
- /** HTTPS URL of the image (for secure contexts). */
817
- readonly secureUrl: Schema.optional<Schema.String>;
818
- /** MIME type of the image (e.g. `"image/png"`). */
819
- readonly type: Schema.optional<Schema.String>;
820
- /** Image width in pixels. */
821
- readonly width: Schema.optional<Schema.Number>;
822
- /** Image height in pixels. */
823
- readonly height: Schema.optional<Schema.Number>;
824
- /** Alt text for the image. */
825
- readonly alt: Schema.optional<Schema.String>;
826
- }>>;
827
- /** Open Graph object type (e.g. `"article"`). */
828
- readonly ogType: Schema.String;
829
- }>;
830
- /** @public */
831
- type OpenGraphMetadata = typeof OpenGraphMetadata.Type;
832
- //#endregion
833
747
  //#region src/config-helpers.d.ts
834
748
  /**
835
749
  * Metadata discovered from a single rslib-builder localPaths package folder.
@@ -940,6 +854,90 @@ declare const ApiExtractorPlugin: typeof ApiExtractorPluginImpl & {
940
854
  };
941
855
  };
942
856
  //#endregion
857
+ //#region src/schemas/opengraph.d.ts
858
+ /**
859
+ * Structured Open Graph image metadata (alternative to a plain URL string).
860
+ *
861
+ * @public
862
+ */
863
+ declare const OpenGraphImageMetadata: Schema.Struct<{
864
+ /** Absolute URL of the image. */
865
+ readonly url: Schema.String;
866
+ /** HTTPS URL of the image (for secure contexts). */
867
+ readonly secureUrl: Schema.optional<Schema.String>;
868
+ /** MIME type of the image (e.g. `"image/png"`). */
869
+ readonly type: Schema.optional<Schema.String>;
870
+ /** Image width in pixels. */
871
+ readonly width: Schema.optional<Schema.Number>;
872
+ /** Image height in pixels. */
873
+ readonly height: Schema.optional<Schema.Number>;
874
+ /** Alt text for the image. */
875
+ readonly alt: Schema.optional<Schema.String>;
876
+ }>;
877
+ /** @public */
878
+ type OpenGraphImageMetadata = typeof OpenGraphImageMetadata.Type;
879
+ /**
880
+ * Open Graph image: either a plain URL string or structured `OpenGraphImageMetadata`.
881
+ *
882
+ * @public
883
+ */
884
+ declare const OpenGraphImageConfig: Schema.Union<readonly [Schema.String, Schema.Struct<{
885
+ /** Absolute URL of the image. */
886
+ readonly url: Schema.String;
887
+ /** HTTPS URL of the image (for secure contexts). */
888
+ readonly secureUrl: Schema.optional<Schema.String>;
889
+ /** MIME type of the image (e.g. `"image/png"`). */
890
+ readonly type: Schema.optional<Schema.String>;
891
+ /** Image width in pixels. */
892
+ readonly width: Schema.optional<Schema.Number>;
893
+ /** Image height in pixels. */
894
+ readonly height: Schema.optional<Schema.Number>;
895
+ /** Alt text for the image. */
896
+ readonly alt: Schema.optional<Schema.String>;
897
+ }>]>;
898
+ /** @public */
899
+ type OpenGraphImageConfig = typeof OpenGraphImageConfig.Type;
900
+ /**
901
+ * Resolved Open Graph metadata emitted into each generated page's frontmatter.
902
+ *
903
+ * @public
904
+ */
905
+ declare const OpenGraphMetadata: Schema.Struct<{
906
+ /** Canonical site base URL. */
907
+ readonly siteUrl: Schema.String;
908
+ /** Page route path (e.g. `/api/classes/myclass`). */
909
+ readonly pageRoute: Schema.String;
910
+ /** Page description for the `og:description` tag. */
911
+ readonly description: Schema.String;
912
+ /** ISO 8601 date string for `article:published_time`. */
913
+ readonly publishedTime: Schema.String;
914
+ /** ISO 8601 date string for `article:modified_time`. */
915
+ readonly modifiedTime: Schema.String;
916
+ /** Article section label (e.g. `"API"`). */
917
+ readonly section: Schema.String;
918
+ /** Article tag keywords. */
919
+ readonly tags: Schema.mutable<Schema.$Array<Schema.String>>;
920
+ /** Optional structured image metadata. */
921
+ readonly ogImage: Schema.optional<Schema.Struct<{
922
+ /** Absolute URL of the image. */
923
+ readonly url: Schema.String;
924
+ /** HTTPS URL of the image (for secure contexts). */
925
+ readonly secureUrl: Schema.optional<Schema.String>;
926
+ /** MIME type of the image (e.g. `"image/png"`). */
927
+ readonly type: Schema.optional<Schema.String>;
928
+ /** Image width in pixels. */
929
+ readonly width: Schema.optional<Schema.Number>;
930
+ /** Image height in pixels. */
931
+ readonly height: Schema.optional<Schema.Number>;
932
+ /** Alt text for the image. */
933
+ readonly alt: Schema.optional<Schema.String>;
934
+ }>>;
935
+ /** Open Graph object type (e.g. `"article"`). */
936
+ readonly ogType: Schema.String;
937
+ }>;
938
+ /** @public */
939
+ type OpenGraphMetadata = typeof OpenGraphMetadata.Type;
940
+ //#endregion
943
941
  //#region src/serve.d.ts
944
942
  /**
945
943
  * Which RSPress server {@link serve} runs.