rspress-plugin-api-extractor 0.2.2 → 0.3.1

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 (51) hide show
  1. package/api-extracted-package.js +2 -1
  2. package/build-program.js +20 -12
  3. package/build-stages.js +124 -30
  4. package/config-utils.js +36 -7
  5. package/content-hash.js +1 -1
  6. package/errors.js +1 -1
  7. package/index.d.ts +329 -202
  8. package/layers/ConfigServiceLive.js +300 -136
  9. package/layers/ObservabilityLive.js +49 -85
  10. package/layers/TypeRegistryServiceLive.js +122 -21
  11. package/layers/build-metrics.js +61 -0
  12. package/llms-program.js +29 -7
  13. package/loader.js +16 -2
  14. package/markdown/helpers.js +1 -1
  15. package/markdown/index.js +1 -1
  16. package/markdown/shiki-utils.js +16 -2
  17. package/observability/EventBus.js +38 -0
  18. package/observability/events.js +17 -0
  19. package/observability/sinks/console-sink.js +63 -0
  20. package/observability/sinks/metrics-sink.js +68 -0
  21. package/observability/sinks/trace-sink.js +38 -0
  22. package/observability/spans.js +57 -0
  23. package/og-resolver.js +37 -5
  24. package/package.json +6 -6
  25. package/plugin.js +73 -19
  26. package/prettier-formatter.js +15 -4
  27. package/remark-api-codeblocks.js +22 -3
  28. package/remark-with-api.js +27 -14
  29. package/route-collisions.js +1 -1
  30. package/runtime/components/ApiExample/index.js +5 -5
  31. package/runtime/components/ApiMember/index.js +5 -7
  32. package/runtime/components/ApiSignature/index.js +4 -6
  33. package/runtime/components/EnumMembersTable/index.js +5 -0
  34. package/runtime/components/ExampleBlock/index.js +5 -3
  35. package/runtime/components/MemberSignature/index.js +4 -2
  36. package/runtime/components/ParametersTable/index.js +5 -0
  37. package/runtime/components/SignatureBlock/index.js +4 -2
  38. package/runtime/components/shared/variables.css +0 -15
  39. package/runtime/index.d.ts +65 -399
  40. package/runtime/index.js +1 -5
  41. package/runtime/utils/hast-renderer.js +1 -0
  42. package/schemas/config.js +105 -4
  43. package/schemas/index.js +3 -2
  44. package/schemas/observability.js +62 -0
  45. package/schemas/opengraph.js +30 -0
  46. package/schemas/performance.js +1 -1
  47. package/serve.js +13 -0
  48. package/tsconfig-parser.js +1 -1
  49. package/twoslash-patterns.js +1 -1
  50. package/twoslash-transformer.js +93 -8
  51. package/typescript-config.js +1 -1
@@ -1,13 +1,21 @@
1
- import { BuildMetrics } from "./layers/ObservabilityLive.js";
1
+ import { PluginEvent } from "./observability/events.js";
2
2
  import { formatCode } from "./prettier-formatter.js";
3
3
  import { stripTwoslashDirectives } from "./markdown/helpers.js";
4
+ import { TwoslashManager } from "./twoslash-transformer.js";
4
5
  import { DEFAULT_SHIKI_THEMES } from "./markdown/shiki-utils.js";
5
- import { Effect, Metric } from "effect";
6
6
  import { codeToHast, hastToHtml } from "shiki";
7
7
  import { visit } from "unist-util-visit";
8
8
 
9
9
  //#region src/remark-with-api.ts
10
- /* v8 ignore start -- remark plugin, requires MDX compilation context */
10
+ /** Module-level emitter injected by plugin.ts at startup. */
11
+ let emitEvent = () => {};
12
+ let currentBuildId = "";
13
+ let currentSlowCodeBlockMs = 500;
14
+ function setRemarkWithApiEventEmitter(fn, buildId = "", slowCodeBlockMs = 500) {
15
+ emitEvent = fn;
16
+ currentBuildId = buildId;
17
+ currentSlowCodeBlockMs = slowCodeBlockMs;
18
+ }
11
19
  /**
12
20
  * Supported languages for with-api code blocks
13
21
  * Based on GitHub Linguist standard aliases:
@@ -15,7 +23,7 @@ import { visit } from "unist-util-visit";
15
23
  * - JavaScript: javascript, js, node
16
24
  * - TSX/JSX: tsx, jsx (Shiki-supported)
17
25
  */
18
- const SUPPORTED_LANGUAGES = new Set([
26
+ const SUPPORTED_LANGUAGES = /* @__PURE__ */ new Set([
19
27
  "typescript",
20
28
  "ts",
21
29
  "javascript",
@@ -53,22 +61,20 @@ const remarkWithApi = (options) => {
53
61
  const { shikiCrossLinker, getTransformer, theme } = options;
54
62
  const resolvedTheme = theme ?? DEFAULT_SHIKI_THEMES;
55
63
  return async function remarkTransformer(tree, file) {
56
- const fileStart = performance.now();
57
64
  const promises = [];
58
- let blockCount = 0;
59
65
  let needsApiExampleImport = false;
60
66
  const isSsgMd = import.meta.env?.SSG_MD || process.env.RSBUILD_ENVIRONMENT === "node_md" || process.env.BUILD_TARGET === "node_md";
61
67
  const currentFilePath = file.path;
62
68
  if (currentFilePath) {
63
69
  const apiScope = inferApiScope(currentFilePath);
64
70
  if (apiScope) shikiCrossLinker.setApiScope(apiScope);
71
+ TwoslashManager.getInstance().setCurrentFile(currentFilePath);
65
72
  }
66
73
  visit(tree, "code", (node, index, parent) => {
67
74
  const hasWithApi = node.meta?.includes("with-api");
68
75
  const lang = node.lang || "typescript";
69
76
  const isSupported = SUPPORTED_LANGUAGES.has(lang);
70
77
  if (!hasWithApi || !isSupported) return;
71
- blockCount++;
72
78
  const promise = (async () => {
73
79
  const blockStart = performance.now();
74
80
  const rawCode = node.value;
@@ -91,10 +97,19 @@ const remarkWithApi = (options) => {
91
97
  if (apiScope) hast = shikiCrossLinker.transformHast(hast, apiScope);
92
98
  const shikiTime = performance.now() - shikiStart;
93
99
  const totalBlockTime = performance.now() - blockStart;
94
- Effect.runSync(Metric.increment(BuildMetrics.codeblockTotal));
95
- Effect.runSync(Metric.update(BuildMetrics.codeblockDuration, totalBlockTime));
96
- if (shikiTime > 0) Effect.runSync(Metric.update(BuildMetrics.codeblockShikiDuration, shikiTime));
97
- if (totalBlockTime > 100) Effect.runSync(Metric.increment(BuildMetrics.codeblockSlow));
100
+ const isSlow = totalBlockTime > currentSlowCodeBlockMs;
101
+ emitEvent(PluginEvent.CodeBlockProcessed({
102
+ ctx: {
103
+ buildId: currentBuildId,
104
+ ...currentFilePath != null ? { file: currentFilePath } : {}
105
+ },
106
+ lang,
107
+ shikiMs: shikiTime,
108
+ twoslashMs: totalBlockTime - shikiTime,
109
+ totalMs: totalBlockTime,
110
+ slow: isSlow,
111
+ level: "debug"
112
+ }));
98
113
  if (parent && typeof index === "number") if (isSsgMd) {
99
114
  const cleanCode = hastToHtml(hast).replace(/<[^>]*>/g, "").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, "\"").replace(/&#39;/g, "'").trim();
100
115
  node.lang = "typescript";
@@ -163,10 +178,8 @@ const remarkWithApi = (options) => {
163
178
  tree.children.splice(insertIndex, 0, importNode);
164
179
  }
165
180
  }
166
- const fileTime = performance.now() - fileStart;
167
- if (blockCount > 0) console.log(`⏱️ [remark-with-api] Processed ${blockCount} blocks in ${fileTime.toFixed(0)}ms (avg: ${(fileTime / blockCount).toFixed(0)}ms per block)`);
168
181
  };
169
182
  };
170
183
 
171
184
  //#endregion
172
- export { remarkWithApi };
185
+ export { remarkWithApi, setRemarkWithApiEventEmitter };
@@ -49,4 +49,4 @@ function assertNoRouteCollisions(candidates, baseRoute) {
49
49
  }
50
50
 
51
51
  //#endregion
52
- export { assertNoRouteCollisions };
52
+ export { assertNoRouteCollisions, detectRouteCollisions, formatRouteCollisionError };
@@ -5,13 +5,13 @@ import { Fragment, jsx } from "react/jsx-runtime";
5
5
 
6
6
  //#region src/runtime/components/ApiExample/index.tsx
7
7
  /**
8
- * Renders an example code block.
8
+ * Renders an example code block in generated API documentation pages.
9
9
  *
10
- * Replaces ExampleBlockWrapper with a simpler component that takes a plain
11
- * code string (no base64, no HAST for code). The code should already have
12
- * Twoslash directives stripped.
10
+ * In browser mode renders a Shiki-highlighted code block with copy and wrap
11
+ * controls. In SSG-MD mode renders a plain fenced code block.
13
12
  *
14
- * In SSG-MD mode, renders a plain code block with the example code.
13
+ * @param props - {@link ApiExampleProps}
14
+ * @public
15
15
  */
16
16
  function ApiExample({ code, hast }) {
17
17
  const parsedHast = useMemo(() => decodeHast(hast, "ApiExample"), [hast]);
@@ -5,15 +5,13 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
5
 
6
6
  //#region src/runtime/components/ApiMember/index.tsx
7
7
  /**
8
- * Renders an individual class/interface member signature block.
8
+ * Renders an individual class or interface member signature block.
9
9
  *
10
- * Replaces MemberSignatureWrapper with a simpler component that takes plain
11
- * string props. The `code` prop contains only the member signature (not
12
- * wrapped in a class skeleton), and `summary` is plain markdown-compatible
13
- * text (not HTML with anchor tags, not base64-encoded).
10
+ * In browser mode renders an `h3` heading with a Shiki-highlighted signature.
11
+ * In SSG-MD mode renders a heading, summary, and plain fenced code block.
14
12
  *
15
- * In SSG-MD mode, renders a heading with member name, summary text,
16
- * and a plain code block with the member signature.
13
+ * @param props - {@link ApiMemberProps}
14
+ * @public
17
15
  */
18
16
  function ApiMember({ code, memberName, summary, id, hast, hasParameters }) {
19
17
  const parsedHast = useMemo(() => decodeHast(hast, "ApiMember"), [hast]);
@@ -7,13 +7,11 @@ import { Fragment, jsx } from "react/jsx-runtime";
7
7
  /**
8
8
  * Renders a full API type signature block.
9
9
  *
10
- * Replaces SignatureBlockWrapper with a simpler component that takes plain
11
- * string props (no base64 encoding for code). Produces clean semantic HTML
12
- * that RSPress converts to LLM-readable markdown in SSG-MD mode, and renders
13
- * interactive Shiki-highlighted code in browser mode.
10
+ * In browser mode renders an interactive Shiki-highlighted code block.
11
+ * In SSG-MD mode renders a plain fenced code block for LLM consumption.
14
12
  *
15
- * In SSG-MD mode, renders a "Signature" heading followed by a plain
16
- * code block with the type signature.
13
+ * @param props - {@link ApiSignatureProps}
14
+ * @public
17
15
  */
18
16
  function ApiSignature({ code, heading: _heading = "Signature", id: _id = "signature", hast, hasParameters, hasMembers }) {
19
17
  const parsedHast = useMemo(() => decodeHast(hast, "ApiSignature"), [hast]);
@@ -3,6 +3,11 @@ import index_module_default from "./index.module.js";
3
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
4
 
5
5
  //#region src/runtime/components/EnumMembersTable/index.tsx
6
+ /**
7
+ * Renders a table of enum members with their values and descriptions.
8
+ *
9
+ * @public
10
+ */
6
11
  const EnumMembersTable = ({ members }) => {
7
12
  if (!members || members.length === 0) return null;
8
13
  if (import.meta.env.SSG_MD) {
@@ -9,9 +9,11 @@ import { jsx, jsxs } from "react/jsx-runtime";
9
9
 
10
10
  //#region src/runtime/components/ExampleBlock/index.tsx
11
11
  /**
12
- * Code block for examples - displays syntax-highlighted code without a heading
13
- * Similar to SignatureBlock but without the "Signature" header
14
- * Includes both copy and wrap buttons in the toolbar
12
+ * Code block for examples displays syntax-highlighted code without a heading.
13
+ * Similar to {@link SignatureBlock} but without the "Signature" header.
14
+ * Includes both copy and wrap buttons in the toolbar.
15
+ *
16
+ * @internal
15
17
  */
16
18
  function ExampleBlock({ hast, code }) {
17
19
  const { wrapped, toggleWrap } = useWrapToggle();
@@ -8,8 +8,10 @@ import clsx from "clsx";
8
8
 
9
9
  //#region src/runtime/components/MemberSignature/index.tsx
10
10
  /**
11
- * Interactive member signature block with h3 header and wrap button
12
- * Displays syntax-highlighted TypeScript member signatures with hover tooltips
11
+ * Interactive member signature block with h3 header and wrap button.
12
+ * Displays syntax-highlighted TypeScript member signatures with hover tooltips.
13
+ *
14
+ * @internal
13
15
  */
14
16
  function MemberSignature({ hast, memberName, id, summary, hasParameters = false }) {
15
17
  const { wrapped, toggleWrap } = useWrapToggle();
@@ -3,6 +3,11 @@ import index_module_default from "./index.module.js";
3
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
4
 
5
5
  //#region src/runtime/components/ParametersTable/index.tsx
6
+ /**
7
+ * Renders a table of function or method parameters with their types and descriptions.
8
+ *
9
+ * @public
10
+ */
6
11
  const ParametersTable = ({ parameters }) => {
7
12
  if (!parameters || parameters.length === 0) return null;
8
13
  if (import.meta.env.SSG_MD) {
@@ -8,8 +8,10 @@ import clsx from "clsx";
8
8
 
9
9
  //#region src/runtime/components/SignatureBlock/index.tsx
10
10
  /**
11
- * Interactive signature block with wrap button
12
- * Displays syntax-highlighted TypeScript signatures with hover tooltips
11
+ * Interactive signature block with wrap button.
12
+ * Displays syntax-highlighted TypeScript signatures with hover tooltips.
13
+ *
14
+ * @internal
13
15
  */
14
16
  function SignatureBlock({ hast, heading = "Signature", id = "signature", hasParameters = false }) {
15
17
  const { wrapped, toggleWrap } = useWrapToggle();
@@ -65,18 +65,3 @@ html.rp-dark {
65
65
  --api-color-link-hover: #79c0ff;
66
66
  --api-color-code-bg: #6e768166;
67
67
  }
68
-
69
- html.rp-dark .rp-social-links__item {
70
- background-color: var(--api-color-bg-secondary);
71
- border-color: var(--api-color-border);
72
- color: var(--api-color-text);
73
- }
74
-
75
- html.rp-dark .rp-social-links__item:hover {
76
- background-color: var(--api-color-hover-bg);
77
- border-color: var(--api-color-border-hover);
78
- }
79
-
80
- html.rp-dark .rp-social-links__item:active {
81
- background-color: var(--api-color-active-bg);
82
- }