rspress-plugin-api-extractor 0.1.2 → 0.2.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/README.md +2 -2
- package/api-extracted-package.js +426 -0
- package/build-program.js +120 -0
- package/build-stages.js +664 -0
- package/category-resolver.js +50 -0
- package/code-post-processor.js +38 -0
- package/config-helpers.js +106 -0
- package/config-utils.js +258 -0
- package/content-hash.js +79 -0
- package/errors.js +29 -0
- package/formatter.js +69 -0
- package/hide-cut-transformer.js +96 -0
- package/index.d.ts +901 -831
- package/index.js +4 -6128
- package/layers/ConfigServiceLive.js +377 -0
- package/layers/ObservabilityLive.js +136 -0
- package/layers/PathDerivationServiceLive.js +16 -0
- package/layers/SnapshotServiceLive.js +94 -0
- package/layers/TypeRegistryServiceLive.js +46 -0
- package/llms-processing.js +270 -0
- package/llms-program.js +262 -0
- package/loader.js +186 -0
- package/markdown/cross-linker.js +156 -0
- package/markdown/helpers.js +364 -0
- package/markdown/index.js +11 -0
- package/markdown/page-generators/class-page.js +357 -0
- package/markdown/page-generators/enum-page.js +152 -0
- package/markdown/page-generators/function-page.js +127 -0
- package/markdown/page-generators/index-pages.js +27 -0
- package/markdown/page-generators/interface-page.js +307 -0
- package/markdown/page-generators/namespace-page.js +280 -0
- package/markdown/page-generators/type-alias-page.js +110 -0
- package/markdown/page-generators/variable-page.js +110 -0
- package/markdown/shiki-utils.js +48 -0
- package/migrations/001_create_snapshots.js +25 -0
- package/model-loader.js +95 -0
- package/multi-entry-resolver.js +70 -0
- package/og-resolver.js +271 -0
- package/package.json +63 -73
- package/path-derivation.js +48 -0
- package/plugin.js +218 -0
- package/prettier-formatter.js +73 -0
- package/public/tsconfig/rspress.json +44 -0
- package/remark-api-codeblocks.js +130 -0
- package/remark-with-api.js +172 -0
- package/route-collisions.js +52 -0
- package/runtime/components/ApiExample/index.js +29 -25
- package/runtime/components/ApiLlmsPackageActions/index.js +252 -323
- package/runtime/components/ApiLlmsViewOptions/index.js +269 -340
- package/runtime/components/ApiMember/index.js +49 -47
- package/runtime/components/ApiSignature/index.js +32 -28
- package/runtime/components/EnumMembersTable/{index_module.css → index.css} +18 -19
- package/runtime/components/EnumMembersTable/index.js +36 -67
- package/runtime/components/EnumMembersTable/index.module.js +9 -6
- package/runtime/components/ExampleBlock/{index_module.css → index.css} +2 -3
- package/runtime/components/ExampleBlock/index.js +23 -28
- package/runtime/components/ExampleBlock/index.module.js +8 -5
- package/runtime/components/MarkdownContent/index.js +26 -18
- package/runtime/components/MarkdownText/index.js +28 -22
- package/runtime/components/MemberSignature/{index_module.css → index.css} +5 -6
- package/runtime/components/MemberSignature/index.js +46 -46
- package/runtime/components/MemberSignature/index.module.js +9 -6
- package/runtime/components/ParametersTable/{index_module.css → index.css} +19 -20
- package/runtime/components/ParametersTable/index.js +36 -67
- package/runtime/components/ParametersTable/index.module.js +9 -6
- package/runtime/components/SignatureBlock/{index_module.css → index.css} +5 -6
- package/runtime/components/SignatureBlock/index.js +30 -29
- package/runtime/components/SignatureBlock/index.module.js +9 -6
- package/runtime/components/SignatureCode/{index_module.css → index.css} +9 -10
- package/runtime/components/SignatureCode/index.js +40 -32
- package/runtime/components/SignatureCode/index.module.js +9 -6
- package/runtime/components/SignatureToolbar/{index_module.css → index.css} +18 -20
- package/runtime/components/SignatureToolbar/index.js +48 -51
- package/runtime/components/SignatureToolbar/index.module.js +13 -10
- package/runtime/components/buttons/ButtonGroup.js +13 -6
- package/runtime/components/buttons/CopyCodeButton.js +40 -38
- package/runtime/components/buttons/WrapSignatureButton.js +20 -16
- package/runtime/components/buttons/{index_module.css → index.css} +5 -6
- package/runtime/components/buttons/index.module.js +8 -5
- package/runtime/components/icons/CheckIcon/index.js +20 -17
- package/runtime/components/icons/CopyIcon/index.js +20 -17
- package/runtime/components/icons/UnwrapIcon/index.js +22 -18
- package/runtime/components/icons/WrapIcon/index.js +20 -17
- package/runtime/components/shared/_twoslash.css +3 -10
- package/runtime/components/shared/variables.css +0 -3
- package/runtime/hooks/useWrapToggle.js +32 -9
- package/runtime/index.d.ts +513 -173
- package/runtime/index.js +11 -9
- package/runtime/utils/decode-hast.js +31 -16
- package/runtime/utils/hast-renderer.js +21 -7
- package/schemas/config.js +199 -0
- package/schemas/index.js +5 -0
- package/schemas/opengraph.js +26 -0
- package/schemas/performance.js +19 -0
- package/serve.js +133 -0
- package/services/ConfigService.js +7 -0
- package/services/PathDerivationService.js +7 -0
- package/services/SnapshotService.js +7 -0
- package/services/TypeRegistryService.js +7 -0
- package/shiki-transformer.js +758 -0
- package/tsconfig-parser.js +127 -0
- package/tsdoc-metadata.json +11 -11
- package/twoslash-patterns.js +87 -0
- package/twoslash-transformer.js +316 -0
- package/type-reference-extractor.js +201 -0
- package/typescript-config.js +168 -0
- package/vfs-registry.js +121 -0
- package/0~llms-program.js +0 -344
- package/runtime/components/ApiLlmsPackageActions/index.module.js +0 -2
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { ApiParser } from "../../loader.js";
|
|
2
|
+
import { TypeReferenceExtractor } from "../../type-reference-extractor.js";
|
|
3
|
+
import { escapeMdxGenerics, formatExampleCode, generateAvailableFrom, generateFrontmatter, prepareExampleCode, prependHiddenImports, stripTwoslashDirectives } from "../helpers.js";
|
|
4
|
+
import { markdownCrossLinker } from "../cross-linker.js";
|
|
5
|
+
import { TypeSignatureFormatter } from "../../formatter.js";
|
|
6
|
+
|
|
7
|
+
//#region src/markdown/page-generators/type-alias-page.ts
|
|
8
|
+
/**
|
|
9
|
+
* Generates MDX documentation pages for TypeScript type aliases.
|
|
10
|
+
*
|
|
11
|
+
* This class transforms API Extractor type alias models into rich MDX documentation
|
|
12
|
+
* pages with syntax-highlighted signatures and cross-linked type references.
|
|
13
|
+
*
|
|
14
|
+
* **Page Structure:**
|
|
15
|
+
* 1. Frontmatter with title, description, and Open Graph metadata
|
|
16
|
+
* 2. Component imports
|
|
17
|
+
* 3. Page title (H1) and summary
|
|
18
|
+
* 4. Optional deprecation warning and release tag badge
|
|
19
|
+
* 5. Source code link toolbar
|
|
20
|
+
* 6. Type alias signature block (full type definition)
|
|
21
|
+
* 7. Examples section with Twoslash-enabled code blocks
|
|
22
|
+
* 8. See Also references
|
|
23
|
+
*
|
|
24
|
+
* **Relationships:**
|
|
25
|
+
* - Created and invoked by {@link ApiExtractorPlugin} during page generation
|
|
26
|
+
* - Uses {@link TypeSignatureFormatter} for formatting type signatures
|
|
27
|
+
* - Uses {@link ApiParser} for extracting documentation from API models
|
|
28
|
+
* - Uses {@link MarkdownCrossLinker} for adding type reference links
|
|
29
|
+
*
|
|
30
|
+
* @see {@link InterfacePageGenerator} for interface documentation
|
|
31
|
+
* @see {@link EnumPageGenerator} for enum documentation
|
|
32
|
+
*/
|
|
33
|
+
var TypeAliasPageGenerator = class {
|
|
34
|
+
typeFormatter = new TypeSignatureFormatter();
|
|
35
|
+
/**
|
|
36
|
+
* Generate a markdown page for a type alias
|
|
37
|
+
*
|
|
38
|
+
* @param apiScope - API scope identifier for VFS lookup
|
|
39
|
+
*/
|
|
40
|
+
async generate(apiTypeAlias, baseRoute, packageName, singularName, apiScope, apiName, sourceConfig, suppressExampleErrors, llmsPlugin, availableFrom) {
|
|
41
|
+
const shouldSuppressErrors = suppressExampleErrors ?? true;
|
|
42
|
+
const name = apiTypeAlias.displayName;
|
|
43
|
+
const summary = ApiParser.getSummary(apiTypeAlias) || "No description available.";
|
|
44
|
+
const releaseTag = ApiParser.getReleaseTag(apiTypeAlias);
|
|
45
|
+
let content = generateFrontmatter(name, summary, singularName, apiName);
|
|
46
|
+
content += `import { SourceCode } from "@rspress/core/theme";\n`;
|
|
47
|
+
content += `import { ParametersTable } from "rspress-plugin-api-extractor/runtime";\n`;
|
|
48
|
+
content += `import { ApiSignature, ApiExample } from "rspress-plugin-api-extractor/runtime";\n\n`;
|
|
49
|
+
content += `# ${name}\n\n`;
|
|
50
|
+
const deprecation = ApiParser.getDeprecation(apiTypeAlias);
|
|
51
|
+
if (deprecation) {
|
|
52
|
+
const message = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(deprecation.message));
|
|
53
|
+
content += `> ⚠️ **Deprecated:** ${message}\n\n`;
|
|
54
|
+
}
|
|
55
|
+
if (releaseTag !== "Public") content += `\`${releaseTag}\`\n\n`;
|
|
56
|
+
content += `${summary}\n\n`;
|
|
57
|
+
content += generateAvailableFrom(packageName, availableFrom);
|
|
58
|
+
const sourceLink = ApiParser.getSourceLink(apiTypeAlias, sourceConfig);
|
|
59
|
+
if (sourceLink) {
|
|
60
|
+
content += `<div className="api-docs-toolbar">\n`;
|
|
61
|
+
content += ` <div className="api-docs-toolbar-left">\n`;
|
|
62
|
+
content += ` <SourceCode href="${sourceLink}" />\n`;
|
|
63
|
+
content += ` </div>\n`;
|
|
64
|
+
if (llmsPlugin?.enabled) {
|
|
65
|
+
content += ` <div className="api-docs-toolbar-right">\n`;
|
|
66
|
+
content += ` </div>\n`;
|
|
67
|
+
}
|
|
68
|
+
content += `</div>\n\n`;
|
|
69
|
+
}
|
|
70
|
+
if (apiTypeAlias.excerpt.text) {
|
|
71
|
+
const signature = this.typeFormatter.format(apiTypeAlias.excerpt).trim();
|
|
72
|
+
let signatureWithImports = signature;
|
|
73
|
+
const apiPackage = apiTypeAlias.getAssociatedPackage?.();
|
|
74
|
+
if (apiPackage) signatureWithImports = prependHiddenImports(signature, new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(apiTypeAlias));
|
|
75
|
+
const displayCode = stripTwoslashDirectives(signatureWithImports);
|
|
76
|
+
content += `<ApiSignature code={${JSON.stringify(displayCode)}} source={${JSON.stringify(signatureWithImports)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
|
|
77
|
+
}
|
|
78
|
+
const examples = ApiParser.getExamples(apiTypeAlias);
|
|
79
|
+
if (examples.length > 0) {
|
|
80
|
+
content += `## Examples\n\n`;
|
|
81
|
+
for (const example of examples) {
|
|
82
|
+
const prepared = prepareExampleCode(example, name, packageName, shouldSuppressErrors);
|
|
83
|
+
const formattedCode = await formatExampleCode(prepared.code, prepared.language, {
|
|
84
|
+
api: packageName,
|
|
85
|
+
blockType: "example"
|
|
86
|
+
});
|
|
87
|
+
if (prepared.isTypeScript) {
|
|
88
|
+
const displayCode = stripTwoslashDirectives(formattedCode);
|
|
89
|
+
content += `<ApiExample code={${JSON.stringify(displayCode)}} source={${JSON.stringify(formattedCode)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
|
|
90
|
+
} else content += `\`\`\`${prepared.language}\n${formattedCode}\n\`\`\`\n\n`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const seeReferences = ApiParser.getSeeReferences(apiTypeAlias);
|
|
94
|
+
if (seeReferences.length > 0) {
|
|
95
|
+
content += `## See Also\n\n`;
|
|
96
|
+
for (const reference of seeReferences) {
|
|
97
|
+
const refText = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(reference.text));
|
|
98
|
+
content += `- ${refText}\n`;
|
|
99
|
+
}
|
|
100
|
+
content += `\n`;
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
routePath: `${baseRoute}/type/${name.toLowerCase()}`,
|
|
104
|
+
content
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
//#endregion
|
|
110
|
+
export { TypeAliasPageGenerator };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { ApiParser } from "../../loader.js";
|
|
2
|
+
import { TypeReferenceExtractor } from "../../type-reference-extractor.js";
|
|
3
|
+
import { escapeMdxGenerics, formatExampleCode, generateAvailableFrom, generateFrontmatter, prepareExampleCode, prependHiddenImports, stripTwoslashDirectives } from "../helpers.js";
|
|
4
|
+
import { markdownCrossLinker } from "../cross-linker.js";
|
|
5
|
+
import { TypeSignatureFormatter } from "../../formatter.js";
|
|
6
|
+
|
|
7
|
+
//#region src/markdown/page-generators/variable-page.ts
|
|
8
|
+
/**
|
|
9
|
+
* Generates MDX documentation pages for exported variables and constants.
|
|
10
|
+
*
|
|
11
|
+
* This class transforms API Extractor variable models into rich MDX documentation
|
|
12
|
+
* pages with syntax-highlighted signatures and cross-linked type references.
|
|
13
|
+
*
|
|
14
|
+
* **Page Structure:**
|
|
15
|
+
* 1. Frontmatter with title, description, and Open Graph metadata
|
|
16
|
+
* 2. Component imports
|
|
17
|
+
* 3. Page title (H1) and summary
|
|
18
|
+
* 4. Optional deprecation warning and release tag badge
|
|
19
|
+
* 5. Source code link toolbar
|
|
20
|
+
* 6. Variable signature block (const/let declaration with type)
|
|
21
|
+
* 7. Examples section with Twoslash-enabled code blocks
|
|
22
|
+
* 8. See Also references
|
|
23
|
+
*
|
|
24
|
+
* **Relationships:**
|
|
25
|
+
* - Created and invoked by {@link ApiExtractorPlugin} during page generation
|
|
26
|
+
* - Uses {@link TypeSignatureFormatter} for formatting type signatures
|
|
27
|
+
* - Uses {@link ApiParser} for extracting documentation from API models
|
|
28
|
+
* - Uses {@link MarkdownCrossLinker} for adding type reference links
|
|
29
|
+
*
|
|
30
|
+
* @see {@link FunctionPageGenerator} for function documentation
|
|
31
|
+
* @see {@link EnumPageGenerator} for enum documentation
|
|
32
|
+
*/
|
|
33
|
+
var VariablePageGenerator = class {
|
|
34
|
+
typeFormatter = new TypeSignatureFormatter();
|
|
35
|
+
/**
|
|
36
|
+
* Generate a markdown page for a variable
|
|
37
|
+
*
|
|
38
|
+
* @param apiScope - API scope identifier for VFS lookup
|
|
39
|
+
*/
|
|
40
|
+
async generate(apiVariable, baseRoute, packageName, singularName, apiScope, apiName, sourceConfig, suppressExampleErrors, llmsPlugin, availableFrom) {
|
|
41
|
+
const shouldSuppressErrors = suppressExampleErrors ?? true;
|
|
42
|
+
const name = apiVariable.displayName;
|
|
43
|
+
const summary = ApiParser.getSummary(apiVariable) || "No description available.";
|
|
44
|
+
const releaseTag = ApiParser.getReleaseTag(apiVariable);
|
|
45
|
+
let content = generateFrontmatter(name, summary, singularName, apiName);
|
|
46
|
+
content += `import { SourceCode } from "@rspress/core/theme";\n`;
|
|
47
|
+
content += `import { ParametersTable } from "rspress-plugin-api-extractor/runtime";\n`;
|
|
48
|
+
content += `import { ApiSignature, ApiExample } from "rspress-plugin-api-extractor/runtime";\n\n`;
|
|
49
|
+
content += `# ${name}\n\n`;
|
|
50
|
+
const deprecation = ApiParser.getDeprecation(apiVariable);
|
|
51
|
+
if (deprecation) {
|
|
52
|
+
const message = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(deprecation.message));
|
|
53
|
+
content += `> ⚠️ **Deprecated:** ${message}\n\n`;
|
|
54
|
+
}
|
|
55
|
+
if (releaseTag !== "Public") content += `\`${releaseTag}\`\n\n`;
|
|
56
|
+
content += `${summary}\n\n`;
|
|
57
|
+
content += generateAvailableFrom(packageName, availableFrom);
|
|
58
|
+
const sourceLink = ApiParser.getSourceLink(apiVariable, sourceConfig);
|
|
59
|
+
if (sourceLink) {
|
|
60
|
+
content += `<div className="api-docs-toolbar">\n`;
|
|
61
|
+
content += ` <div className="api-docs-toolbar-left">\n`;
|
|
62
|
+
content += ` <SourceCode href="${sourceLink}" />\n`;
|
|
63
|
+
content += ` </div>\n`;
|
|
64
|
+
if (llmsPlugin?.enabled) {
|
|
65
|
+
content += ` <div className="api-docs-toolbar-right">\n`;
|
|
66
|
+
content += ` </div>\n`;
|
|
67
|
+
}
|
|
68
|
+
content += `</div>\n\n`;
|
|
69
|
+
}
|
|
70
|
+
if (apiVariable.excerpt.text) {
|
|
71
|
+
const signature = this.typeFormatter.format(apiVariable.excerpt).trim();
|
|
72
|
+
let signatureWithImports = signature;
|
|
73
|
+
const apiPackage = apiVariable.getAssociatedPackage?.();
|
|
74
|
+
if (apiPackage) signatureWithImports = prependHiddenImports(signature, new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(apiVariable));
|
|
75
|
+
const displayCode = stripTwoslashDirectives(signatureWithImports);
|
|
76
|
+
content += `<ApiSignature code={${JSON.stringify(displayCode)}} source={${JSON.stringify(signatureWithImports)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
|
|
77
|
+
}
|
|
78
|
+
const examples = ApiParser.getExamples(apiVariable);
|
|
79
|
+
if (examples.length > 0) {
|
|
80
|
+
content += `## Examples\n\n`;
|
|
81
|
+
for (const example of examples) {
|
|
82
|
+
const prepared = prepareExampleCode(example, name, packageName, shouldSuppressErrors);
|
|
83
|
+
const formattedCode = await formatExampleCode(prepared.code, prepared.language, {
|
|
84
|
+
api: packageName,
|
|
85
|
+
blockType: "example"
|
|
86
|
+
});
|
|
87
|
+
if (prepared.isTypeScript) {
|
|
88
|
+
const displayCode = stripTwoslashDirectives(formattedCode);
|
|
89
|
+
content += `<ApiExample code={${JSON.stringify(displayCode)}} source={${JSON.stringify(formattedCode)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
|
|
90
|
+
} else content += `\`\`\`${prepared.language}\n${formattedCode}\n\`\`\`\n\n`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const seeReferences = ApiParser.getSeeReferences(apiVariable);
|
|
94
|
+
if (seeReferences.length > 0) {
|
|
95
|
+
content += `## See Also\n\n`;
|
|
96
|
+
for (const reference of seeReferences) {
|
|
97
|
+
const refText = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(reference.text));
|
|
98
|
+
content += `- ${refText}\n`;
|
|
99
|
+
}
|
|
100
|
+
content += `\n`;
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
routePath: `${baseRoute}/variable/${name.toLowerCase()}`,
|
|
104
|
+
content
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
//#endregion
|
|
110
|
+
export { VariablePageGenerator };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//#region src/markdown/shiki-utils.ts
|
|
2
|
+
/**
|
|
3
|
+
* Default Shiki theme configuration
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_SHIKI_THEMES = {
|
|
6
|
+
light: "github-light-default",
|
|
7
|
+
dark: "github-dark-default"
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Generate a HAST (Hypertext Abstract Syntax Tree) from TypeScript code using Shiki.
|
|
11
|
+
*
|
|
12
|
+
* This function replaces the duplicated `generateShikiHtml` methods across page generators.
|
|
13
|
+
* It produces a JSON-serializable HAST tree instead of HTML strings, which:
|
|
14
|
+
* - Eliminates MDX parsing issues caused by long HTML strings with special characters
|
|
15
|
+
* - Allows clean JSON serialization in generated MDX files
|
|
16
|
+
* - Enables runtime rendering via `hast-util-to-jsx-runtime` without `dangerouslySetInnerHTML`
|
|
17
|
+
*
|
|
18
|
+
* @param code - The TypeScript code to highlight
|
|
19
|
+
* @param highlighter - Shiki highlighter instance (optional, returns null if not provided)
|
|
20
|
+
* @param transformers - Optional array of Shiki transformers (e.g., Twoslash, cross-linker)
|
|
21
|
+
* @param enableTwoslash - If true, adds meta to trigger Twoslash directive processing
|
|
22
|
+
* @param theme - Optional theme configuration (defaults to github-light/github-dark)
|
|
23
|
+
* @returns A HAST root node, or null if no highlighter is provided or an error occurs
|
|
24
|
+
*/
|
|
25
|
+
async function generateShikiHast(code, highlighter, transformers, enableTwoslash, theme) {
|
|
26
|
+
if (!highlighter) return null;
|
|
27
|
+
const resolvedTheme = theme ?? DEFAULT_SHIKI_THEMES;
|
|
28
|
+
try {
|
|
29
|
+
const options = {
|
|
30
|
+
lang: "typescript",
|
|
31
|
+
themes: {
|
|
32
|
+
light: resolvedTheme.light,
|
|
33
|
+
dark: resolvedTheme.dark
|
|
34
|
+
},
|
|
35
|
+
defaultColor: false,
|
|
36
|
+
cssVariablePrefix: "--api-shiki-",
|
|
37
|
+
transformers: transformers || []
|
|
38
|
+
};
|
|
39
|
+
if (enableTwoslash) options.meta = { __raw: "twoslash" };
|
|
40
|
+
return await highlighter.codeToHast(code, options);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error("Failed to generate Shiki HAST:", error);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
48
|
+
export { DEFAULT_SHIKI_THEMES, generateShikiHast };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import * as SqlClient from "@effect/sql/SqlClient";
|
|
3
|
+
|
|
4
|
+
//#region src/migrations/001_create_snapshots.ts
|
|
5
|
+
const migration = Effect.gen(function* () {
|
|
6
|
+
const sql = yield* SqlClient.SqlClient;
|
|
7
|
+
yield* sql`
|
|
8
|
+
CREATE TABLE IF NOT EXISTS file_snapshots (
|
|
9
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
10
|
+
output_dir TEXT NOT NULL,
|
|
11
|
+
file_path TEXT NOT NULL,
|
|
12
|
+
published_time TEXT NOT NULL,
|
|
13
|
+
modified_time TEXT NOT NULL,
|
|
14
|
+
content_hash TEXT NOT NULL,
|
|
15
|
+
frontmatter_hash TEXT NOT NULL,
|
|
16
|
+
build_time TEXT NOT NULL,
|
|
17
|
+
UNIQUE(output_dir, file_path)
|
|
18
|
+
)
|
|
19
|
+
`;
|
|
20
|
+
yield* sql`CREATE INDEX IF NOT EXISTS idx_output_dir ON file_snapshots(output_dir)`;
|
|
21
|
+
yield* sql`CREATE INDEX IF NOT EXISTS idx_file_path ON file_snapshots(file_path)`;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
export { migration as default };
|
package/model-loader.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { isLoadedModel, isVersionConfig } from "./config-utils.js";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { loadApiModel } from "api-extractor-llms";
|
|
5
|
+
|
|
6
|
+
//#region src/model-loader.ts
|
|
7
|
+
/**
|
|
8
|
+
* Utility class for loading API models from various sources
|
|
9
|
+
*/
|
|
10
|
+
var ApiModelLoader = class ApiModelLoader {
|
|
11
|
+
/**
|
|
12
|
+
* Private constructor to prevent instantiation
|
|
13
|
+
*/
|
|
14
|
+
constructor() {}
|
|
15
|
+
/**
|
|
16
|
+
* Load an API model from a path (string, URL, or Buffer)
|
|
17
|
+
*/
|
|
18
|
+
static async loadFromPath(modelPath) {
|
|
19
|
+
const resolvedPath = path.resolve(modelPath.toString());
|
|
20
|
+
if (!fs.existsSync(resolvedPath)) throw new Error(`API model file not found: ${resolvedPath}`);
|
|
21
|
+
return loadApiModel(resolvedPath);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Load package.json from a path (string, URL, or Buffer)
|
|
25
|
+
*/
|
|
26
|
+
static async loadPackageJsonFromPath(pkgPath) {
|
|
27
|
+
const resolvedPath = path.resolve(pkgPath.toString());
|
|
28
|
+
if (!fs.existsSync(resolvedPath)) throw new Error(`Package.json file not found: ${resolvedPath}`);
|
|
29
|
+
const content = fs.readFileSync(resolvedPath, "utf-8");
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(content);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
throw new Error(`Failed to parse package.json at ${resolvedPath}: ${error.message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Load package.json from PathLike or async function
|
|
38
|
+
*/
|
|
39
|
+
static async loadPackageJson(loader) {
|
|
40
|
+
if (typeof loader === "function") return await loader();
|
|
41
|
+
return await ApiModelLoader.loadPackageJsonFromPath(loader);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Load an API model from PathLike or async function
|
|
45
|
+
*/
|
|
46
|
+
static async loadApiModel(loader) {
|
|
47
|
+
if (typeof loader === "function") {
|
|
48
|
+
const result = await loader();
|
|
49
|
+
if (isLoadedModel(result)) {
|
|
50
|
+
const model = result.model;
|
|
51
|
+
if (model && typeof model === "object" && "packages" in model) {
|
|
52
|
+
const packages = model.packages;
|
|
53
|
+
if (packages.length === 0) throw new Error("API model returned by function contains no packages");
|
|
54
|
+
const loadedResult = { apiPackage: packages[0] };
|
|
55
|
+
if (result.source != null) loadedResult.source = result.source;
|
|
56
|
+
return loadedResult;
|
|
57
|
+
}
|
|
58
|
+
throw new Error("API model loader function must return an ApiModel");
|
|
59
|
+
}
|
|
60
|
+
if (result && typeof result === "object" && "packages" in result) {
|
|
61
|
+
const packages = result.packages;
|
|
62
|
+
if (packages.length === 0) throw new Error("API model returned by function contains no packages");
|
|
63
|
+
return { apiPackage: packages[0] };
|
|
64
|
+
}
|
|
65
|
+
throw new Error("API model loader function must return an ApiModel or LoadedModel");
|
|
66
|
+
}
|
|
67
|
+
return { apiPackage: await ApiModelLoader.loadFromPath(loader) };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Resolve and load a version config
|
|
71
|
+
*/
|
|
72
|
+
static async loadVersionModel(versionValue) {
|
|
73
|
+
if (isVersionConfig(versionValue)) {
|
|
74
|
+
const { apiPackage, source: loaderSource } = await ApiModelLoader.loadApiModel(versionValue.model);
|
|
75
|
+
const packageJson = versionValue.packageJson ? await ApiModelLoader.loadPackageJson(versionValue.packageJson) : void 0;
|
|
76
|
+
const versionResult = { apiPackage };
|
|
77
|
+
if (packageJson != null) versionResult.packageJson = packageJson;
|
|
78
|
+
if (versionValue.categories != null) versionResult.categories = versionValue.categories;
|
|
79
|
+
const resolvedSource = loaderSource || versionValue.source;
|
|
80
|
+
if (resolvedSource != null) versionResult.source = resolvedSource;
|
|
81
|
+
if (versionValue.externalPackages != null) versionResult.externalPackages = versionValue.externalPackages;
|
|
82
|
+
if (versionValue.autoDetectDependencies != null) versionResult.autoDetectDependencies = versionValue.autoDetectDependencies;
|
|
83
|
+
if (versionValue.ogImage != null) versionResult.ogImage = versionValue.ogImage;
|
|
84
|
+
if (versionValue.llmsPlugin != null) versionResult.llmsPlugin = versionValue.llmsPlugin;
|
|
85
|
+
return versionResult;
|
|
86
|
+
}
|
|
87
|
+
const { apiPackage, source } = await ApiModelLoader.loadApiModel(versionValue);
|
|
88
|
+
const simpleResult = { apiPackage };
|
|
89
|
+
if (source != null) simpleResult.source = source;
|
|
90
|
+
return simpleResult;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
//#endregion
|
|
95
|
+
export { ApiModelLoader };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
//#region src/multi-entry-resolver.ts
|
|
2
|
+
/**
|
|
3
|
+
* Derive an entry point name from its display name in the API model.
|
|
4
|
+
*
|
|
5
|
+
* - Empty string (main entry "." in package.json) maps to "default"
|
|
6
|
+
* - Named entries (e.g., "testing") keep their name
|
|
7
|
+
*/
|
|
8
|
+
function getEntryPointName(displayName) {
|
|
9
|
+
return displayName === "" ? "default" : displayName;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create a stable identity key for an API item based on its display name and kind.
|
|
13
|
+
* Used to detect re-exports across entry points.
|
|
14
|
+
*/
|
|
15
|
+
function itemKey(item) {
|
|
16
|
+
return `${item.displayName}::${item.kind}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolve all entry points from an API package into a flat list of
|
|
20
|
+
* deduplicated items.
|
|
21
|
+
*
|
|
22
|
+
* - Re-exported items (same displayName + kind across entries) are
|
|
23
|
+
* deduplicated to a single entry with availableFrom listing all
|
|
24
|
+
* entry points. The defining entry point prefers "default".
|
|
25
|
+
* - Items with different kinds but the same displayName (e.g. the
|
|
26
|
+
* Effect const + type companion pattern) remain as separate entries.
|
|
27
|
+
*
|
|
28
|
+
* @param apiPackage - The merged API package with 1+ entry points
|
|
29
|
+
* @returns Flat array of resolved items
|
|
30
|
+
*/
|
|
31
|
+
function resolveEntryPoints(apiPackage) {
|
|
32
|
+
const itemsByKey = /* @__PURE__ */ new Map();
|
|
33
|
+
for (const entryPoint of apiPackage.entryPoints) {
|
|
34
|
+
const epName = getEntryPointName(entryPoint.displayName);
|
|
35
|
+
for (const member of entryPoint.members) {
|
|
36
|
+
const key = itemKey(member);
|
|
37
|
+
const existing = itemsByKey.get(key) || [];
|
|
38
|
+
existing.push({
|
|
39
|
+
item: member,
|
|
40
|
+
entryPointName: epName
|
|
41
|
+
});
|
|
42
|
+
itemsByKey.set(key, existing);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const intermediate = [];
|
|
46
|
+
for (const [, entries] of itemsByKey) if (entries.length === 1) {
|
|
47
|
+
const { item, entryPointName } = entries[0];
|
|
48
|
+
intermediate.push({
|
|
49
|
+
item,
|
|
50
|
+
definingEntryPoint: entryPointName,
|
|
51
|
+
availableFrom: [entryPointName]
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
const definingEntry = entries.find((e) => e.entryPointName === "default") || entries[0];
|
|
55
|
+
const allEntryPoints = [...new Set(entries.map((e) => e.entryPointName))];
|
|
56
|
+
intermediate.push({
|
|
57
|
+
item: definingEntry.item,
|
|
58
|
+
definingEntryPoint: definingEntry.entryPointName,
|
|
59
|
+
availableFrom: allEntryPoints
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return intermediate.map((r) => ({
|
|
63
|
+
item: r.item,
|
|
64
|
+
definingEntryPoint: r.definingEntryPoint,
|
|
65
|
+
availableFrom: r.availableFrom
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
export { resolveEntryPoints };
|