rspress-plugin-api-extractor 0.10.0 → 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/build-program.js +33 -31
- package/build-stages.js +47 -39
- package/errors.js +0 -1
- package/layers/ConfigServiceLive.js +339 -433
- package/layers/HighlighterServiceLive.js +52 -0
- package/layers/OgServiceLive.js +134 -0
- package/layers/TwoslashCacheServiceLive.js +74 -19
- package/layers/TwoslashEnvironmentsLive.js +33 -0
- package/layers/TypeRegistryServiceLive.js +54 -47
- 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 -7
- package/observability/spans.js +3 -1
- package/observability/sync-emitter.js +78 -0
- package/og-resolver.js +46 -287
- package/package.json +2 -3
- package/path-derivation.js +19 -1
- package/plugin.js +48 -73
- package/prettier-formatter.js +4 -10
- package/remark-api-codeblocks.js +10 -18
- package/remark-with-api.js +11 -21
- package/services/HighlighterService.js +30 -0
- package/services/OgService.js +23 -0
- package/services/PluginConfig.js +26 -0
- package/services/TwoslashEnvironments.js +7 -0
- package/shiki-transformer.js +53 -234
- package/twoslash-access.js +48 -0
- package/twoslash-transformer.js +106 -83
- package/vfs-registry.js +1 -31
- package/layers/PathDerivationServiceLive.js +0 -16
- package/services/PathDerivationService.js +0 -7
|
@@ -1,60 +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
21
|
import { TwoslashCacheService } from "../services/TwoslashCacheService.js";
|
|
22
22
|
import { TypeRegistryService } from "../services/TypeRegistryService.js";
|
|
23
|
-
import {
|
|
23
|
+
import { twoslashEnvHash } from "../twoslash-cache.js";
|
|
24
24
|
import path from "node:path";
|
|
25
25
|
import { hashContent } from "@tsdoctor/snapshot";
|
|
26
26
|
import { Effect, Layer, Metric } from "effect";
|
|
27
27
|
import ts from "typescript";
|
|
28
|
-
import os from "node:os";
|
|
29
|
-
import { createHighlighter } from "shiki";
|
|
30
28
|
|
|
31
29
|
//#region src/layers/ConfigServiceLive.ts
|
|
32
|
-
const DEFAULT_THRESHOLDS = {
|
|
33
|
-
slowCodeBlock: 100,
|
|
34
|
-
slowPageGeneration: 500,
|
|
35
|
-
slowApiLoad: 1e3,
|
|
36
|
-
slowFileOperation: 50,
|
|
37
|
-
slowHttpRequest: 2e3,
|
|
38
|
-
slowDbOperation: 100
|
|
39
|
-
};
|
|
40
|
-
/**
|
|
41
|
-
* Normalize theme configuration from user input to a consistent format.
|
|
42
|
-
*/
|
|
43
|
-
function normalizeThemeConfig(theme) {
|
|
44
|
-
if (!theme) return { ...DEFAULT_SHIKI_THEMES };
|
|
45
|
-
if (typeof theme === "string") return {
|
|
46
|
-
light: theme,
|
|
47
|
-
dark: theme
|
|
48
|
-
};
|
|
49
|
-
if ("light" in theme && "dark" in theme && typeof theme.light === "string" && typeof theme.dark === "string") return {
|
|
50
|
-
light: theme.light,
|
|
51
|
-
dark: theme.dark
|
|
52
|
-
};
|
|
53
|
-
return {
|
|
54
|
-
light: theme,
|
|
55
|
-
dark: theme
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
30
|
/**
|
|
59
31
|
* Prepend import statements for external type references to the VFS declaration files.
|
|
60
32
|
* Returns per-entry payloads for event emission (heavy content/importRefs gated on wantTrace).
|
|
@@ -132,7 +104,7 @@ function validateOptions(options, rspressConfig) {
|
|
|
132
104
|
});
|
|
133
105
|
} else {
|
|
134
106
|
if (api.versions) yield* emit(PluginEvent.ConfigCascadeWarning({
|
|
135
|
-
ctx: {
|
|
107
|
+
ctx: {},
|
|
136
108
|
level: "warn",
|
|
137
109
|
field: "versions",
|
|
138
110
|
chosen: "(none — multiVersion not configured)",
|
|
@@ -147,255 +119,171 @@ function validateOptions(options, rspressConfig) {
|
|
|
147
119
|
});
|
|
148
120
|
}
|
|
149
121
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
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.
|
|
153
132
|
*/
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
});
|
|
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
|
+
};
|
|
234
212
|
});
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
docsRoot: rspressRoot,
|
|
247
|
-
baseRoute,
|
|
248
|
-
apiFolder: api.apiFolder ?? "api",
|
|
249
|
-
locales: rspressLocales,
|
|
250
|
-
defaultLang: rspressLang,
|
|
251
|
-
versions: [version],
|
|
252
|
-
defaultVersion: rspressMultiVersion?.default
|
|
253
|
-
}))[0];
|
|
254
|
-
if (!versionDp) return {
|
|
255
|
-
vfs: /* @__PURE__ */ new Map(),
|
|
256
|
-
vfsPayloads: [],
|
|
257
|
-
externalPackages: [],
|
|
258
|
-
config: null
|
|
259
|
-
};
|
|
260
|
-
const versionConfig = isVersionConfig(versionValue) ? versionValue : { model: versionValue };
|
|
261
|
-
const { apiPackage, packageJson: versionPackageJson, categories: versionCategories, source: versionSource, externalPackages: versionExternalPackages, autoDetectDependencies: versionAutoDetectDependencies, llmsPlugin: versionLlms, ogImage: versionOgImage } = yield* withModelLoadEvents(loadVersionModel(versionConfig));
|
|
262
|
-
return yield* Effect.promise(async () => {
|
|
263
|
-
Effect.runSync(Metric.update(BuildMetrics.apiVersionsLoaded, 1));
|
|
264
|
-
const resolvedCategories = categoryResolver.resolveCategoryConfig(pluginDefaults, api.categories, versionCategories);
|
|
265
|
-
const resolvedSource = categoryResolver.resolveSourceConfig(api.source, versionSource);
|
|
266
|
-
const resolvedLlms = mergeLlmsPluginConfig(options.llmsPlugin, api.llmsPlugin, versionLlms);
|
|
267
|
-
const packageJson = versionPackageJson || (api.packageJson ? await loadPackageJson(api.packageJson) : void 0);
|
|
268
|
-
validateExternalPackages(versionExternalPackages || api.externalPackages, packageJson);
|
|
269
|
-
const autoDetectOptions = versionAutoDetectDependencies || api.autoDetectDependencies;
|
|
270
|
-
const externalPackages = versionExternalPackages || api.externalPackages || extractAutoDetectedPackages(packageJson, autoDetectOptions);
|
|
271
|
-
if (externalPackages && externalPackages.length > 0) Effect.runSync(Metric.update(BuildMetrics.externalPackagesTotal, externalPackages.length));
|
|
272
|
-
const vfs = ApiExtractedPackage.fromPackage(apiPackage, api.packageName).generateVfs();
|
|
273
|
-
const vfsPayloads = prependImportsToVfs(vfs, apiPackage, api.packageName, wantTrace);
|
|
274
|
-
const resolvedOgImage = versionOgImage ?? api.ogImage ?? options.ogImage;
|
|
275
|
-
const resolvedTheme = normalizeThemeConfig(api.theme);
|
|
276
|
-
const outputDir = versionDp.outputDir;
|
|
277
|
-
const fullRoute = versionDp.routeBase;
|
|
278
|
-
return {
|
|
279
|
-
vfs,
|
|
280
|
-
vfsPayloads,
|
|
281
|
-
externalPackages: externalPackages || [],
|
|
282
|
-
config: {
|
|
283
|
-
apiPackage,
|
|
284
|
-
packageName: `${api.packageName} (${version})`,
|
|
285
|
-
...api.name != null ? { apiName: api.name } : {},
|
|
286
|
-
outputDir,
|
|
287
|
-
baseRoute: fullRoute,
|
|
288
|
-
categories: resolvedCategories,
|
|
289
|
-
...resolvedSource != null ? { source: resolvedSource } : {},
|
|
290
|
-
...packageJson != null ? { packageJson } : {},
|
|
291
|
-
...resolvedLlms != null ? { llmsPlugin: resolvedLlms } : {},
|
|
292
|
-
...options.siteUrl != null ? { siteUrl: options.siteUrl } : {},
|
|
293
|
-
...resolvedOgImage != null ? { ogImage: resolvedOgImage } : {},
|
|
294
|
-
docsDir: path.dirname(outputDir),
|
|
295
|
-
...docsRoot != null ? { docsRoot } : {},
|
|
296
|
-
...resolvedTheme != null ? { theme: resolvedTheme } : {}
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
});
|
|
300
|
-
}), { concurrency: "unbounded" });
|
|
301
|
-
for (const result of versionResults) {
|
|
302
|
-
for (const [filepath, content] of result.vfs.entries()) combinedVfs.set(filepath, content);
|
|
303
|
-
if (result.externalPackages.length > 0) allExternalPackages.push(...result.externalPackages);
|
|
304
|
-
if (result.config) apiConfigs.push(result.config);
|
|
305
|
-
for (const payload of result.vfsPayloads) {
|
|
306
|
-
yield* emit(PluginEvent.VfsGenerated({
|
|
307
|
-
ctx: {
|
|
308
|
-
buildId: "",
|
|
309
|
-
packageName: api.packageName,
|
|
310
|
-
...payload.entryPoint ? { entryPoint: payload.entryPoint } : {}
|
|
311
|
-
},
|
|
312
|
-
level: "debug",
|
|
313
|
-
file: payload.file,
|
|
314
|
-
declCount: payload.declCount,
|
|
315
|
-
contentHash: payload.contentHash,
|
|
316
|
-
...wantTrace && payload.content ? { content: payload.content } : {}
|
|
317
|
-
}));
|
|
318
|
-
if (payload.hasImports) yield* emit(PluginEvent.ImportsPrepended({
|
|
319
|
-
ctx: {
|
|
320
|
-
buildId: "",
|
|
321
|
-
packageName: api.packageName,
|
|
322
|
-
...payload.entryPoint ? { entryPoint: payload.entryPoint } : {}
|
|
323
|
-
},
|
|
324
|
-
level: "debug",
|
|
325
|
-
file: payload.file,
|
|
326
|
-
imports: wantTrace ? payload.importRefs : []
|
|
327
|
-
}));
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
} else if (api.model) {
|
|
331
|
-
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({
|
|
332
224
|
mode: "single",
|
|
333
225
|
docsRoot: rspressRoot,
|
|
334
226
|
baseRoute,
|
|
335
227
|
apiFolder: api.apiFolder ?? "api",
|
|
336
228
|
locales: rspressLocales,
|
|
337
229
|
defaultLang: rspressLang,
|
|
338
|
-
versions: [],
|
|
339
|
-
defaultVersion:
|
|
340
|
-
})
|
|
341
|
-
if (
|
|
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
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}))[0];
|
|
393
|
-
if (!dp) return [];
|
|
394
|
-
const result = yield* processSimpleApi(api, api.model, dp.outputDir, dp.routeBase, wantTrace);
|
|
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);
|
|
395
284
|
for (const payload of result.vfsPayloads) {
|
|
396
285
|
yield* emit(PluginEvent.VfsGenerated({
|
|
397
286
|
ctx: {
|
|
398
|
-
buildId: "",
|
|
399
287
|
packageName: api.packageName,
|
|
400
288
|
...payload.entryPoint ? { entryPoint: payload.entryPoint } : {}
|
|
401
289
|
},
|
|
@@ -407,7 +295,6 @@ function ConfigServiceLive(options, shikiCrossLinker, buildId = "", resolvedThre
|
|
|
407
295
|
}));
|
|
408
296
|
if (payload.hasImports) yield* emit(PluginEvent.ImportsPrepended({
|
|
409
297
|
ctx: {
|
|
410
|
-
buildId: "",
|
|
411
298
|
packageName: api.packageName,
|
|
412
299
|
...payload.entryPoint ? { entryPoint: payload.entryPoint } : {}
|
|
413
300
|
},
|
|
@@ -416,171 +303,196 @@ function ConfigServiceLive(options, shikiCrossLinker, buildId = "", resolvedThre
|
|
|
416
303
|
imports: wantTrace ? payload.importRefs : []
|
|
417
304
|
}));
|
|
418
305
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
306
|
+
}
|
|
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) {
|
|
319
|
+
const result = yield* processSimpleApi(api, api.model, dp.outputDir, dp.routeBase, wantTrace);
|
|
422
320
|
for (const [filepath, content] of result.vfs.entries()) combinedVfs.set(filepath, content);
|
|
423
321
|
if (result.externalPackages.length > 0) allExternalPackages.push(...result.externalPackages);
|
|
424
322
|
apiConfigs.push(result.config);
|
|
323
|
+
for (const payload of result.vfsPayloads) {
|
|
324
|
+
yield* emit(PluginEvent.VfsGenerated({
|
|
325
|
+
ctx: {
|
|
326
|
+
packageName: api.packageName,
|
|
327
|
+
...payload.entryPoint ? { entryPoint: payload.entryPoint } : {}
|
|
328
|
+
},
|
|
329
|
+
level: "debug",
|
|
330
|
+
file: payload.file,
|
|
331
|
+
declCount: payload.declCount,
|
|
332
|
+
contentHash: payload.contentHash,
|
|
333
|
+
...wantTrace && payload.content ? { content: payload.content } : {}
|
|
334
|
+
}));
|
|
335
|
+
if (payload.hasImports) yield* emit(PluginEvent.ImportsPrepended({
|
|
336
|
+
ctx: {
|
|
337
|
+
packageName: api.packageName,
|
|
338
|
+
...payload.entryPoint ? { entryPoint: payload.entryPoint } : {}
|
|
339
|
+
},
|
|
340
|
+
level: "debug",
|
|
341
|
+
file: payload.file,
|
|
342
|
+
imports: wantTrace ? payload.importRefs : []
|
|
343
|
+
}));
|
|
344
|
+
}
|
|
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
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
+
},
|
|
376
|
+
level: "debug",
|
|
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
|
+
},
|
|
466
387
|
level: "debug",
|
|
467
|
-
|
|
468
|
-
|
|
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
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
399
|
+
}
|
|
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
|
+
}));
|
|
503
443
|
}
|
|
504
|
-
manager.initialize(combinedVfs, void 0, void 0, void 0, scopeOptions, twoslashCache);
|
|
505
|
-
manager.registerScope(apiScope, scopeOptions);
|
|
506
444
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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;
|
|
525
473
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
...docsRoot != null ? { docsRoot } : {}
|
|
549
|
-
}) : null;
|
|
550
|
-
const hideCutTransformer = MemberFormatTransformer;
|
|
551
|
-
const hideCutLinesTransformer = HideCutLinesTransformer;
|
|
552
|
-
const twoslashTransformer = TwoslashManager.getInstance().getTransformer() ?? void 0;
|
|
553
|
-
const logLevel = options.logLevel ?? "info";
|
|
554
|
-
const suppressExampleErrors = options.errors?.example !== "show";
|
|
555
|
-
return {
|
|
556
|
-
apiConfigs,
|
|
557
|
-
combinedVfs,
|
|
558
|
-
highlighter,
|
|
559
|
-
resolvedCompilerOptions,
|
|
560
|
-
ogResolver,
|
|
561
|
-
shikiCrossLinker,
|
|
562
|
-
hideCutTransformer,
|
|
563
|
-
hideCutLinesTransformer,
|
|
564
|
-
twoslashTransformer,
|
|
565
|
-
twoslashCache,
|
|
566
|
-
twoslashEnvHash: twoslashEnv,
|
|
567
|
-
pageConcurrency: os.cpus().length,
|
|
568
|
-
logLevel: logLevel === "none" ? "info" : logLevel,
|
|
569
|
-
suppressExampleErrors,
|
|
570
|
-
thresholds: resolvedThresholds ?? DEFAULT_THRESHOLDS,
|
|
571
|
-
buildId
|
|
572
|
-
};
|
|
573
|
-
}) };
|
|
574
|
-
}));
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* Derive the API scope key from a base route, matching the derivation in
|
|
578
|
-
* `build-program.ts` so config resolution and the remark plugins agree on the
|
|
579
|
-
* name a code block is attributed to.
|
|
580
|
-
*/
|
|
581
|
-
function apiScopeOf(baseRoute, packageName) {
|
|
582
|
-
return baseRoute.replace(/^\//, "").split("/")[0] || packageName;
|
|
583
|
-
}
|
|
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
|
+
}));
|
|
584
496
|
/** The raw TypeScript config an API declares, or undefined when it declares none. */
|
|
585
497
|
function rawTsConfig(api) {
|
|
586
498
|
if (api.tsconfig == null && api.compilerOptions == null) return void 0;
|
|
@@ -589,12 +501,6 @@ function rawTsConfig(api) {
|
|
|
589
501
|
if (api.compilerOptions != null) cfg.compilerOptions = api.compilerOptions;
|
|
590
502
|
return cfg;
|
|
591
503
|
}
|
|
592
|
-
/**
|
|
593
|
-
* Strip npm scope from a package name.
|
|
594
|
-
*/
|
|
595
|
-
function unscopedName(packageName) {
|
|
596
|
-
return packageName.startsWith("@") ? packageName.split("/")[1] ?? packageName : packageName;
|
|
597
|
-
}
|
|
598
504
|
|
|
599
505
|
//#endregion
|
|
600
506
|
export { ConfigServiceLive };
|