rspress-plugin-api-extractor 0.9.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BuildEnv.js +58 -0
- package/README.md +2 -1
- package/build-program.js +33 -30
- package/build-stages.js +47 -39
- package/errors.js +0 -1
- package/index.d.ts +22 -14
- package/layers/ConfigServiceLive.js +349 -400
- package/layers/HighlighterServiceLive.js +52 -0
- package/layers/ObservabilityLive.js +26 -7
- package/layers/OgServiceLive.js +134 -0
- package/layers/TwoslashCacheServiceLive.js +108 -0
- package/layers/TwoslashEnvironmentsLive.js +33 -0
- package/layers/TypeRegistryServiceLive.js +54 -47
- package/layers/build-metrics.js +32 -5
- package/layers/xdg.js +44 -0
- package/markdown/helpers.js +9 -55
- package/markdown/page-generators/class-page.js +8 -31
- package/markdown/page-generators/index-pages.js +6 -8
- package/markdown/page-generators/interface-page.js +7 -7
- package/markdown/shiki-utils.js +65 -10
- package/observability/EventBus.js +29 -9
- package/observability/heartbeat.js +1 -1
- package/observability/metric-report.js +124 -0
- package/observability/sinks/console-sink.js +6 -0
- package/observability/sinks/metrics-sink.js +64 -21
- package/observability/sinks/render-sink.js +86 -0
- package/observability/sinks/trace-sink.js +10 -17
- package/observability/spans.js +4 -2
- package/observability/sync-emitter.js +78 -0
- package/og-resolver.js +46 -287
- package/package.json +4 -5
- package/path-derivation.js +19 -1
- package/plugin.js +64 -52
- package/prettier-formatter.js +4 -10
- package/remark-api-codeblocks.js +33 -15
- package/remark-with-api.js +24 -27
- package/schemas/config.js +11 -7
- package/services/HighlighterService.js +30 -0
- package/services/OgService.js +23 -0
- package/services/PluginConfig.js +26 -0
- package/services/TwoslashCacheService.js +15 -0
- package/services/TwoslashEnvironments.js +7 -0
- package/shiki-transformer.js +55 -256
- package/twoslash-access.js +48 -0
- package/twoslash-cache.js +174 -0
- package/twoslash-patterns.js +1 -1
- package/twoslash-timing-wrapper.js +23 -0
- package/twoslash-transformer.js +153 -89
- package/vfs-registry.js +1 -31
- package/layers/PathDerivationServiceLive.js +0 -16
- package/runtime/components/MarkdownText/index.js +0 -34
- package/services/PathDerivationService.js +0 -7
|
@@ -1,57 +1,32 @@
|
|
|
1
|
+
import { BuildId } from "../BuildEnv.js";
|
|
1
2
|
import { PluginEvent } from "../observability/events.js";
|
|
2
3
|
import { emit, wantsLevel } from "../observability/EventBus.js";
|
|
3
4
|
import { BuildMetrics } from "./build-metrics.js";
|
|
4
5
|
import "./ObservabilityLive.js";
|
|
5
6
|
import { TypeReferenceExtractor } from "../type-reference-extractor.js";
|
|
6
|
-
import { OpenGraphResolver } from "../og-resolver.js";
|
|
7
7
|
import { withPhase } from "../observability/spans.js";
|
|
8
|
+
import { TwoslashEnvironments } from "../services/TwoslashEnvironments.js";
|
|
8
9
|
import { resolveTypeScriptConfig } from "../typescript-config.js";
|
|
9
|
-
import {
|
|
10
|
+
import { apiScopeOf, deriveOutputPaths, normalizeBaseRoute, unscopedName } from "../path-derivation.js";
|
|
10
11
|
import { classifyApiConfig, extractAutoDetectedPackages, isVersionConfig, mergeLlmsPluginConfig, validateExternalPackages } from "../config-utils.js";
|
|
11
12
|
import { ApiExtractedPackage } from "../api-extracted-package.js";
|
|
12
13
|
import { CategoryResolver } from "../category-resolver.js";
|
|
13
14
|
import { ConfigValidationError } from "../errors.js";
|
|
14
|
-
import {
|
|
15
|
-
import { DEFAULT_SHIKI_THEMES } from "../markdown/shiki-utils.js";
|
|
15
|
+
import { normalizeThemeConfig } from "../markdown/shiki-utils.js";
|
|
16
16
|
import { loadApiModel, loadPackageJson, loadVersionModel } from "../model-loader.js";
|
|
17
17
|
import { DEFAULT_CATEGORIES } from "../schemas/config.js";
|
|
18
18
|
import "../schemas/index.js";
|
|
19
19
|
import { ConfigService } from "../services/ConfigService.js";
|
|
20
|
-
import {
|
|
20
|
+
import { PluginConfig } from "../services/PluginConfig.js";
|
|
21
|
+
import { TwoslashCacheService } from "../services/TwoslashCacheService.js";
|
|
21
22
|
import { TypeRegistryService } from "../services/TypeRegistryService.js";
|
|
23
|
+
import { twoslashEnvHash } from "../twoslash-cache.js";
|
|
22
24
|
import path from "node:path";
|
|
23
25
|
import { hashContent } from "@tsdoctor/snapshot";
|
|
24
26
|
import { Effect, Layer, Metric } from "effect";
|
|
25
|
-
import
|
|
26
|
-
import { createHighlighter } from "shiki";
|
|
27
|
+
import ts from "typescript";
|
|
27
28
|
|
|
28
29
|
//#region src/layers/ConfigServiceLive.ts
|
|
29
|
-
const DEFAULT_THRESHOLDS = {
|
|
30
|
-
slowCodeBlock: 100,
|
|
31
|
-
slowPageGeneration: 500,
|
|
32
|
-
slowApiLoad: 1e3,
|
|
33
|
-
slowFileOperation: 50,
|
|
34
|
-
slowHttpRequest: 2e3,
|
|
35
|
-
slowDbOperation: 100
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* Normalize theme configuration from user input to a consistent format.
|
|
39
|
-
*/
|
|
40
|
-
function normalizeThemeConfig(theme) {
|
|
41
|
-
if (!theme) return { ...DEFAULT_SHIKI_THEMES };
|
|
42
|
-
if (typeof theme === "string") return {
|
|
43
|
-
light: theme,
|
|
44
|
-
dark: theme
|
|
45
|
-
};
|
|
46
|
-
if ("light" in theme && "dark" in theme && typeof theme.light === "string" && typeof theme.dark === "string") return {
|
|
47
|
-
light: theme.light,
|
|
48
|
-
dark: theme.dark
|
|
49
|
-
};
|
|
50
|
-
return {
|
|
51
|
-
light: theme,
|
|
52
|
-
dark: theme
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
30
|
/**
|
|
56
31
|
* Prepend import statements for external type references to the VFS declaration files.
|
|
57
32
|
* Returns per-entry payloads for event emission (heavy content/importRefs gated on wantTrace).
|
|
@@ -129,7 +104,7 @@ function validateOptions(options, rspressConfig) {
|
|
|
129
104
|
});
|
|
130
105
|
} else {
|
|
131
106
|
if (api.versions) yield* emit(PluginEvent.ConfigCascadeWarning({
|
|
132
|
-
ctx: {
|
|
107
|
+
ctx: {},
|
|
133
108
|
level: "warn",
|
|
134
109
|
field: "versions",
|
|
135
110
|
chosen: "(none — multiVersion not configured)",
|
|
@@ -144,258 +119,210 @@ function validateOptions(options, rspressConfig) {
|
|
|
144
119
|
});
|
|
145
120
|
}
|
|
146
121
|
/**
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
122
|
+
* Resolve plugin options + RSPress config into the API configs the pipeline
|
|
123
|
+
* runs over.
|
|
124
|
+
*
|
|
125
|
+
* @remarks
|
|
126
|
+
* A module-level `const`, not a factory. It took the plugin options as an
|
|
127
|
+
* argument and was called inline at the merge site, which made it a
|
|
128
|
+
* layer-returning function: layers memoize by reference, so a second call
|
|
129
|
+
* would build a second `ConfigService` with its own captured `TypeRegistry`.
|
|
130
|
+
* The options come from {@link PluginConfig} now, so there is nothing to pass
|
|
131
|
+
* and nothing to call twice.
|
|
150
132
|
*/
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
133
|
+
const ConfigServiceLive = Layer.effect(ConfigService, Effect.gen(function* () {
|
|
134
|
+
const typeRegistry = yield* TypeRegistryService;
|
|
135
|
+
const options = yield* PluginConfig;
|
|
136
|
+
return { resolve: (rspressConfig) => Effect.gen(function* () {
|
|
137
|
+
const buildId = yield* BuildId;
|
|
138
|
+
const loadStart = performance.now();
|
|
139
|
+
const wantTrace = yield* wantsLevel("trace");
|
|
140
|
+
yield* validateOptions(options, { ...rspressConfig.multiVersion ? { multiVersion: {
|
|
141
|
+
default: rspressConfig.multiVersion.default,
|
|
142
|
+
versions: [...rspressConfig.multiVersion.versions]
|
|
143
|
+
} } : {} });
|
|
144
|
+
const rspressMultiVersion = rspressConfig.multiVersion;
|
|
145
|
+
const rspressLocales = rspressConfig.locales?.map((l) => l.lang) ?? [];
|
|
146
|
+
const rspressLang = rspressConfig.lang;
|
|
147
|
+
const docsRoot = rspressConfig.root;
|
|
148
|
+
const rspressRoot = docsRoot || process.cwd();
|
|
149
|
+
const categoryResolver = new CategoryResolver();
|
|
150
|
+
const pluginDefaults = categoryResolver.mergeCategories(DEFAULT_CATEGORIES, options.defaultCategories);
|
|
151
|
+
const apiConfigs = [];
|
|
152
|
+
const combinedVfs = /* @__PURE__ */ new Map();
|
|
153
|
+
const allExternalPackages = [];
|
|
154
|
+
let firstApiTsconfig;
|
|
155
|
+
let firstApiCompilerOptions;
|
|
156
|
+
/**
|
|
157
|
+
* Raw TypeScript config per API scope. Each documented package is
|
|
158
|
+
* type-checked under its OWN configuration; the build no longer picks
|
|
159
|
+
* one and applies it to everything.
|
|
160
|
+
*/
|
|
161
|
+
const scopeTsConfigs = /* @__PURE__ */ new Map();
|
|
162
|
+
/**
|
|
163
|
+
* Emit a typed ModelLoadFailed event for a failed model load, then
|
|
164
|
+
* convert the typed failure to a defect — a missing or unparsable
|
|
165
|
+
* model remains fatal to the build, exactly as before, but the
|
|
166
|
+
* event now rides the error channel instead of a sync-island seam.
|
|
167
|
+
*/
|
|
168
|
+
const withModelLoadEvents = (self) => self.pipe(Effect.tapError((error) => emit(PluginEvent.ModelLoadFailed({
|
|
169
|
+
ctx: { buildId },
|
|
170
|
+
level: "error",
|
|
171
|
+
modelPath: "modelPath" in error ? error.modelPath : "<loader function>",
|
|
172
|
+
reason: error.message
|
|
173
|
+
}))), Effect.orDie);
|
|
174
|
+
/**
|
|
175
|
+
* Helper to process a single API model (shared by single and multi modes).
|
|
176
|
+
*/
|
|
177
|
+
const processSimpleApi = (api, model, outputDir, fullRoute, wantTrace) => Effect.gen(function* () {
|
|
178
|
+
const { apiPackage, source: loaderSource } = yield* withModelLoadEvents(loadApiModel(model));
|
|
179
|
+
return yield* Effect.promise(async () => {
|
|
180
|
+
const resolvedCategories = categoryResolver.resolveCategoryConfig(pluginDefaults, api.categories);
|
|
181
|
+
const resolvedSource = categoryResolver.resolveSourceConfig(api.source, loaderSource);
|
|
182
|
+
const resolvedLlms = mergeLlmsPluginConfig(options.llmsPlugin, api.llmsPlugin);
|
|
183
|
+
const packageJson = api.packageJson ? await loadPackageJson(api.packageJson) : void 0;
|
|
184
|
+
validateExternalPackages(api.externalPackages, packageJson);
|
|
185
|
+
const externalPackages = api.externalPackages || extractAutoDetectedPackages(packageJson, api.autoDetectDependencies);
|
|
186
|
+
if (externalPackages && externalPackages.length > 0) Effect.runSync(Metric.update(BuildMetrics.externalPackagesTotal, externalPackages.length));
|
|
187
|
+
const vfs = ApiExtractedPackage.fromPackage(apiPackage, api.packageName).generateVfs();
|
|
188
|
+
const vfsPayloads = prependImportsToVfs(vfs, apiPackage, api.packageName, wantTrace);
|
|
189
|
+
const resolvedOgImage = api.ogImage ?? options.ogImage;
|
|
190
|
+
const resolvedTheme = normalizeThemeConfig(api.theme);
|
|
191
|
+
return {
|
|
192
|
+
vfs,
|
|
193
|
+
vfsPayloads,
|
|
194
|
+
externalPackages: externalPackages || [],
|
|
195
|
+
config: {
|
|
196
|
+
apiPackage,
|
|
197
|
+
packageName: api.packageName,
|
|
198
|
+
...api.name != null ? { apiName: api.name } : {},
|
|
199
|
+
outputDir,
|
|
200
|
+
baseRoute: fullRoute,
|
|
201
|
+
categories: resolvedCategories,
|
|
202
|
+
...resolvedSource != null ? { source: resolvedSource } : {},
|
|
203
|
+
...packageJson != null ? { packageJson } : {},
|
|
204
|
+
...resolvedLlms != null ? { llmsPlugin: resolvedLlms } : {},
|
|
205
|
+
...options.siteUrl != null ? { siteUrl: options.siteUrl } : {},
|
|
206
|
+
...resolvedOgImage != null ? { ogImage: resolvedOgImage } : {},
|
|
207
|
+
docsDir: path.dirname(outputDir),
|
|
208
|
+
...docsRoot != null ? { docsRoot } : {},
|
|
209
|
+
...resolvedTheme != null ? { theme: resolvedTheme } : {}
|
|
210
|
+
}
|
|
211
|
+
};
|
|
225
212
|
});
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
baseRoute,
|
|
238
|
-
apiFolder: api.apiFolder ?? "api",
|
|
239
|
-
locales: rspressLocales,
|
|
240
|
-
defaultLang: rspressLang,
|
|
241
|
-
versions: [version],
|
|
242
|
-
defaultVersion: rspressMultiVersion?.default
|
|
243
|
-
}))[0];
|
|
244
|
-
if (!versionDp) return {
|
|
245
|
-
vfs: /* @__PURE__ */ new Map(),
|
|
246
|
-
vfsPayloads: [],
|
|
247
|
-
externalPackages: [],
|
|
248
|
-
config: null
|
|
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));
|
|
252
|
-
return yield* Effect.promise(async () => {
|
|
253
|
-
Effect.runSync(Metric.update(BuildMetrics.apiVersionsLoaded, 1));
|
|
254
|
-
const resolvedCategories = categoryResolver.resolveCategoryConfig(pluginDefaults, api.categories, versionCategories);
|
|
255
|
-
const resolvedSource = categoryResolver.resolveSourceConfig(api.source, versionSource);
|
|
256
|
-
const resolvedLlms = mergeLlmsPluginConfig(options.llmsPlugin, api.llmsPlugin, versionLlms);
|
|
257
|
-
const packageJson = versionPackageJson || (api.packageJson ? await loadPackageJson(api.packageJson) : void 0);
|
|
258
|
-
validateExternalPackages(versionExternalPackages || api.externalPackages, packageJson);
|
|
259
|
-
const autoDetectOptions = versionAutoDetectDependencies || api.autoDetectDependencies;
|
|
260
|
-
const externalPackages = versionExternalPackages || api.externalPackages || extractAutoDetectedPackages(packageJson, autoDetectOptions);
|
|
261
|
-
if (externalPackages && externalPackages.length > 0) Effect.runSync(Metric.update(BuildMetrics.externalPackagesTotal, externalPackages.length));
|
|
262
|
-
const vfs = ApiExtractedPackage.fromPackage(apiPackage, api.packageName).generateVfs();
|
|
263
|
-
const vfsPayloads = prependImportsToVfs(vfs, apiPackage, api.packageName, wantTrace);
|
|
264
|
-
const resolvedOgImage = versionOgImage ?? api.ogImage ?? options.ogImage;
|
|
265
|
-
const resolvedTheme = normalizeThemeConfig(api.theme);
|
|
266
|
-
const outputDir = versionDp.outputDir;
|
|
267
|
-
const fullRoute = versionDp.routeBase;
|
|
268
|
-
return {
|
|
269
|
-
vfs,
|
|
270
|
-
vfsPayloads,
|
|
271
|
-
externalPackages: externalPackages || [],
|
|
272
|
-
config: {
|
|
273
|
-
apiPackage,
|
|
274
|
-
packageName: `${api.packageName} (${version})`,
|
|
275
|
-
...api.name != null ? { apiName: api.name } : {},
|
|
276
|
-
outputDir,
|
|
277
|
-
baseRoute: fullRoute,
|
|
278
|
-
categories: resolvedCategories,
|
|
279
|
-
...resolvedSource != null ? { source: resolvedSource } : {},
|
|
280
|
-
...packageJson != null ? { packageJson } : {},
|
|
281
|
-
...resolvedLlms != null ? { llmsPlugin: resolvedLlms } : {},
|
|
282
|
-
...options.siteUrl != null ? { siteUrl: options.siteUrl } : {},
|
|
283
|
-
...resolvedOgImage != null ? { ogImage: resolvedOgImage } : {},
|
|
284
|
-
docsDir: path.dirname(outputDir),
|
|
285
|
-
...docsRoot != null ? { docsRoot } : {},
|
|
286
|
-
...resolvedTheme != null ? { theme: resolvedTheme } : {}
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
});
|
|
290
|
-
}), { concurrency: "unbounded" });
|
|
291
|
-
for (const result of versionResults) {
|
|
292
|
-
for (const [filepath, content] of result.vfs.entries()) combinedVfs.set(filepath, content);
|
|
293
|
-
if (result.externalPackages.length > 0) allExternalPackages.push(...result.externalPackages);
|
|
294
|
-
if (result.config) apiConfigs.push(result.config);
|
|
295
|
-
for (const payload of result.vfsPayloads) {
|
|
296
|
-
yield* emit(PluginEvent.VfsGenerated({
|
|
297
|
-
ctx: {
|
|
298
|
-
buildId: "",
|
|
299
|
-
packageName: api.packageName,
|
|
300
|
-
...payload.entryPoint ? { entryPoint: payload.entryPoint } : {}
|
|
301
|
-
},
|
|
302
|
-
level: "debug",
|
|
303
|
-
file: payload.file,
|
|
304
|
-
declCount: payload.declCount,
|
|
305
|
-
contentHash: payload.contentHash,
|
|
306
|
-
...wantTrace && payload.content ? { content: payload.content } : {}
|
|
307
|
-
}));
|
|
308
|
-
if (payload.hasImports) yield* emit(PluginEvent.ImportsPrepended({
|
|
309
|
-
ctx: {
|
|
310
|
-
buildId: "",
|
|
311
|
-
packageName: api.packageName,
|
|
312
|
-
...payload.entryPoint ? { entryPoint: payload.entryPoint } : {}
|
|
313
|
-
},
|
|
314
|
-
level: "debug",
|
|
315
|
-
file: payload.file,
|
|
316
|
-
imports: wantTrace ? payload.importRefs : []
|
|
317
|
-
}));
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
} else if (api.model) {
|
|
321
|
-
const dp = (yield* pathService.derivePaths({
|
|
213
|
+
});
|
|
214
|
+
yield* withPhase("modelLoad", { buildId }, Effect.gen(function* () {
|
|
215
|
+
if (options.api) {
|
|
216
|
+
const api = options.api;
|
|
217
|
+
const baseRoute = normalizeBaseRoute(api.baseRoute ?? "/");
|
|
218
|
+
firstApiTsconfig = api.tsconfig;
|
|
219
|
+
firstApiCompilerOptions = api.compilerOptions;
|
|
220
|
+
scopeTsConfigs.set(apiScopeOf(baseRoute, api.packageName), rawTsConfig(api));
|
|
221
|
+
if (rspressMultiVersion && api.versions) {
|
|
222
|
+
const versionResults = yield* Effect.forEach(Object.entries(api.versions), ([version, versionValue]) => Effect.gen(function* () {
|
|
223
|
+
const versionDp = deriveOutputPaths({
|
|
322
224
|
mode: "single",
|
|
323
225
|
docsRoot: rspressRoot,
|
|
324
226
|
baseRoute,
|
|
325
227
|
apiFolder: api.apiFolder ?? "api",
|
|
326
228
|
locales: rspressLocales,
|
|
327
229
|
defaultLang: rspressLang,
|
|
328
|
-
versions: [],
|
|
329
|
-
defaultVersion:
|
|
330
|
-
})
|
|
331
|
-
if (
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
230
|
+
versions: [version],
|
|
231
|
+
defaultVersion: rspressMultiVersion?.default
|
|
232
|
+
})[0];
|
|
233
|
+
if (!versionDp) return {
|
|
234
|
+
vfs: /* @__PURE__ */ new Map(),
|
|
235
|
+
vfsPayloads: [],
|
|
236
|
+
externalPackages: [],
|
|
237
|
+
config: null
|
|
238
|
+
};
|
|
239
|
+
const versionConfig = isVersionConfig(versionValue) ? versionValue : { model: versionValue };
|
|
240
|
+
const { apiPackage, packageJson: versionPackageJson, categories: versionCategories, source: versionSource, externalPackages: versionExternalPackages, autoDetectDependencies: versionAutoDetectDependencies, llmsPlugin: versionLlms, ogImage: versionOgImage } = yield* withModelLoadEvents(loadVersionModel(versionConfig));
|
|
241
|
+
return yield* Effect.promise(async () => {
|
|
242
|
+
Effect.runSync(Metric.update(BuildMetrics.apiVersionsLoaded, 1));
|
|
243
|
+
const resolvedCategories = categoryResolver.resolveCategoryConfig(pluginDefaults, api.categories, versionCategories);
|
|
244
|
+
const resolvedSource = categoryResolver.resolveSourceConfig(api.source, versionSource);
|
|
245
|
+
const resolvedLlms = mergeLlmsPluginConfig(options.llmsPlugin, api.llmsPlugin, versionLlms);
|
|
246
|
+
const packageJson = versionPackageJson || (api.packageJson ? await loadPackageJson(api.packageJson) : void 0);
|
|
247
|
+
validateExternalPackages(versionExternalPackages || api.externalPackages, packageJson);
|
|
248
|
+
const autoDetectOptions = versionAutoDetectDependencies || api.autoDetectDependencies;
|
|
249
|
+
const externalPackages = versionExternalPackages || api.externalPackages || extractAutoDetectedPackages(packageJson, autoDetectOptions);
|
|
250
|
+
if (externalPackages && externalPackages.length > 0) Effect.runSync(Metric.update(BuildMetrics.externalPackagesTotal, externalPackages.length));
|
|
251
|
+
const vfs = ApiExtractedPackage.fromPackage(apiPackage, api.packageName).generateVfs();
|
|
252
|
+
const vfsPayloads = prependImportsToVfs(vfs, apiPackage, api.packageName, wantTrace);
|
|
253
|
+
const resolvedOgImage = versionOgImage ?? api.ogImage ?? options.ogImage;
|
|
254
|
+
const resolvedTheme = normalizeThemeConfig(api.theme);
|
|
255
|
+
const outputDir = versionDp.outputDir;
|
|
256
|
+
const fullRoute = versionDp.routeBase;
|
|
257
|
+
return {
|
|
258
|
+
vfs,
|
|
259
|
+
vfsPayloads,
|
|
260
|
+
externalPackages: externalPackages || [],
|
|
261
|
+
config: {
|
|
262
|
+
apiPackage,
|
|
263
|
+
packageName: `${api.packageName} (${version})`,
|
|
264
|
+
...api.name != null ? { apiName: api.name } : {},
|
|
265
|
+
outputDir,
|
|
266
|
+
baseRoute: fullRoute,
|
|
267
|
+
categories: resolvedCategories,
|
|
268
|
+
...resolvedSource != null ? { source: resolvedSource } : {},
|
|
269
|
+
...packageJson != null ? { packageJson } : {},
|
|
270
|
+
...resolvedLlms != null ? { llmsPlugin: resolvedLlms } : {},
|
|
271
|
+
...options.siteUrl != null ? { siteUrl: options.siteUrl } : {},
|
|
272
|
+
...resolvedOgImage != null ? { ogImage: resolvedOgImage } : {},
|
|
273
|
+
docsDir: path.dirname(outputDir),
|
|
274
|
+
...docsRoot != null ? { docsRoot } : {},
|
|
275
|
+
...resolvedTheme != null ? { theme: resolvedTheme } : {}
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
});
|
|
279
|
+
}), { concurrency: "unbounded" });
|
|
280
|
+
for (const result of versionResults) {
|
|
281
|
+
for (const [filepath, content] of result.vfs.entries()) combinedVfs.set(filepath, content);
|
|
282
|
+
if (result.externalPackages.length > 0) allExternalPackages.push(...result.externalPackages);
|
|
283
|
+
if (result.config) apiConfigs.push(result.config);
|
|
284
|
+
for (const payload of result.vfsPayloads) {
|
|
285
|
+
yield* emit(PluginEvent.VfsGenerated({
|
|
286
|
+
ctx: {
|
|
287
|
+
packageName: api.packageName,
|
|
288
|
+
...payload.entryPoint ? { entryPoint: payload.entryPoint } : {}
|
|
289
|
+
},
|
|
290
|
+
level: "debug",
|
|
291
|
+
file: payload.file,
|
|
292
|
+
declCount: payload.declCount,
|
|
293
|
+
contentHash: payload.contentHash,
|
|
294
|
+
...wantTrace && payload.content ? { content: payload.content } : {}
|
|
295
|
+
}));
|
|
296
|
+
if (payload.hasImports) yield* emit(PluginEvent.ImportsPrepended({
|
|
297
|
+
ctx: {
|
|
298
|
+
packageName: api.packageName,
|
|
299
|
+
...payload.entryPoint ? { entryPoint: payload.entryPoint } : {}
|
|
300
|
+
},
|
|
301
|
+
level: "debug",
|
|
302
|
+
file: payload.file,
|
|
303
|
+
imports: wantTrace ? payload.importRefs : []
|
|
376
304
|
}));
|
|
377
305
|
}
|
|
378
306
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
defaultVersion: void 0
|
|
392
|
-
}))[0];
|
|
393
|
-
if (!dp) return [];
|
|
307
|
+
} else if (api.model) {
|
|
308
|
+
const dp = deriveOutputPaths({
|
|
309
|
+
mode: "single",
|
|
310
|
+
docsRoot: rspressRoot,
|
|
311
|
+
baseRoute,
|
|
312
|
+
apiFolder: api.apiFolder ?? "api",
|
|
313
|
+
locales: rspressLocales,
|
|
314
|
+
defaultLang: rspressLang,
|
|
315
|
+
versions: [],
|
|
316
|
+
defaultVersion: void 0
|
|
317
|
+
})[0];
|
|
318
|
+
if (dp) {
|
|
394
319
|
const result = yield* processSimpleApi(api, api.model, dp.outputDir, dp.routeBase, wantTrace);
|
|
320
|
+
for (const [filepath, content] of result.vfs.entries()) combinedVfs.set(filepath, content);
|
|
321
|
+
if (result.externalPackages.length > 0) allExternalPackages.push(...result.externalPackages);
|
|
322
|
+
apiConfigs.push(result.config);
|
|
395
323
|
for (const payload of result.vfsPayloads) {
|
|
396
324
|
yield* emit(PluginEvent.VfsGenerated({
|
|
397
325
|
ctx: {
|
|
398
|
-
buildId: "",
|
|
399
326
|
packageName: api.packageName,
|
|
400
327
|
...payload.entryPoint ? { entryPoint: payload.entryPoint } : {}
|
|
401
328
|
},
|
|
@@ -407,7 +334,6 @@ function ConfigServiceLive(options, shikiCrossLinker, buildId = "", resolvedThre
|
|
|
407
334
|
}));
|
|
408
335
|
if (payload.hasImports) yield* emit(PluginEvent.ImportsPrepended({
|
|
409
336
|
ctx: {
|
|
410
|
-
buildId: "",
|
|
411
337
|
packageName: api.packageName,
|
|
412
338
|
...payload.entryPoint ? { entryPoint: payload.entryPoint } : {}
|
|
413
339
|
},
|
|
@@ -416,141 +342,164 @@ function ConfigServiceLive(options, shikiCrossLinker, buildId = "", resolvedThre
|
|
|
416
342
|
imports: wantTrace ? payload.importRefs : []
|
|
417
343
|
}));
|
|
418
344
|
}
|
|
419
|
-
return [result];
|
|
420
|
-
}), { concurrency: "unbounded" });
|
|
421
|
-
for (const results of multiResults) for (const result of results) {
|
|
422
|
-
for (const [filepath, content] of result.vfs.entries()) combinedVfs.set(filepath, content);
|
|
423
|
-
if (result.externalPackages.length > 0) allExternalPackages.push(...result.externalPackages);
|
|
424
|
-
apiConfigs.push(result.config);
|
|
425
345
|
}
|
|
426
346
|
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
if (droppedCount > 0) yield* emit(PluginEvent.ExternalPackageSkipped({
|
|
457
|
-
ctx: { buildId: "" },
|
|
458
|
-
level: "debug",
|
|
459
|
-
reason: `${droppedCount} unresolvable package(s) (unpublished or workspace-only)`
|
|
460
|
-
}));
|
|
461
|
-
if (resolvedPackages.length > 0) {
|
|
462
|
-
const result = yield* typeRegistry.loadPackages(resolvedPackages);
|
|
463
|
-
for (const [filePath, content] of result.vfs.entries()) combinedVfs.set(filePath, content);
|
|
464
|
-
yield* emit(PluginEvent.VfsMerged({
|
|
465
|
-
ctx: { buildId: "" },
|
|
347
|
+
} else if (options.apis) {
|
|
348
|
+
const apisWithTsconfig = options.apis.filter((a) => a.tsconfig);
|
|
349
|
+
if (apisWithTsconfig.length > 0) firstApiTsconfig = apisWithTsconfig[0].tsconfig;
|
|
350
|
+
const apisWithCompilerOptions = options.apis.filter((a) => a.compilerOptions);
|
|
351
|
+
if (apisWithCompilerOptions.length > 0) firstApiCompilerOptions = apisWithCompilerOptions[0].compilerOptions;
|
|
352
|
+
for (const a of options.apis) {
|
|
353
|
+
const scopeRoute = normalizeBaseRoute(a.baseRoute ?? `/${unscopedName(a.packageName)}`);
|
|
354
|
+
scopeTsConfigs.set(apiScopeOf(scopeRoute, a.packageName), rawTsConfig(a));
|
|
355
|
+
}
|
|
356
|
+
const multiResults = yield* Effect.forEach(options.apis, (api) => Effect.gen(function* () {
|
|
357
|
+
const apiBaseRoute = normalizeBaseRoute(api.baseRoute ?? `/${unscopedName(api.packageName)}`);
|
|
358
|
+
const dp = deriveOutputPaths({
|
|
359
|
+
mode: "multi",
|
|
360
|
+
docsRoot: rspressRoot,
|
|
361
|
+
baseRoute: apiBaseRoute,
|
|
362
|
+
apiFolder: api.apiFolder ?? "api",
|
|
363
|
+
locales: rspressLocales,
|
|
364
|
+
defaultLang: rspressLang,
|
|
365
|
+
versions: [],
|
|
366
|
+
defaultVersion: void 0
|
|
367
|
+
})[0];
|
|
368
|
+
if (!dp) return [];
|
|
369
|
+
const result = yield* processSimpleApi(api, api.model, dp.outputDir, dp.routeBase, wantTrace);
|
|
370
|
+
for (const payload of result.vfsPayloads) {
|
|
371
|
+
yield* emit(PluginEvent.VfsGenerated({
|
|
372
|
+
ctx: {
|
|
373
|
+
packageName: api.packageName,
|
|
374
|
+
...payload.entryPoint ? { entryPoint: payload.entryPoint } : {}
|
|
375
|
+
},
|
|
466
376
|
level: "debug",
|
|
467
|
-
|
|
468
|
-
|
|
377
|
+
file: payload.file,
|
|
378
|
+
declCount: payload.declCount,
|
|
379
|
+
contentHash: payload.contentHash,
|
|
380
|
+
...wantTrace && payload.content ? { content: payload.content } : {}
|
|
381
|
+
}));
|
|
382
|
+
if (payload.hasImports) yield* emit(PluginEvent.ImportsPrepended({
|
|
383
|
+
ctx: {
|
|
384
|
+
packageName: api.packageName,
|
|
385
|
+
...payload.entryPoint ? { entryPoint: payload.entryPoint } : {}
|
|
386
|
+
},
|
|
387
|
+
level: "debug",
|
|
388
|
+
file: payload.file,
|
|
389
|
+
imports: wantTrace ? payload.importRefs : []
|
|
469
390
|
}));
|
|
470
391
|
}
|
|
392
|
+
return [result];
|
|
393
|
+
}), { concurrency: "unbounded" });
|
|
394
|
+
for (const results of multiResults) for (const result of results) {
|
|
395
|
+
for (const [filepath, content] of result.vfs.entries()) combinedVfs.set(filepath, content);
|
|
396
|
+
if (result.externalPackages.length > 0) allExternalPackages.push(...result.externalPackages);
|
|
397
|
+
apiConfigs.push(result.config);
|
|
471
398
|
}
|
|
472
|
-
}));
|
|
473
|
-
if (typeLoadResult._tag === "Failure") yield* emit(PluginEvent.ConfigCascadeWarning({
|
|
474
|
-
ctx: { buildId: "" },
|
|
475
|
-
level: "warn",
|
|
476
|
-
field: "externalTypes",
|
|
477
|
-
chosen: "empty VFS",
|
|
478
|
-
ignored: [typeLoadResult.failure.message ?? String(typeLoadResult.failure)]
|
|
479
|
-
}));
|
|
480
|
-
const twoslashStartMs = performance.now();
|
|
481
|
-
TwoslashManager.getInstance().initialize(combinedVfs, void 0, void 0, void 0, resolvedCompilerOptions);
|
|
482
|
-
yield* emit(PluginEvent.TwoslashInitialized({
|
|
483
|
-
ctx: { buildId: "" },
|
|
484
|
-
level: "debug",
|
|
485
|
-
durationMs: Math.round(performance.now() - twoslashStartMs),
|
|
486
|
-
vfsFileCount: combinedVfs.size
|
|
487
|
-
}));
|
|
488
|
-
const shikiStartMs = performance.now();
|
|
489
|
-
const themeSet = /* @__PURE__ */ new Set();
|
|
490
|
-
const customThemes = [];
|
|
491
|
-
for (const config of apiConfigs) {
|
|
492
|
-
const theme = config.theme ?? {
|
|
493
|
-
light: DEFAULT_SHIKI_THEMES.light,
|
|
494
|
-
dark: DEFAULT_SHIKI_THEMES.dark
|
|
495
|
-
};
|
|
496
|
-
if (typeof theme.light === "string") themeSet.add(theme.light);
|
|
497
|
-
else if (typeof theme.light === "object") customThemes.push(theme.light);
|
|
498
|
-
if (typeof theme.dark === "string") themeSet.add(theme.dark);
|
|
499
|
-
else if (typeof theme.dark === "object") customThemes.push(theme.dark);
|
|
500
399
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}
|
|
546
|
-
})
|
|
547
|
-
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
400
|
+
}));
|
|
401
|
+
const loadMs = performance.now() - loadStart;
|
|
402
|
+
yield* emit(PluginEvent.ModelLoaded({
|
|
403
|
+
ctx: {},
|
|
404
|
+
level: "debug",
|
|
405
|
+
entryPoints: apiConfigs.length,
|
|
406
|
+
itemCount: apiConfigs.reduce((sum, cfg) => sum + cfg.apiPackage.entryPoints.reduce((s, ep) => s + ep.members.length, 0), 0),
|
|
407
|
+
durationMs: Math.round(loadMs)
|
|
408
|
+
}));
|
|
409
|
+
const projectRoot = process.cwd();
|
|
410
|
+
let globalTsConfig;
|
|
411
|
+
if (firstApiTsconfig || firstApiCompilerOptions) {
|
|
412
|
+
globalTsConfig = {};
|
|
413
|
+
if (firstApiTsconfig != null) globalTsConfig.tsconfig = firstApiTsconfig;
|
|
414
|
+
if (firstApiCompilerOptions != null) globalTsConfig.compilerOptions = firstApiCompilerOptions;
|
|
415
|
+
}
|
|
416
|
+
const resolvedCompilerOptions = yield* Effect.promise(() => resolveTypeScriptConfig(projectRoot, globalTsConfig));
|
|
417
|
+
yield* emit(PluginEvent.TsCacheCreated({
|
|
418
|
+
ctx: {},
|
|
419
|
+
level: "debug",
|
|
420
|
+
compilerOptions: `target=${resolvedCompilerOptions.target}, module=${resolvedCompilerOptions.module}, lib=[${resolvedCompilerOptions.lib?.join(", ") ?? ""}]`,
|
|
421
|
+
durationMs: 0
|
|
422
|
+
}));
|
|
423
|
+
const documentedPackageNames = new Set(apiConfigs.map((config) => config.packageName));
|
|
424
|
+
const externalPackagesToLoad = allExternalPackages.filter((pkg) => !documentedPackageNames.has(pkg.name));
|
|
425
|
+
const typeLoadResult = yield* Effect.result(Effect.gen(function* () {
|
|
426
|
+
if (externalPackagesToLoad.length > 0) {
|
|
427
|
+
const resolvedPackages = yield* typeRegistry.resolveVersions(externalPackagesToLoad);
|
|
428
|
+
const droppedCount = externalPackagesToLoad.length - resolvedPackages.length;
|
|
429
|
+
if (droppedCount > 0) yield* emit(PluginEvent.ExternalPackageSkipped({
|
|
430
|
+
ctx: {},
|
|
431
|
+
level: "debug",
|
|
432
|
+
reason: `${droppedCount} unresolvable package(s) (unpublished or workspace-only)`
|
|
433
|
+
}));
|
|
434
|
+
if (resolvedPackages.length > 0) {
|
|
435
|
+
const result = yield* typeRegistry.loadPackages(resolvedPackages);
|
|
436
|
+
for (const [filePath, content] of result.vfs.entries()) combinedVfs.set(filePath, content);
|
|
437
|
+
yield* emit(PluginEvent.VfsMerged({
|
|
438
|
+
ctx: {},
|
|
439
|
+
level: "debug",
|
|
440
|
+
totalFiles: result.vfs.size,
|
|
441
|
+
packages: resolvedPackages.map((p) => p.name)
|
|
442
|
+
}));
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}));
|
|
446
|
+
if (typeLoadResult._tag === "Failure") yield* emit(PluginEvent.ConfigCascadeWarning({
|
|
447
|
+
ctx: {},
|
|
448
|
+
level: "warn",
|
|
449
|
+
field: "externalTypes",
|
|
450
|
+
chosen: "empty VFS",
|
|
451
|
+
ignored: [typeLoadResult.failure.message ?? String(typeLoadResult.failure)]
|
|
452
|
+
}));
|
|
453
|
+
const twoslashEnv = twoslashEnvHash(combinedVfs, `typescript@${ts.version}`);
|
|
454
|
+
const twoslashCache = yield* (yield* TwoslashCacheService).open(twoslashEnv);
|
|
455
|
+
yield* emit(PluginEvent.TwoslashCacheLoaded({
|
|
456
|
+
ctx: {},
|
|
457
|
+
level: "debug",
|
|
458
|
+
envHash: twoslashEnv,
|
|
459
|
+
entries: twoslashCache.entries().size
|
|
460
|
+
}));
|
|
461
|
+
const twoslashStartMs = performance.now();
|
|
462
|
+
const environments = yield* TwoslashEnvironments;
|
|
463
|
+
environments.registerEnvironment({
|
|
464
|
+
vfs: combinedVfs,
|
|
465
|
+
compilerOptions: resolvedCompilerOptions,
|
|
466
|
+
typesCache: twoslashCache
|
|
467
|
+
});
|
|
468
|
+
const resolvedByRawConfig = /* @__PURE__ */ new Map();
|
|
469
|
+
for (const [apiScope, rawConfig] of scopeTsConfigs) {
|
|
470
|
+
if (rawConfig === void 0) {
|
|
471
|
+
environments.registerScope(apiScope, resolvedCompilerOptions);
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
const rawKey = JSON.stringify([String(rawConfig.tsconfig ?? ""), rawConfig.compilerOptions ?? null]);
|
|
475
|
+
let scopeOptions = resolvedByRawConfig.get(rawKey);
|
|
476
|
+
if (scopeOptions === void 0) {
|
|
477
|
+
scopeOptions = yield* Effect.promise(() => resolveTypeScriptConfig(projectRoot, rawConfig));
|
|
478
|
+
resolvedByRawConfig.set(rawKey, scopeOptions);
|
|
479
|
+
}
|
|
480
|
+
environments.registerEnvironment({
|
|
481
|
+
vfs: combinedVfs,
|
|
482
|
+
compilerOptions: scopeOptions,
|
|
483
|
+
typesCache: twoslashCache
|
|
484
|
+
});
|
|
485
|
+
environments.registerScope(apiScope, scopeOptions);
|
|
486
|
+
}
|
|
487
|
+
yield* emit(PluginEvent.TwoslashInitialized({
|
|
488
|
+
ctx: {},
|
|
489
|
+
level: "debug",
|
|
490
|
+
durationMs: Math.round(performance.now() - twoslashStartMs),
|
|
491
|
+
vfsFileCount: combinedVfs.size
|
|
492
|
+
}));
|
|
493
|
+
return apiConfigs;
|
|
494
|
+
}) };
|
|
495
|
+
}));
|
|
496
|
+
/** The raw TypeScript config an API declares, or undefined when it declares none. */
|
|
497
|
+
function rawTsConfig(api) {
|
|
498
|
+
if (api.tsconfig == null && api.compilerOptions == null) return void 0;
|
|
499
|
+
const cfg = {};
|
|
500
|
+
if (api.tsconfig != null) cfg.tsconfig = api.tsconfig;
|
|
501
|
+
if (api.compilerOptions != null) cfg.compilerOptions = api.compilerOptions;
|
|
502
|
+
return cfg;
|
|
554
503
|
}
|
|
555
504
|
|
|
556
505
|
//#endregion
|