rspress-plugin-api-extractor 0.1.2 → 0.2.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 (109) hide show
  1. package/README.md +2 -2
  2. package/api-extracted-package.js +426 -0
  3. package/build-program.js +120 -0
  4. package/build-stages.js +664 -0
  5. package/category-resolver.js +50 -0
  6. package/code-post-processor.js +38 -0
  7. package/config-helpers.js +106 -0
  8. package/config-utils.js +258 -0
  9. package/content-hash.js +79 -0
  10. package/errors.js +29 -0
  11. package/formatter.js +69 -0
  12. package/hide-cut-transformer.js +96 -0
  13. package/index.d.ts +901 -831
  14. package/index.js +4 -6128
  15. package/layers/ConfigServiceLive.js +377 -0
  16. package/layers/ObservabilityLive.js +136 -0
  17. package/layers/PathDerivationServiceLive.js +16 -0
  18. package/layers/SnapshotServiceLive.js +94 -0
  19. package/layers/TypeRegistryServiceLive.js +46 -0
  20. package/llms-processing.js +270 -0
  21. package/llms-program.js +262 -0
  22. package/loader.js +186 -0
  23. package/markdown/cross-linker.js +156 -0
  24. package/markdown/helpers.js +364 -0
  25. package/markdown/index.js +11 -0
  26. package/markdown/page-generators/class-page.js +357 -0
  27. package/markdown/page-generators/enum-page.js +152 -0
  28. package/markdown/page-generators/function-page.js +127 -0
  29. package/markdown/page-generators/index-pages.js +27 -0
  30. package/markdown/page-generators/interface-page.js +307 -0
  31. package/markdown/page-generators/namespace-page.js +280 -0
  32. package/markdown/page-generators/type-alias-page.js +110 -0
  33. package/markdown/page-generators/variable-page.js +110 -0
  34. package/markdown/shiki-utils.js +48 -0
  35. package/migrations/001_create_snapshots.js +25 -0
  36. package/model-loader.js +95 -0
  37. package/multi-entry-resolver.js +70 -0
  38. package/og-resolver.js +271 -0
  39. package/package.json +64 -73
  40. package/path-derivation.js +48 -0
  41. package/plugin.js +218 -0
  42. package/prettier-formatter.js +73 -0
  43. package/public/tsconfig/rspress.json +44 -0
  44. package/remark-api-codeblocks.js +130 -0
  45. package/remark-with-api.js +172 -0
  46. package/route-collisions.js +52 -0
  47. package/runtime/components/ApiExample/index.js +29 -25
  48. package/runtime/components/ApiLlmsPackageActions/index.js +252 -323
  49. package/runtime/components/ApiLlmsViewOptions/index.js +269 -340
  50. package/runtime/components/ApiMember/index.js +49 -47
  51. package/runtime/components/ApiSignature/index.js +32 -28
  52. package/runtime/components/EnumMembersTable/{index_module.css → index.css} +18 -19
  53. package/runtime/components/EnumMembersTable/index.js +36 -67
  54. package/runtime/components/EnumMembersTable/index.module.js +9 -6
  55. package/runtime/components/ExampleBlock/{index_module.css → index.css} +2 -3
  56. package/runtime/components/ExampleBlock/index.js +23 -28
  57. package/runtime/components/ExampleBlock/index.module.js +8 -5
  58. package/runtime/components/MarkdownContent/index.js +26 -18
  59. package/runtime/components/MarkdownText/index.js +28 -22
  60. package/runtime/components/MemberSignature/{index_module.css → index.css} +5 -6
  61. package/runtime/components/MemberSignature/index.js +46 -46
  62. package/runtime/components/MemberSignature/index.module.js +9 -6
  63. package/runtime/components/ParametersTable/{index_module.css → index.css} +19 -20
  64. package/runtime/components/ParametersTable/index.js +36 -67
  65. package/runtime/components/ParametersTable/index.module.js +9 -6
  66. package/runtime/components/SignatureBlock/{index_module.css → index.css} +5 -6
  67. package/runtime/components/SignatureBlock/index.js +30 -29
  68. package/runtime/components/SignatureBlock/index.module.js +9 -6
  69. package/runtime/components/SignatureCode/{index_module.css → index.css} +9 -10
  70. package/runtime/components/SignatureCode/index.js +40 -32
  71. package/runtime/components/SignatureCode/index.module.js +9 -6
  72. package/runtime/components/SignatureToolbar/{index_module.css → index.css} +18 -20
  73. package/runtime/components/SignatureToolbar/index.js +48 -51
  74. package/runtime/components/SignatureToolbar/index.module.js +13 -10
  75. package/runtime/components/buttons/ButtonGroup.js +13 -6
  76. package/runtime/components/buttons/CopyCodeButton.js +40 -38
  77. package/runtime/components/buttons/WrapSignatureButton.js +20 -16
  78. package/runtime/components/buttons/{index_module.css → index.css} +5 -6
  79. package/runtime/components/buttons/index.module.js +8 -5
  80. package/runtime/components/icons/CheckIcon/index.js +20 -17
  81. package/runtime/components/icons/CopyIcon/index.js +20 -17
  82. package/runtime/components/icons/UnwrapIcon/index.js +22 -18
  83. package/runtime/components/icons/WrapIcon/index.js +20 -17
  84. package/runtime/components/shared/_twoslash.css +3 -10
  85. package/runtime/components/shared/variables.css +0 -3
  86. package/runtime/hooks/useWrapToggle.js +32 -9
  87. package/runtime/index.d.ts +513 -173
  88. package/runtime/index.js +11 -9
  89. package/runtime/utils/decode-hast.js +31 -16
  90. package/runtime/utils/hast-renderer.js +21 -7
  91. package/schemas/config.js +199 -0
  92. package/schemas/index.js +5 -0
  93. package/schemas/opengraph.js +26 -0
  94. package/schemas/performance.js +19 -0
  95. package/serve.js +133 -0
  96. package/services/ConfigService.js +7 -0
  97. package/services/PathDerivationService.js +7 -0
  98. package/services/SnapshotService.js +7 -0
  99. package/services/TypeRegistryService.js +7 -0
  100. package/shiki-transformer.js +758 -0
  101. package/tsconfig-parser.js +127 -0
  102. package/tsdoc-metadata.json +11 -11
  103. package/twoslash-patterns.js +87 -0
  104. package/twoslash-transformer.js +316 -0
  105. package/type-reference-extractor.js +201 -0
  106. package/typescript-config.js +168 -0
  107. package/vfs-registry.js +121 -0
  108. package/0~llms-program.js +0 -344
  109. package/runtime/components/ApiLlmsPackageActions/index.module.js +0 -2
package/runtime/index.js CHANGED
@@ -1,11 +1,13 @@
1
1
  import "./components/shared/variables.css";
2
2
  import "./components/shared/_twoslash.css";
3
- export { ApiExample } from "./components/ApiExample/index.js";
4
- export { ApiMember } from "./components/ApiMember/index.js";
5
- export { ApiSignature } from "./components/ApiSignature/index.js";
6
- export { EnumMembersTable } from "./components/EnumMembersTable/index.js";
7
- export { ExampleBlock } from "./components/ExampleBlock/index.js";
8
- export { MemberSignature } from "./components/MemberSignature/index.js";
9
- export { ParametersTable } from "./components/ParametersTable/index.js";
10
- export { SignatureBlock } from "./components/SignatureBlock/index.js";
11
- export { hastToReact } from "./utils/hast-renderer.js";
3
+ import { hastToReact } from "./utils/hast-renderer.js";
4
+ import { ExampleBlock } from "./components/ExampleBlock/index.js";
5
+ import { ApiExample } from "./components/ApiExample/index.js";
6
+ import { MemberSignature } from "./components/MemberSignature/index.js";
7
+ import { ApiMember } from "./components/ApiMember/index.js";
8
+ import { SignatureBlock } from "./components/SignatureBlock/index.js";
9
+ import { ApiSignature } from "./components/ApiSignature/index.js";
10
+ import { EnumMembersTable } from "./components/EnumMembersTable/index.js";
11
+ import { ParametersTable } from "./components/ParametersTable/index.js";
12
+
13
+ export { ApiExample, ApiMember, ApiSignature, EnumMembersTable, ExampleBlock, MemberSignature, ParametersTable, SignatureBlock, hastToReact };
@@ -1,18 +1,33 @@
1
+ //#region src/runtime/utils/decode-hast.ts
2
+ /**
3
+ * Decode a HAST value that may be base64-encoded, plain JSON, or already parsed.
4
+ *
5
+ * Supports multiple input formats:
6
+ * - Root object (direct HAST) - returned as-is
7
+ * - Base64-encoded JSON string (from serializeHastForMdx)
8
+ * - Plain JSON string (legacy format)
9
+ *
10
+ * @param hast - The HAST value to decode
11
+ * @param componentName - Optional component name for warning messages
12
+ * @returns Parsed HAST Root or null if decoding fails
13
+ */
1
14
  function decodeHast(hast, componentName) {
2
- if (!hast) return null;
3
- if ("string" != typeof hast) return hast;
4
- try {
5
- if (/^[A-Za-z0-9+/=]+$/.test(hast) && hast.length > 20) {
6
- const binaryString = atob(hast);
7
- const bytes = Uint8Array.from(binaryString, (char)=>char.charCodeAt(0));
8
- const json = new TextDecoder("utf-8").decode(bytes);
9
- return JSON.parse(json);
10
- }
11
- return JSON.parse(hast);
12
- } catch {
13
- const name = componentName ? `${componentName}: ` : "";
14
- console.warn(`${name}Failed to decode HAST`);
15
- return null;
16
- }
15
+ if (!hast) return null;
16
+ if (typeof hast !== "string") return hast;
17
+ try {
18
+ if (/^[A-Za-z0-9+/=]+$/.test(hast) && hast.length > 20) {
19
+ const binaryString = atob(hast);
20
+ const bytes = Uint8Array.from(binaryString, (char) => char.charCodeAt(0));
21
+ const json = new TextDecoder("utf-8").decode(bytes);
22
+ return JSON.parse(json);
23
+ }
24
+ return JSON.parse(hast);
25
+ } catch {
26
+ const name = componentName ? `${componentName}: ` : "";
27
+ console.warn(`${name}Failed to decode HAST`);
28
+ return null;
29
+ }
17
30
  }
18
- export { decodeHast };
31
+
32
+ //#endregion
33
+ export { decodeHast };
@@ -1,10 +1,24 @@
1
- import { toJsxRuntime } from "hast-util-to-jsx-runtime";
2
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { toJsxRuntime } from "hast-util-to-jsx-runtime";
3
+
4
+ //#region src/runtime/utils/hast-renderer.tsx
5
+ /**
6
+ * Convert a HAST (Hypertext Abstract Syntax Tree) root to a React element.
7
+ *
8
+ * This function is used to render pre-generated Shiki HAST trees at runtime,
9
+ * avoiding the need for `dangerouslySetInnerHTML` and eliminating MDX parsing
10
+ * issues caused by long HTML strings with special characters.
11
+ *
12
+ * @param hast - The HAST root node from Shiki's `codeToHast()`
13
+ * @returns A React element representing the HAST tree
14
+ */
3
15
  function hastToReact(hast) {
4
- return toJsxRuntime(hast, {
5
- Fragment: Fragment,
6
- jsx: jsx,
7
- jsxs: jsxs
8
- });
16
+ return toJsxRuntime(hast, {
17
+ Fragment,
18
+ jsx,
19
+ jsxs
20
+ });
9
21
  }
10
- export { hastToReact };
22
+
23
+ //#endregion
24
+ export { hastToReact };
@@ -0,0 +1,199 @@
1
+ import { OpenGraphImageConfig } from "./opengraph.js";
2
+ import { PerformanceConfig } from "./performance.js";
3
+ import { Schema } from "effect";
4
+ import { ApiItemKind } from "@microsoft/api-extractor-model";
5
+
6
+ //#region src/schemas/config.ts
7
+ /**
8
+ * Opaque input type for config fields that accept a file path string,
9
+ * an async loader function, or a URL.
10
+ */
11
+ const ModelInput = Schema.declare((input) => typeof input === "string" || typeof input === "function" || input instanceof URL);
12
+ const LogLevel$1 = Schema.Literal("none", "info", "verbose", "debug", "warn", "error");
13
+ const ExternalPackageSpec = Schema.mutable(Schema.Struct({
14
+ name: Schema.String,
15
+ version: Schema.String,
16
+ tsconfig: Schema.optional(ModelInput),
17
+ compilerOptions: Schema.optional(Schema.Unknown)
18
+ }));
19
+ const AutoDetectDependencies = Schema.mutable(Schema.Struct({
20
+ dependencies: Schema.optionalWith(Schema.Boolean, { default: () => false }),
21
+ devDependencies: Schema.optionalWith(Schema.Boolean, { default: () => false }),
22
+ peerDependencies: Schema.optionalWith(Schema.Boolean, { default: () => true }),
23
+ autoDependencies: Schema.optionalWith(Schema.Boolean, { default: () => true })
24
+ }));
25
+ const ErrorConfig = Schema.mutable(Schema.Struct({ example: Schema.optional(Schema.Literal("suppress", "show")) }));
26
+ const LlmsPlugin = Schema.mutable(Schema.Struct({
27
+ enabled: Schema.optionalWith(Schema.Boolean, { default: () => true }),
28
+ scopes: Schema.optionalWith(Schema.Boolean, { default: () => true }),
29
+ apiTxt: Schema.optionalWith(Schema.Boolean, { default: () => true }),
30
+ showCopyButton: Schema.optionalWith(Schema.Boolean, { default: () => true }),
31
+ showViewOptions: Schema.optionalWith(Schema.Boolean, { default: () => true }),
32
+ copyButtonText: Schema.optionalWith(Schema.String, { default: () => "Copy Markdown" }),
33
+ viewOptions: Schema.optionalWith(Schema.mutable(Schema.Array(Schema.Literal("markdownLink", "chatgpt", "claude"))), { default: () => [
34
+ "markdownLink",
35
+ "chatgpt",
36
+ "claude"
37
+ ] })
38
+ }));
39
+ const ApiItemKindSchema = Schema.declare((input) => typeof input === "number");
40
+ const CategoryConfig = Schema.mutable(Schema.Struct({
41
+ displayName: Schema.String,
42
+ singularName: Schema.String,
43
+ folderName: Schema.String,
44
+ itemKinds: Schema.optional(Schema.mutable(Schema.Array(ApiItemKindSchema))),
45
+ tsdocModifier: Schema.optional(Schema.String),
46
+ collapsible: Schema.optionalWith(Schema.Boolean, { default: () => true }),
47
+ collapsed: Schema.optionalWith(Schema.Boolean, { default: () => true }),
48
+ overviewHeaders: Schema.optionalWith(Schema.mutable(Schema.Array(Schema.Number)), { default: () => [2] })
49
+ }));
50
+ const SourceConfig = Schema.mutable(Schema.Struct({
51
+ url: Schema.String,
52
+ ref: Schema.optional(Schema.String)
53
+ }));
54
+ const ThemeConfig = Schema.Union(Schema.String, Schema.mutable(Schema.Struct({
55
+ light: Schema.String,
56
+ dark: Schema.String
57
+ })), Schema.mutable(Schema.Record({
58
+ key: Schema.String,
59
+ value: Schema.Unknown
60
+ })));
61
+ /**
62
+ * Built-in default categories
63
+ */
64
+ const DEFAULT_CATEGORIES = {
65
+ classes: {
66
+ displayName: "Classes",
67
+ singularName: "Class",
68
+ folderName: "class",
69
+ itemKinds: [ApiItemKind.Class],
70
+ collapsible: true,
71
+ collapsed: true,
72
+ overviewHeaders: [2]
73
+ },
74
+ interfaces: {
75
+ displayName: "Interfaces",
76
+ singularName: "Interface",
77
+ folderName: "interface",
78
+ itemKinds: [ApiItemKind.Interface],
79
+ collapsible: true,
80
+ collapsed: true,
81
+ overviewHeaders: [2]
82
+ },
83
+ functions: {
84
+ displayName: "Functions",
85
+ singularName: "Function",
86
+ folderName: "function",
87
+ itemKinds: [ApiItemKind.Function],
88
+ collapsible: true,
89
+ collapsed: true,
90
+ overviewHeaders: [2]
91
+ },
92
+ types: {
93
+ displayName: "Types",
94
+ singularName: "Type",
95
+ folderName: "type",
96
+ itemKinds: [ApiItemKind.TypeAlias],
97
+ collapsible: true,
98
+ collapsed: true,
99
+ overviewHeaders: [2]
100
+ },
101
+ enums: {
102
+ displayName: "Enums",
103
+ singularName: "Enum",
104
+ folderName: "enum",
105
+ itemKinds: [ApiItemKind.Enum],
106
+ collapsible: true,
107
+ collapsed: true,
108
+ overviewHeaders: [2]
109
+ },
110
+ variables: {
111
+ displayName: "Variables",
112
+ singularName: "Variable",
113
+ folderName: "variable",
114
+ itemKinds: [ApiItemKind.Variable],
115
+ collapsible: true,
116
+ collapsed: true,
117
+ overviewHeaders: [2]
118
+ },
119
+ namespaces: {
120
+ displayName: "Namespaces",
121
+ singularName: "Namespace",
122
+ folderName: "namespace",
123
+ itemKinds: [ApiItemKind.Namespace],
124
+ collapsible: true,
125
+ collapsed: true,
126
+ overviewHeaders: [2]
127
+ }
128
+ };
129
+ /** Reusable categories record (internal helper) */
130
+ const CategoriesRecord = Schema.mutable(Schema.Record({
131
+ key: Schema.String,
132
+ value: CategoryConfig
133
+ }));
134
+ const VersionConfig = Schema.mutable(Schema.Struct({
135
+ model: ModelInput,
136
+ packageJson: Schema.optional(ModelInput),
137
+ categories: Schema.optional(CategoriesRecord),
138
+ source: Schema.optional(SourceConfig),
139
+ externalPackages: Schema.optional(Schema.mutable(Schema.Array(ExternalPackageSpec))),
140
+ autoDetectDependencies: Schema.optional(AutoDetectDependencies),
141
+ ogImage: Schema.optional(OpenGraphImageConfig),
142
+ llmsPlugin: Schema.optional(LlmsPlugin),
143
+ tsconfig: Schema.optional(ModelInput),
144
+ compilerOptions: Schema.optional(Schema.Unknown)
145
+ }));
146
+ /** Union for the versions record value: can be a path/function OR a full VersionConfig */
147
+ const VersionValue = Schema.Union(ModelInput, VersionConfig);
148
+ const SingleApiConfig = Schema.mutable(Schema.Struct({
149
+ packageName: Schema.String,
150
+ name: Schema.optional(Schema.String),
151
+ baseRoute: Schema.optional(Schema.String),
152
+ apiFolder: Schema.optional(Schema.Union(Schema.String, Schema.Null)),
153
+ model: Schema.optional(ModelInput),
154
+ packageJson: Schema.optional(ModelInput),
155
+ versions: Schema.optional(Schema.mutable(Schema.Record({
156
+ key: Schema.String,
157
+ value: VersionValue
158
+ }))),
159
+ theme: Schema.optional(ThemeConfig),
160
+ categories: Schema.optional(CategoriesRecord),
161
+ source: Schema.optional(SourceConfig),
162
+ externalPackages: Schema.optional(Schema.mutable(Schema.Array(ExternalPackageSpec))),
163
+ autoDetectDependencies: Schema.optional(AutoDetectDependencies),
164
+ ogImage: Schema.optional(OpenGraphImageConfig),
165
+ llmsPlugin: Schema.optional(LlmsPlugin),
166
+ tsconfig: Schema.optional(ModelInput),
167
+ compilerOptions: Schema.optional(Schema.Unknown)
168
+ }));
169
+ const MultiApiConfig = Schema.mutable(Schema.Struct({
170
+ packageName: Schema.String,
171
+ name: Schema.optional(Schema.String),
172
+ baseRoute: Schema.optional(Schema.String),
173
+ apiFolder: Schema.optional(Schema.Union(Schema.String, Schema.Null)),
174
+ model: ModelInput,
175
+ packageJson: Schema.optional(ModelInput),
176
+ theme: Schema.optional(ThemeConfig),
177
+ categories: Schema.optional(CategoriesRecord),
178
+ source: Schema.optional(SourceConfig),
179
+ externalPackages: Schema.optional(Schema.mutable(Schema.Array(ExternalPackageSpec))),
180
+ autoDetectDependencies: Schema.optional(AutoDetectDependencies),
181
+ ogImage: Schema.optional(OpenGraphImageConfig),
182
+ llmsPlugin: Schema.optional(LlmsPlugin),
183
+ tsconfig: Schema.optional(ModelInput),
184
+ compilerOptions: Schema.optional(Schema.Unknown)
185
+ }));
186
+ const PluginOptions = Schema.mutable(Schema.Struct({
187
+ api: Schema.optional(SingleApiConfig),
188
+ apis: Schema.optional(Schema.mutable(Schema.Array(MultiApiConfig))),
189
+ siteUrl: Schema.optional(Schema.String),
190
+ ogImage: Schema.optional(OpenGraphImageConfig),
191
+ defaultCategories: Schema.optional(CategoriesRecord),
192
+ errors: Schema.optional(ErrorConfig),
193
+ llmsPlugin: Schema.optional(Schema.Union(Schema.Boolean, LlmsPlugin)),
194
+ logLevel: Schema.optional(LogLevel$1),
195
+ performance: Schema.optional(PerformanceConfig)
196
+ }));
197
+
198
+ //#endregion
199
+ export { AutoDetectDependencies, CategoryConfig, DEFAULT_CATEGORIES, ErrorConfig, ExternalPackageSpec, LlmsPlugin, LogLevel$1 as LogLevel, ModelInput, MultiApiConfig, PluginOptions, SingleApiConfig, SourceConfig, ThemeConfig, VersionConfig };
@@ -0,0 +1,5 @@
1
+ import { OpenGraphImageConfig, OpenGraphImageMetadata } from "./opengraph.js";
2
+ import { PerformanceConfig, PerformanceThresholds } from "./performance.js";
3
+ import { AutoDetectDependencies, CategoryConfig, DEFAULT_CATEGORIES, ErrorConfig, ExternalPackageSpec, LlmsPlugin, LogLevel, ModelInput, MultiApiConfig, PluginOptions, SingleApiConfig, SourceConfig, ThemeConfig, VersionConfig } from "./config.js";
4
+
5
+ export { };
@@ -0,0 +1,26 @@
1
+ import { Schema } from "effect";
2
+
3
+ //#region src/schemas/opengraph.ts
4
+ const OpenGraphImageMetadata = Schema.mutable(Schema.Struct({
5
+ url: Schema.String,
6
+ secureUrl: Schema.optional(Schema.String),
7
+ type: Schema.optional(Schema.String),
8
+ width: Schema.optional(Schema.Number),
9
+ height: Schema.optional(Schema.Number),
10
+ alt: Schema.optional(Schema.String)
11
+ }));
12
+ const OpenGraphImageConfig = Schema.Union(Schema.String, OpenGraphImageMetadata);
13
+ const OpenGraphMetadata = Schema.mutable(Schema.Struct({
14
+ siteUrl: Schema.String,
15
+ pageRoute: Schema.String,
16
+ description: Schema.String,
17
+ publishedTime: Schema.String,
18
+ modifiedTime: Schema.String,
19
+ section: Schema.String,
20
+ tags: Schema.mutable(Schema.Array(Schema.String)),
21
+ ogImage: Schema.optional(OpenGraphImageMetadata),
22
+ ogType: Schema.String
23
+ }));
24
+
25
+ //#endregion
26
+ export { OpenGraphImageConfig, OpenGraphImageMetadata };
@@ -0,0 +1,19 @@
1
+ import { Schema } from "effect";
2
+
3
+ //#region src/schemas/performance.ts
4
+ const PerformanceThresholds = Schema.mutable(Schema.Struct({
5
+ slowCodeBlock: Schema.optionalWith(Schema.Number, { default: () => 100 }),
6
+ slowPageGeneration: Schema.optionalWith(Schema.Number, { default: () => 500 }),
7
+ slowApiLoad: Schema.optionalWith(Schema.Number, { default: () => 1e3 }),
8
+ slowFileOperation: Schema.optionalWith(Schema.Number, { default: () => 50 }),
9
+ slowHttpRequest: Schema.optionalWith(Schema.Number, { default: () => 2e3 }),
10
+ slowDbOperation: Schema.optionalWith(Schema.Number, { default: () => 100 })
11
+ }));
12
+ const PerformanceConfig = Schema.mutable(Schema.Struct({
13
+ thresholds: Schema.optional(PerformanceThresholds),
14
+ showInsights: Schema.optionalWith(Schema.Boolean, { default: () => true }),
15
+ trackDetailedMetrics: Schema.optionalWith(Schema.Boolean, { default: () => false })
16
+ }));
17
+
18
+ //#endregion
19
+ export { PerformanceConfig, PerformanceThresholds };
package/serve.js ADDED
@@ -0,0 +1,133 @@
1
+ import { execFileSync, spawn } from "node:child_process";
2
+
3
+ //#region src/serve.ts
4
+ const DEFAULT_PORT = 4173;
5
+ /**
6
+ * Readiness heuristic over a chunk of server output. Both RSPress `dev` and
7
+ * `preview` print a `Local:` address line once the server is listening, which
8
+ * is the reliable cross-mode signal. `dev` additionally prints a
9
+ * "ready ... built in" line, kept as a fallback in case the address-line format
10
+ * changes. Pure and exported so it can be unit-tested and reused as a
11
+ * {@link ServeOptions.readyWhen} building block.
12
+ */
13
+ function isServerReady(mode, output) {
14
+ if (output.includes("Local:")) return true;
15
+ if (mode === "dev") return output.includes("ready") && output.includes("built in");
16
+ return false;
17
+ }
18
+ /**
19
+ * Resolve {@link ServeOptions} into a concrete {@link ResolvedServeConfig},
20
+ * applying all defaults. Pure (modulo reading `process.env`) and exported so
21
+ * the resolution logic can be unit-tested without spawning a server.
22
+ */
23
+ function resolveServeConfig(options = {}) {
24
+ const mode = options.mode ?? "dev";
25
+ const port = options.port ?? (mode === "dev" ? Number(process.env.DEV_PORT) || DEFAULT_PORT : DEFAULT_PORT);
26
+ const open = options.open ?? !process.env.NO_OPEN;
27
+ const openPath = options.openPath ?? "/";
28
+ const packageManager = options.packageManager ?? "pnpm";
29
+ const cwd = options.cwd ?? process.cwd();
30
+ const isReady = options.readyWhen ?? ((output) => isServerReady(mode, output));
31
+ const normalizedPath = openPath.startsWith("/") ? openPath : `/${openPath}`;
32
+ return {
33
+ mode,
34
+ port,
35
+ open,
36
+ openPath,
37
+ packageManager,
38
+ cwd,
39
+ args: [
40
+ "rspress",
41
+ mode,
42
+ "--port",
43
+ String(port)
44
+ ],
45
+ url: `http://localhost:${port}${normalizedPath}`,
46
+ isReady
47
+ };
48
+ }
49
+ /* v8 ignore start -- spawns child processes / opens a browser; covered by the site smoke test, not unit tests */
50
+ /**
51
+ * Best-effort kill of any process listening on `port` (frees a stale dev/preview
52
+ * server). Invokes `lsof` directly via `execFileSync` (no shell), so the port is
53
+ * never interpolated into a shell string. A no-op on platforms without `lsof`
54
+ * (e.g. Windows) or for a non-positive-integer port.
55
+ */
56
+ function killProcessOnPort(port) {
57
+ if (!Number.isInteger(port) || port <= 0) return;
58
+ try {
59
+ const pids = execFileSync("lsof", [
60
+ "-t",
61
+ "-i",
62
+ `:${port}`
63
+ ], { encoding: "utf-8" }).trim().split("\n").filter(Boolean);
64
+ for (const pid of pids) process.kill(Number(pid), "SIGTERM");
65
+ if (pids.length > 0) console.log(`Killed process(es) on port ${port}: ${pids.join(", ")}`);
66
+ } catch {}
67
+ }
68
+ /**
69
+ * Run an RSPress `dev` or `preview` server, freeing the port first and opening a
70
+ * browser once the server is ready. A drop-in replacement for the per-site
71
+ * `dev.mts` / `preview.mts` scripts:
72
+ *
73
+ * ```ts
74
+ * import { serve } from "rspress-plugin-api-extractor";
75
+ *
76
+ * await serve({ mode: "dev", openPath: "/api/" });
77
+ * ```
78
+ *
79
+ * This runs the server for the lifetime of the host process and calls
80
+ * `process.exit` when the server exits (matching a top-level script runner). The
81
+ * returned promise resolves once the server is ready and the browser has been
82
+ * opened; it does not resolve when the server stops. Port-freeing and browser
83
+ * opening are best-effort and never reject.
84
+ */
85
+ async function serve(options = {}) {
86
+ const config = resolveServeConfig(options);
87
+ killProcessOnPort(config.port);
88
+ const child = spawn(config.packageManager, config.args, {
89
+ cwd: config.cwd,
90
+ stdio: [
91
+ "inherit",
92
+ "pipe",
93
+ "pipe"
94
+ ],
95
+ env: {
96
+ ...process.env,
97
+ FORCE_COLOR: "1"
98
+ }
99
+ });
100
+ const waitForReady = new Promise((resolve) => {
101
+ let resolved = false;
102
+ const handleOutput = (data) => {
103
+ const output = data.toString();
104
+ process.stdout.write(data);
105
+ if (!resolved && config.isReady(output)) {
106
+ resolved = true;
107
+ resolve();
108
+ }
109
+ };
110
+ child.stdout?.on("data", handleOutput);
111
+ child.stderr?.on("data", handleOutput);
112
+ });
113
+ child.on("error", (error) => {
114
+ console.error(`Failed to start rspress ${config.mode} server:`, error);
115
+ process.exit(1);
116
+ });
117
+ child.on("exit", (code) => {
118
+ console.log(`Rspress ${config.mode} server exited`);
119
+ process.exit(code ?? 0);
120
+ });
121
+ await waitForReady;
122
+ if (config.open) try {
123
+ const { default: open } = await import("open");
124
+ await open(config.url);
125
+ console.log(`✅ Opened ${config.url}`);
126
+ } catch (error) {
127
+ console.error("Failed to open browser:", error);
128
+ }
129
+ }
130
+ /* v8 ignore stop */
131
+
132
+ //#endregion
133
+ export { isServerReady, resolveServeConfig, serve };
@@ -0,0 +1,7 @@
1
+ import { Context } from "effect";
2
+
3
+ //#region src/services/ConfigService.ts
4
+ var ConfigService = class extends Context.Tag("rspress-plugin-api-extractor/ConfigService")() {};
5
+
6
+ //#endregion
7
+ export { ConfigService };
@@ -0,0 +1,7 @@
1
+ import { Context } from "effect";
2
+
3
+ //#region src/services/PathDerivationService.ts
4
+ var PathDerivationService = class extends Context.Tag("rspress-plugin-api-extractor/PathDerivationService")() {};
5
+
6
+ //#endregion
7
+ export { PathDerivationService };
@@ -0,0 +1,7 @@
1
+ import { Context } from "effect";
2
+
3
+ //#region src/services/SnapshotService.ts
4
+ var SnapshotService = class extends Context.Tag("rspress-plugin-api-extractor/SnapshotService")() {};
5
+
6
+ //#endregion
7
+ export { SnapshotService };
@@ -0,0 +1,7 @@
1
+ import { Context } from "effect";
2
+
3
+ //#region src/services/TypeRegistryService.ts
4
+ var TypeRegistryService = class extends Context.Tag("rspress-plugin-api-extractor/TypeRegistryService")() {};
5
+
6
+ //#endregion
7
+ export { TypeRegistryService };