@urbicon-ui/docs-gen 6.1.4
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 +92 -0
- package/dist/cli/CLI.d.ts +10 -0
- package/dist/cli/CLI.d.ts.map +1 -0
- package/dist/cli/CLI.js +340 -0
- package/dist/cli/CLI.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/discovery/ComponentFinder.d.ts +43 -0
- package/dist/core/discovery/ComponentFinder.d.ts.map +1 -0
- package/dist/core/discovery/ComponentFinder.js +421 -0
- package/dist/core/discovery/ComponentFinder.js.map +1 -0
- package/dist/core/discovery/FileStructureAnalyzer.d.ts +15 -0
- package/dist/core/discovery/FileStructureAnalyzer.d.ts.map +1 -0
- package/dist/core/discovery/FileStructureAnalyzer.js +35 -0
- package/dist/core/discovery/FileStructureAnalyzer.js.map +1 -0
- package/dist/core/discovery/MetadataExtractor.d.ts +15 -0
- package/dist/core/discovery/MetadataExtractor.d.ts.map +1 -0
- package/dist/core/discovery/MetadataExtractor.js +47 -0
- package/dist/core/discovery/MetadataExtractor.js.map +1 -0
- package/dist/core/discovery/index.d.ts +2 -0
- package/dist/core/discovery/index.d.ts.map +1 -0
- package/dist/core/discovery/index.js +2 -0
- package/dist/core/discovery/index.js.map +1 -0
- package/dist/core/enrichment/APIDataGenerator.d.ts +77 -0
- package/dist/core/enrichment/APIDataGenerator.d.ts.map +1 -0
- package/dist/core/enrichment/APIDataGenerator.js +663 -0
- package/dist/core/enrichment/APIDataGenerator.js.map +1 -0
- package/dist/core/enrichment/index.d.ts +2 -0
- package/dist/core/enrichment/index.d.ts.map +1 -0
- package/dist/core/enrichment/index.js +6 -0
- package/dist/core/enrichment/index.js.map +1 -0
- package/dist/core/extraction/ExtractionCoordinator.d.ts +53 -0
- package/dist/core/extraction/ExtractionCoordinator.d.ts.map +1 -0
- package/dist/core/extraction/ExtractionCoordinator.js +352 -0
- package/dist/core/extraction/ExtractionCoordinator.js.map +1 -0
- package/dist/core/extraction/index.d.ts +2 -0
- package/dist/core/extraction/index.d.ts.map +1 -0
- package/dist/core/extraction/index.js +2 -0
- package/dist/core/extraction/index.js.map +1 -0
- package/dist/core/generation/GenerationCoordinator.d.ts +51 -0
- package/dist/core/generation/GenerationCoordinator.d.ts.map +1 -0
- package/dist/core/generation/GenerationCoordinator.js +206 -0
- package/dist/core/generation/GenerationCoordinator.js.map +1 -0
- package/dist/core/generation/index.d.ts +2 -0
- package/dist/core/generation/index.d.ts.map +1 -0
- package/dist/core/generation/index.js +2 -0
- package/dist/core/generation/index.js.map +1 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +16 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/pipeline/ErrorHandler.d.ts +129 -0
- package/dist/core/pipeline/ErrorHandler.d.ts.map +1 -0
- package/dist/core/pipeline/ErrorHandler.js +321 -0
- package/dist/core/pipeline/ErrorHandler.js.map +1 -0
- package/dist/core/pipeline/PipelineOrchestrator.d.ts +45 -0
- package/dist/core/pipeline/PipelineOrchestrator.d.ts.map +1 -0
- package/dist/core/pipeline/PipelineOrchestrator.js +216 -0
- package/dist/core/pipeline/PipelineOrchestrator.js.map +1 -0
- package/dist/core/pipeline/index.d.ts +1 -0
- package/dist/core/pipeline/index.d.ts.map +1 -0
- package/dist/core/pipeline/index.js +2 -0
- package/dist/core/pipeline/index.js.map +1 -0
- package/dist/extractors/BaseExtractor.d.ts +33 -0
- package/dist/extractors/BaseExtractor.d.ts.map +1 -0
- package/dist/extractors/BaseExtractor.js +68 -0
- package/dist/extractors/BaseExtractor.js.map +1 -0
- package/dist/extractors/ExtractorFactory.d.ts +35 -0
- package/dist/extractors/ExtractorFactory.d.ts.map +1 -0
- package/dist/extractors/ExtractorFactory.js +88 -0
- package/dist/extractors/ExtractorFactory.js.map +1 -0
- package/dist/extractors/documentation/index.d.ts +1 -0
- package/dist/extractors/documentation/index.d.ts.map +1 -0
- package/dist/extractors/documentation/index.js +2 -0
- package/dist/extractors/documentation/index.js.map +1 -0
- package/dist/extractors/index.d.ts +1 -0
- package/dist/extractors/index.d.ts.map +1 -0
- package/dist/extractors/index.js +2 -0
- package/dist/extractors/index.js.map +1 -0
- package/dist/extractors/typescript/ExamplesExtractor.d.ts +32 -0
- package/dist/extractors/typescript/ExamplesExtractor.d.ts.map +1 -0
- package/dist/extractors/typescript/ExamplesExtractor.js +209 -0
- package/dist/extractors/typescript/ExamplesExtractor.js.map +1 -0
- package/dist/extractors/typescript/InheritanceExtractor.d.ts +45 -0
- package/dist/extractors/typescript/InheritanceExtractor.d.ts.map +1 -0
- package/dist/extractors/typescript/InheritanceExtractor.js +368 -0
- package/dist/extractors/typescript/InheritanceExtractor.js.map +1 -0
- package/dist/extractors/typescript/LocalTypesExtractor.d.ts +20 -0
- package/dist/extractors/typescript/LocalTypesExtractor.d.ts.map +1 -0
- package/dist/extractors/typescript/LocalTypesExtractor.js +100 -0
- package/dist/extractors/typescript/LocalTypesExtractor.js.map +1 -0
- package/dist/extractors/typescript/PropsExtractor.d.ts +142 -0
- package/dist/extractors/typescript/PropsExtractor.d.ts.map +1 -0
- package/dist/extractors/typescript/PropsExtractor.js +709 -0
- package/dist/extractors/typescript/PropsExtractor.js.map +1 -0
- package/dist/extractors/typescript/TypeScriptBaseExtractor.d.ts +74 -0
- package/dist/extractors/typescript/TypeScriptBaseExtractor.d.ts.map +1 -0
- package/dist/extractors/typescript/TypeScriptBaseExtractor.js +249 -0
- package/dist/extractors/typescript/TypeScriptBaseExtractor.js.map +1 -0
- package/dist/extractors/typescript/index.d.ts +1 -0
- package/dist/extractors/typescript/index.d.ts.map +1 -0
- package/dist/extractors/typescript/index.js +2 -0
- package/dist/extractors/typescript/index.js.map +1 -0
- package/dist/extractors/variants/TailwindVariantsParser.d.ts +16 -0
- package/dist/extractors/variants/TailwindVariantsParser.d.ts.map +1 -0
- package/dist/extractors/variants/TailwindVariantsParser.js +53 -0
- package/dist/extractors/variants/TailwindVariantsParser.js.map +1 -0
- package/dist/extractors/variants/VariantsExtractor.d.ts +45 -0
- package/dist/extractors/variants/VariantsExtractor.d.ts.map +1 -0
- package/dist/extractors/variants/VariantsExtractor.js +340 -0
- package/dist/extractors/variants/VariantsExtractor.js.map +1 -0
- package/dist/extractors/variants/index.d.ts +2 -0
- package/dist/extractors/variants/index.d.ts.map +1 -0
- package/dist/extractors/variants/index.js +2 -0
- package/dist/extractors/variants/index.js.map +1 -0
- package/dist/generators/api/APIFileGenerator.d.ts +32 -0
- package/dist/generators/api/APIFileGenerator.d.ts.map +1 -0
- package/dist/generators/api/APIFileGenerator.js +450 -0
- package/dist/generators/api/APIFileGenerator.js.map +1 -0
- package/dist/generators/api/TypeDefinitionGenerator.d.ts +1 -0
- package/dist/generators/api/TypeDefinitionGenerator.d.ts.map +1 -0
- package/dist/generators/api/TypeDefinitionGenerator.js +2 -0
- package/dist/generators/api/TypeDefinitionGenerator.js.map +1 -0
- package/dist/generators/api/index.d.ts +2 -0
- package/dist/generators/api/index.d.ts.map +1 -0
- package/dist/generators/api/index.js +2 -0
- package/dist/generators/api/index.js.map +1 -0
- package/dist/generators/index.d.ts +5 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +4 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/generators/llm/LLMDocumentationGenerator.d.ts +34 -0
- package/dist/generators/llm/LLMDocumentationGenerator.d.ts.map +1 -0
- package/dist/generators/llm/LLMDocumentationGenerator.js +480 -0
- package/dist/generators/llm/LLMDocumentationGenerator.js.map +1 -0
- package/dist/generators/llm/LLMFormatter.d.ts +4 -0
- package/dist/generators/llm/LLMFormatter.d.ts.map +1 -0
- package/dist/generators/llm/LLMFormatter.js +8 -0
- package/dist/generators/llm/LLMFormatter.js.map +1 -0
- package/dist/generators/llm/LlmsFullAssembler.d.ts +20 -0
- package/dist/generators/llm/LlmsFullAssembler.d.ts.map +1 -0
- package/dist/generators/llm/LlmsFullAssembler.js +63 -0
- package/dist/generators/llm/LlmsFullAssembler.js.map +1 -0
- package/dist/generators/llm/index.d.ts +1 -0
- package/dist/generators/llm/index.d.ts.map +1 -0
- package/dist/generators/llm/index.js +2 -0
- package/dist/generators/llm/index.js.map +1 -0
- package/dist/generators/mcp/MCPCatalogAssembler.d.ts +27 -0
- package/dist/generators/mcp/MCPCatalogAssembler.d.ts.map +1 -0
- package/dist/generators/mcp/MCPCatalogAssembler.js +147 -0
- package/dist/generators/mcp/MCPCatalogAssembler.js.map +1 -0
- package/dist/generators/mcp/MCPCatalogGenerator.d.ts +55 -0
- package/dist/generators/mcp/MCPCatalogGenerator.d.ts.map +1 -0
- package/dist/generators/mcp/MCPCatalogGenerator.js +153 -0
- package/dist/generators/mcp/MCPCatalogGenerator.js.map +1 -0
- package/dist/generators/svelte/PageGenerator.d.ts +82 -0
- package/dist/generators/svelte/PageGenerator.d.ts.map +1 -0
- package/dist/generators/svelte/PageGenerator.js +557 -0
- package/dist/generators/svelte/PageGenerator.js.map +1 -0
- package/dist/generators/svelte/PlaygroundPresets.d.ts +2 -0
- package/dist/generators/svelte/PlaygroundPresets.d.ts.map +1 -0
- package/dist/generators/svelte/PlaygroundPresets.js +4 -0
- package/dist/generators/svelte/PlaygroundPresets.js.map +1 -0
- package/dist/generators/svelte/SectionMerger.d.ts +38 -0
- package/dist/generators/svelte/SectionMerger.d.ts.map +1 -0
- package/dist/generators/svelte/SectionMerger.js +154 -0
- package/dist/generators/svelte/SectionMerger.js.map +1 -0
- package/dist/generators/svelte/TemplateEngine.d.ts +91 -0
- package/dist/generators/svelte/TemplateEngine.d.ts.map +1 -0
- package/dist/generators/svelte/TemplateEngine.js +500 -0
- package/dist/generators/svelte/TemplateEngine.js.map +1 -0
- package/dist/generators/svelte/index.d.ts +8 -0
- package/dist/generators/svelte/index.d.ts.map +1 -0
- package/dist/generators/svelte/index.js +11 -0
- package/dist/generators/svelte/index.js.map +1 -0
- package/dist/generators/svelte/renderers/ApiRenderer.d.ts +17 -0
- package/dist/generators/svelte/renderers/ApiRenderer.d.ts.map +1 -0
- package/dist/generators/svelte/renderers/ApiRenderer.js +44 -0
- package/dist/generators/svelte/renderers/ApiRenderer.js.map +1 -0
- package/dist/generators/svelte/renderers/ExampleRenderer.d.ts +33 -0
- package/dist/generators/svelte/renderers/ExampleRenderer.d.ts.map +1 -0
- package/dist/generators/svelte/renderers/ExampleRenderer.js +137 -0
- package/dist/generators/svelte/renderers/ExampleRenderer.js.map +1 -0
- package/dist/generators/svelte/renderers/OverviewRenderer.d.ts +9 -0
- package/dist/generators/svelte/renderers/OverviewRenderer.d.ts.map +1 -0
- package/dist/generators/svelte/renderers/OverviewRenderer.js +45 -0
- package/dist/generators/svelte/renderers/OverviewRenderer.js.map +1 -0
- package/dist/generators/svelte/renderers/PlaygroundRenderer.d.ts +35 -0
- package/dist/generators/svelte/renderers/PlaygroundRenderer.d.ts.map +1 -0
- package/dist/generators/svelte/renderers/PlaygroundRenderer.js +319 -0
- package/dist/generators/svelte/renderers/PlaygroundRenderer.js.map +1 -0
- package/dist/generators/svelte/renderers/TypesRenderer.d.ts +9 -0
- package/dist/generators/svelte/renderers/TypesRenderer.d.ts.map +1 -0
- package/dist/generators/svelte/renderers/TypesRenderer.js +23 -0
- package/dist/generators/svelte/renderers/TypesRenderer.js.map +1 -0
- package/dist/generators/svelte/renderers/UsageRenderer.d.ts +14 -0
- package/dist/generators/svelte/renderers/UsageRenderer.d.ts.map +1 -0
- package/dist/generators/svelte/renderers/UsageRenderer.js +91 -0
- package/dist/generators/svelte/renderers/UsageRenderer.js.map +1 -0
- package/dist/generators/svelte/renderers/VariantsRenderer.d.ts +43 -0
- package/dist/generators/svelte/renderers/VariantsRenderer.d.ts.map +1 -0
- package/dist/generators/svelte/renderers/VariantsRenderer.js +277 -0
- package/dist/generators/svelte/renderers/VariantsRenderer.js.map +1 -0
- package/dist/generators/svelte/renderers/index.d.ts +8 -0
- package/dist/generators/svelte/renderers/index.d.ts.map +1 -0
- package/dist/generators/svelte/renderers/index.js +8 -0
- package/dist/generators/svelte/renderers/index.js.map +1 -0
- package/dist/generators/svelte/types.d.ts +13 -0
- package/dist/generators/svelte/types.d.ts.map +1 -0
- package/dist/generators/svelte/types.js +2 -0
- package/dist/generators/svelte/types.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/SvelteDocsParser.d.ts +44 -0
- package/dist/parsers/SvelteDocsParser.d.ts.map +1 -0
- package/dist/parsers/SvelteDocsParser.js +141 -0
- package/dist/parsers/SvelteDocsParser.js.map +1 -0
- package/dist/parsers/index.d.ts +2 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +6 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/schema/ConfigurationBuilder.d.ts +55 -0
- package/dist/schema/ConfigurationBuilder.d.ts.map +1 -0
- package/dist/schema/ConfigurationBuilder.js +447 -0
- package/dist/schema/ConfigurationBuilder.js.map +1 -0
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/configuration.d.ts +255 -0
- package/dist/types/configuration.d.ts.map +1 -0
- package/dist/types/configuration.js +2 -0
- package/dist/types/configuration.js.map +1 -0
- package/dist/types/core.d.ts +96 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/types/core.js +2 -0
- package/dist/types/core.js.map +1 -0
- package/dist/types/docs-config.d.ts +5 -0
- package/dist/types/docs-config.d.ts.map +1 -0
- package/dist/types/docs-config.js +51 -0
- package/dist/types/docs-config.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +9 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/validation.d.ts +321 -0
- package/dist/types/validation.d.ts.map +1 -0
- package/dist/types/validation.js +2 -0
- package/dist/types/validation.js.map +1 -0
- package/dist/utils/DeploymentManager.d.ts +80 -0
- package/dist/utils/DeploymentManager.d.ts.map +1 -0
- package/dist/utils/DeploymentManager.js +360 -0
- package/dist/utils/DeploymentManager.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import { TypeScriptBaseExtractor } from './TypeScriptBaseExtractor';
|
|
3
|
+
/**
|
|
4
|
+
* Extracts props from TypeScript interfaces using AST parsing
|
|
5
|
+
* Supports JSDoc comments, type analysis, and prop categorization
|
|
6
|
+
*/
|
|
7
|
+
export class PropsExtractor extends TypeScriptBaseExtractor {
|
|
8
|
+
config;
|
|
9
|
+
currentVariantKeys = [];
|
|
10
|
+
constructor(tsConfig) {
|
|
11
|
+
super(tsConfig);
|
|
12
|
+
const ext = tsConfig
|
|
13
|
+
?.extraction?.typescript;
|
|
14
|
+
this.config = {
|
|
15
|
+
extractJSDoc: true,
|
|
16
|
+
extractTypeReferences: true,
|
|
17
|
+
extractDefaultValues: true,
|
|
18
|
+
resolveTypeAliases: true,
|
|
19
|
+
includePrivateProps: false,
|
|
20
|
+
...(ext ?? tsConfig)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Extract props from TypeScript interface
|
|
25
|
+
*/
|
|
26
|
+
async extract(input) {
|
|
27
|
+
const startTime = Date.now();
|
|
28
|
+
if (!this.validateInput(input)) {
|
|
29
|
+
return this.handleError(new Error('Invalid input for props extraction'), input);
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
console.log(`🔍 Extracting props for ${input.componentName} from ${input.filePath}`);
|
|
33
|
+
// Keep variant keys for Omit filtering within this extraction call
|
|
34
|
+
this.currentVariantKeys = Array.isArray(input.variantKeys) ? input.variantKeys : [];
|
|
35
|
+
const sourceFile = await this.getSourceFile(input.filePath);
|
|
36
|
+
if (!sourceFile) {
|
|
37
|
+
return this.handleError(new Error(`Could not load source file: ${input.filePath}`), input);
|
|
38
|
+
}
|
|
39
|
+
// Find the props declaration — either an interface or a
|
|
40
|
+
// type-alias whose RHS is a union (discriminated-union pattern).
|
|
41
|
+
const propsDeclaration = this.findPropsDeclaration(sourceFile, input.componentName);
|
|
42
|
+
if (!propsDeclaration) {
|
|
43
|
+
const warning = this.addWarning('no_props_interface', `No ${input.componentName}Props interface found`, `Create a ${input.componentName}Props interface to document component properties`);
|
|
44
|
+
return this.createSuccessResult([], [warning], input.filePath, Date.now() - startTime);
|
|
45
|
+
}
|
|
46
|
+
console.log(`✅ Found ${input.componentName}Props (${propsDeclaration.kind === 'interface' ? 'interface' : 'union type alias'})`);
|
|
47
|
+
// Extract props from interface OR from the union members.
|
|
48
|
+
const props = propsDeclaration.kind === 'interface'
|
|
49
|
+
? await this.extractPropsFromInterface(propsDeclaration.node, sourceFile)
|
|
50
|
+
: await this.extractPropsFromUnion(propsDeclaration.node, sourceFile, propsDeclaration.jsDocHost.name.text);
|
|
51
|
+
console.log(`📝 Extracted ${props.length} props from ${input.componentName}Props`);
|
|
52
|
+
const duration = Date.now() - startTime;
|
|
53
|
+
const result = this.createSuccessResult(props, [], input.filePath, duration);
|
|
54
|
+
// Clear per-call state
|
|
55
|
+
this.currentVariantKeys = [];
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
this.currentVariantKeys = [];
|
|
60
|
+
return this.handleError(error, input);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Extract the component description from the JSDoc on the *Props declaration.
|
|
65
|
+
* Checks @description tag first, then falls back to leading prose text.
|
|
66
|
+
*/
|
|
67
|
+
async extractDescription(input) {
|
|
68
|
+
const sourceFile = await this.getSourceFile(input.filePath);
|
|
69
|
+
if (!sourceFile)
|
|
70
|
+
return null;
|
|
71
|
+
const declaration = this.findPropsDeclaration(sourceFile, input.componentName);
|
|
72
|
+
if (!declaration)
|
|
73
|
+
return null;
|
|
74
|
+
const tagged = this.extractJSDocTag(declaration.jsDocHost, 'description');
|
|
75
|
+
if (tagged)
|
|
76
|
+
return tagged;
|
|
77
|
+
return this.extractJSDocComment(declaration.jsDocHost);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Extract all @tag values from the *Props declaration JSDoc.
|
|
81
|
+
*/
|
|
82
|
+
async extractTags(input) {
|
|
83
|
+
const sourceFile = await this.getSourceFile(input.filePath);
|
|
84
|
+
if (!sourceFile)
|
|
85
|
+
return [];
|
|
86
|
+
const declaration = this.findPropsDeclaration(sourceFile, input.componentName);
|
|
87
|
+
if (!declaration)
|
|
88
|
+
return [];
|
|
89
|
+
return this.extractJSDocTagAll(declaration.jsDocHost, 'tag');
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Extract all @related values from the *Props declaration JSDoc.
|
|
93
|
+
*/
|
|
94
|
+
async extractRelated(input) {
|
|
95
|
+
const sourceFile = await this.getSourceFile(input.filePath);
|
|
96
|
+
if (!sourceFile)
|
|
97
|
+
return [];
|
|
98
|
+
const declaration = this.findPropsDeclaration(sourceFile, input.componentName);
|
|
99
|
+
if (!declaration)
|
|
100
|
+
return [];
|
|
101
|
+
return this.extractJSDocTagAll(declaration.jsDocHost, 'related');
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Extract the `@stability` tag from the *Props declaration JSDoc.
|
|
105
|
+
* Accepts `experimental | beta | stable | deprecated`; unknown
|
|
106
|
+
* values are dropped so a typo doesn't silently propagate into the
|
|
107
|
+
* Editorial badge.
|
|
108
|
+
*/
|
|
109
|
+
async extractStability(input) {
|
|
110
|
+
const sourceFile = await this.getSourceFile(input.filePath);
|
|
111
|
+
if (!sourceFile)
|
|
112
|
+
return null;
|
|
113
|
+
const declaration = this.findPropsDeclaration(sourceFile, input.componentName);
|
|
114
|
+
if (!declaration)
|
|
115
|
+
return null;
|
|
116
|
+
const raw = this.extractJSDocTag(declaration.jsDocHost, 'stability');
|
|
117
|
+
if (!raw)
|
|
118
|
+
return null;
|
|
119
|
+
const normalised = raw.trim().toLowerCase();
|
|
120
|
+
if (normalised === 'experimental' ||
|
|
121
|
+
normalised === 'beta' ||
|
|
122
|
+
normalised === 'stable' ||
|
|
123
|
+
normalised === 'deprecated') {
|
|
124
|
+
return normalised;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
// ==========================================
|
|
129
|
+
// DECLARATION DISCOVERY
|
|
130
|
+
// ==========================================
|
|
131
|
+
/**
|
|
132
|
+
* Locate the component's props declaration. Either:
|
|
133
|
+
* - an `interface FooProps { ... }` → kind: 'interface'
|
|
134
|
+
* - a `type FooProps = A | B | …` → kind: 'union'
|
|
135
|
+
*
|
|
136
|
+
* `node` is the AST node whose members get walked for props; `jsDocHost`
|
|
137
|
+
* is the declaration that owns the leading JSDoc block (for an interface
|
|
138
|
+
* they're the same; for a union the JSDoc lives on the type-alias, not
|
|
139
|
+
* on the `UnionTypeNode` itself). Other type-alias shapes (intersection,
|
|
140
|
+
* single reference, mapped types, etc.) aren't supported here and fall
|
|
141
|
+
* through to `null`.
|
|
142
|
+
*/
|
|
143
|
+
findPropsDeclaration(sourceFile, componentName) {
|
|
144
|
+
const possibleNames = [
|
|
145
|
+
`${componentName}Props`,
|
|
146
|
+
`I${componentName}Props`,
|
|
147
|
+
`${componentName}Properties`
|
|
148
|
+
];
|
|
149
|
+
for (const name of possibleNames) {
|
|
150
|
+
const iface = this.findInterface(sourceFile, name);
|
|
151
|
+
if (iface)
|
|
152
|
+
return { kind: 'interface', node: iface, jsDocHost: iface };
|
|
153
|
+
const alias = this.findTypeAlias(sourceFile, name);
|
|
154
|
+
if (alias && ts.isUnionTypeNode(alias.type)) {
|
|
155
|
+
return { kind: 'union', node: alias.type, jsDocHost: alias };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
// ==========================================
|
|
161
|
+
// PROPS EXTRACTION
|
|
162
|
+
// ==========================================
|
|
163
|
+
/**
|
|
164
|
+
* Extract props from a discriminated-union type alias.
|
|
165
|
+
*
|
|
166
|
+
* Algorithm (generic — no hard-coded discriminator name):
|
|
167
|
+
* 1. Resolve each union member to a local interface. Members that
|
|
168
|
+
* can't be resolved (anonymous type literals, external refs,
|
|
169
|
+
* intersections, etc.) are dropped with a warning.
|
|
170
|
+
* 2. Extract props from each resolved interface (reusing the
|
|
171
|
+
* interface path so heritage / Omit / variants are honoured).
|
|
172
|
+
* 3. Detect the discriminator: a property that exists in every
|
|
173
|
+
* member with a single string-literal type, and whose literal
|
|
174
|
+
* value differs across members.
|
|
175
|
+
* 4. Merge the per-member prop sets:
|
|
176
|
+
* - Props that appear in every member → unconditional.
|
|
177
|
+
* - Props that appear in only some members → `conditionalOn`
|
|
178
|
+
* records the discriminator and the values where they apply.
|
|
179
|
+
* - The discriminator prop itself is collapsed into one entry
|
|
180
|
+
* whose `values` array enumerates every literal it can take.
|
|
181
|
+
*/
|
|
182
|
+
async extractPropsFromUnion(union, sourceFile, aliasName) {
|
|
183
|
+
const memberProps = [];
|
|
184
|
+
for (const memberType of union.types) {
|
|
185
|
+
const resolved = this.resolveUnionMemberInterface(memberType, sourceFile);
|
|
186
|
+
if (!resolved) {
|
|
187
|
+
console.log(`⚠️ Skipping un-resolvable union member: ${memberType.getText().slice(0, 60)}`);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const props = await this.extractPropsFromInterface(resolved.iface, sourceFile);
|
|
191
|
+
memberProps.push({ memberName: resolved.name, props });
|
|
192
|
+
}
|
|
193
|
+
if (memberProps.length === 0) {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
if (memberProps.length === 1) {
|
|
197
|
+
// Degenerate single-member union — treat as plain interface.
|
|
198
|
+
return memberProps[0].props;
|
|
199
|
+
}
|
|
200
|
+
// Build a "prop name → values seen per member" map.
|
|
201
|
+
//
|
|
202
|
+
// A `never` declaration counts as "not present in this variant" —
|
|
203
|
+
// it's the discriminated-union idiom for forbidding a property on
|
|
204
|
+
// a specific arm (`children?: never`). We still keep the
|
|
205
|
+
// occurrence around in case it's the only one we have, but it
|
|
206
|
+
// doesn't gate `conditionalOn` membership.
|
|
207
|
+
const presence = new Map();
|
|
208
|
+
const everPresent = new Map();
|
|
209
|
+
for (const { memberName, props } of memberProps) {
|
|
210
|
+
for (const prop of props) {
|
|
211
|
+
everPresent.set(prop.name, prop);
|
|
212
|
+
if (prop.type === 'never')
|
|
213
|
+
continue;
|
|
214
|
+
if (!presence.has(prop.name))
|
|
215
|
+
presence.set(prop.name, new Map());
|
|
216
|
+
presence.get(prop.name).set(memberName, prop);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Seed any properties that only appeared as `never` declarations
|
|
220
|
+
// (no real-typed occurrence anywhere) — they exist syntactically
|
|
221
|
+
// but should be reported as never-typed.
|
|
222
|
+
for (const [name, prop] of everPresent) {
|
|
223
|
+
if (!presence.has(name)) {
|
|
224
|
+
presence.set(name, new Map([['__never__', prop]]));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const memberCount = memberProps.length;
|
|
228
|
+
// Find the discriminator: the prop that exists in every member with
|
|
229
|
+
// a single distinct string-literal type per member.
|
|
230
|
+
const discriminator = this.detectDiscriminator(presence, memberProps);
|
|
231
|
+
const merged = [];
|
|
232
|
+
for (const [name, byMember] of presence) {
|
|
233
|
+
const occurrences = Array.from(byMember.values());
|
|
234
|
+
// Membership counts only real members (skip the `__never__`
|
|
235
|
+
// sentinel that holds purely-never declarations).
|
|
236
|
+
const realMemberKeys = Array.from(byMember.keys()).filter((k) => k !== '__never__');
|
|
237
|
+
const isUnconditional = realMemberKeys.length === memberCount;
|
|
238
|
+
if (discriminator && name === discriminator.propName) {
|
|
239
|
+
// Collapse the discriminator into one entry whose `values`
|
|
240
|
+
// enumerates every literal it can take, with `type` rebuilt
|
|
241
|
+
// as the union of all member literals.
|
|
242
|
+
const allValues = new Set();
|
|
243
|
+
for (const occ of occurrences) {
|
|
244
|
+
for (const v of occ.values ?? [])
|
|
245
|
+
allValues.add(v);
|
|
246
|
+
}
|
|
247
|
+
const base = { ...this.pickInformativeOccurrence(occurrences) };
|
|
248
|
+
base.values = Array.from(allValues);
|
|
249
|
+
base.type = base.values.map((v) => `'${v}'`).join(' | ');
|
|
250
|
+
// The discriminator is effectively required only when every
|
|
251
|
+
// member declares it as required. If any arm makes it optional
|
|
252
|
+
// (typically the one with a default literal), consumers can
|
|
253
|
+
// omit it and the type narrows to that arm — so from the
|
|
254
|
+
// public API view it's optional.
|
|
255
|
+
base.required = occurrences.every((occ) => occ.required);
|
|
256
|
+
// Re-source onto the outer alias — the discriminator's sub-
|
|
257
|
+
// interface name is implementation detail (see the same
|
|
258
|
+
// re-source below for non-discriminator props).
|
|
259
|
+
base.source = { type: 'direct', name: aliasName };
|
|
260
|
+
// No conditionalOn — the discriminator is the property that's
|
|
261
|
+
// always present.
|
|
262
|
+
delete base.conditionalOn;
|
|
263
|
+
merged.push(base);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
// When a prop is declared as `never` in one member and a real
|
|
267
|
+
// type in another (the `BadgeDotProps.interactive?: never` /
|
|
268
|
+
// `BadgeStandardProps.interactive?: boolean` pattern), prefer
|
|
269
|
+
// the real declaration — it's the one consumers actually use.
|
|
270
|
+
const head = { ...this.pickInformativeOccurrence(occurrences) };
|
|
271
|
+
// From the consumer's point of view every prop on the union is
|
|
272
|
+
// direct on the outer alias — the per-member sub-interfaces are
|
|
273
|
+
// implementation detail. Re-source accordingly so the API surface
|
|
274
|
+
// doesn't leak `BadgeBaseProps` / `BadgeStandardProps` as if the
|
|
275
|
+
// consumer had to know them.
|
|
276
|
+
head.source = { type: 'direct', name: aliasName };
|
|
277
|
+
if (!isUnconditional && discriminator) {
|
|
278
|
+
const applicableValues = [];
|
|
279
|
+
for (const memberName of realMemberKeys) {
|
|
280
|
+
const values = discriminator.valuesByMember.get(memberName);
|
|
281
|
+
if (values)
|
|
282
|
+
applicableValues.push(...values);
|
|
283
|
+
}
|
|
284
|
+
head.conditionalOn = {
|
|
285
|
+
propName: discriminator.propName,
|
|
286
|
+
values: Array.from(new Set(applicableValues))
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
merged.push(head);
|
|
290
|
+
}
|
|
291
|
+
return this.sortProps(merged);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Pick the most informative occurrence of a prop across union members.
|
|
295
|
+
* Prefers occurrences whose type isn't `never`, whose description isn't
|
|
296
|
+
* an auto-generated placeholder (`'<name> property'`), and which carry
|
|
297
|
+
* a `defaultValue` over those that don't. This keeps
|
|
298
|
+
* `<Badge variant="dot" removable>` extracting as `removable: boolean`
|
|
299
|
+
* (from `BadgeStandardProps`) rather than `removable?: never` (from
|
|
300
|
+
* `BadgeDotProps`) — and, when collapsing the discriminator, picks the
|
|
301
|
+
* arm whose `@default` JSDoc is set so the docs site renders a real
|
|
302
|
+
* default value in the API table.
|
|
303
|
+
*/
|
|
304
|
+
pickInformativeOccurrence(occurrences) {
|
|
305
|
+
if (occurrences.length === 0) {
|
|
306
|
+
throw new Error('pickInformativeOccurrence called with no occurrences');
|
|
307
|
+
}
|
|
308
|
+
const ranked = [...occurrences].sort((a, b) => {
|
|
309
|
+
const aPlaceholder = a.type === 'never' ? 2 : 0;
|
|
310
|
+
const bPlaceholder = b.type === 'never' ? 2 : 0;
|
|
311
|
+
const aDoc = a.description.endsWith(' property') ? 1 : 0;
|
|
312
|
+
const bDoc = b.description.endsWith(' property') ? 1 : 0;
|
|
313
|
+
const aNoDefault = a.defaultValue === undefined ? 0.5 : 0;
|
|
314
|
+
const bNoDefault = b.defaultValue === undefined ? 0.5 : 0;
|
|
315
|
+
return aPlaceholder + aDoc + aNoDefault - (bPlaceholder + bDoc + bNoDefault);
|
|
316
|
+
});
|
|
317
|
+
return ranked[0];
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Resolve a union-member type to a local interface declaration. Currently
|
|
321
|
+
* supports plain TypeReferenceNode shapes (`A`, `Foo<T>`). Returns null for
|
|
322
|
+
* anonymous type literals, intersections, external references the file
|
|
323
|
+
* doesn't import directly, etc.
|
|
324
|
+
*/
|
|
325
|
+
resolveUnionMemberInterface(memberType, sourceFile) {
|
|
326
|
+
if (!ts.isTypeReferenceNode(memberType))
|
|
327
|
+
return null;
|
|
328
|
+
const name = memberType.typeName.getText();
|
|
329
|
+
const iface = this.findInterface(sourceFile, name);
|
|
330
|
+
if (!iface)
|
|
331
|
+
return null;
|
|
332
|
+
return { iface, name };
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Detect the discriminator property. A discriminator must:
|
|
336
|
+
* - exist in every union member,
|
|
337
|
+
* - have a string-literal-union type in each member (one or more
|
|
338
|
+
* `'x' | 'y'` values),
|
|
339
|
+
* - have non-overlapping value-sets across members (so the literal
|
|
340
|
+
* uniquely identifies which member a value belongs to).
|
|
341
|
+
*
|
|
342
|
+
* Returns null if no such property exists.
|
|
343
|
+
*/
|
|
344
|
+
detectDiscriminator(presence, memberProps) {
|
|
345
|
+
const memberCount = memberProps.length;
|
|
346
|
+
for (const [propName, byMember] of presence) {
|
|
347
|
+
if (byMember.size !== memberCount)
|
|
348
|
+
continue;
|
|
349
|
+
const valuesByMember = new Map();
|
|
350
|
+
let valid = true;
|
|
351
|
+
for (const { memberName } of memberProps) {
|
|
352
|
+
const prop = byMember.get(memberName);
|
|
353
|
+
if (!prop?.values || prop.values.length === 0) {
|
|
354
|
+
valid = false;
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
valuesByMember.set(memberName, prop.values);
|
|
358
|
+
}
|
|
359
|
+
if (!valid)
|
|
360
|
+
continue;
|
|
361
|
+
// Reject the property if any value appears in more than one member.
|
|
362
|
+
const seen = new Set();
|
|
363
|
+
let overlap = false;
|
|
364
|
+
for (const values of valuesByMember.values()) {
|
|
365
|
+
for (const v of values) {
|
|
366
|
+
if (seen.has(v)) {
|
|
367
|
+
overlap = true;
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
seen.add(v);
|
|
371
|
+
}
|
|
372
|
+
if (overlap)
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
if (overlap)
|
|
376
|
+
continue;
|
|
377
|
+
return { propName, valuesByMember };
|
|
378
|
+
}
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
async extractPropsFromInterface(propsInterface, sourceFile) {
|
|
382
|
+
const props = [];
|
|
383
|
+
// Extract direct properties from interface
|
|
384
|
+
for (const member of propsInterface.members) {
|
|
385
|
+
if (ts.isPropertySignature(member)) {
|
|
386
|
+
const prop = this.extractPropFromMember(member);
|
|
387
|
+
if (prop) {
|
|
388
|
+
// Mark as direct prop
|
|
389
|
+
prop.source = {
|
|
390
|
+
type: 'direct',
|
|
391
|
+
name: propsInterface.name.text
|
|
392
|
+
};
|
|
393
|
+
props.push(prop);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
console.log(`📝 Extracted ${props.length} direct props`);
|
|
398
|
+
// Process heritage clauses (extends/implements)
|
|
399
|
+
if (propsInterface.heritageClauses) {
|
|
400
|
+
console.log(`🔍 Processing ${propsInterface.heritageClauses.length} heritage clauses...`);
|
|
401
|
+
for (const heritageClause of propsInterface.heritageClauses) {
|
|
402
|
+
for (const heritageType of heritageClause.types) {
|
|
403
|
+
const inheritedProps = await this.extractInheritedProps(heritageType, sourceFile);
|
|
404
|
+
props.push(...inheritedProps);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// Sort props by category and name
|
|
409
|
+
return this.sortProps(props);
|
|
410
|
+
}
|
|
411
|
+
extractPropFromMember(member) {
|
|
412
|
+
const propName = this.getPropertyName(member);
|
|
413
|
+
if (!propName)
|
|
414
|
+
return null;
|
|
415
|
+
// Skip private props if configured
|
|
416
|
+
if (!this.config.includePrivateProps && propName.startsWith('_')) {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
const propType = this.getTypeString(member.type);
|
|
420
|
+
const isRequired = this.isPropertyRequired(member);
|
|
421
|
+
// Extract JSDoc information
|
|
422
|
+
let description = `${propName} property`;
|
|
423
|
+
let defaultValue;
|
|
424
|
+
let examples = [];
|
|
425
|
+
let deprecated;
|
|
426
|
+
let seeAlso;
|
|
427
|
+
let experimental = false;
|
|
428
|
+
let since;
|
|
429
|
+
if (this.config.extractJSDoc) {
|
|
430
|
+
const jsDocComment = this.extractJSDocComment(member);
|
|
431
|
+
if (jsDocComment) {
|
|
432
|
+
description = jsDocComment;
|
|
433
|
+
}
|
|
434
|
+
// Extract JSDoc tags
|
|
435
|
+
defaultValue = this.extractJSDocTag(member, 'default');
|
|
436
|
+
since = this.extractJSDocTag(member, 'since');
|
|
437
|
+
// NEW: Extract @see references
|
|
438
|
+
const seeTag = this.extractJSDocTag(member, 'see');
|
|
439
|
+
if (seeTag) {
|
|
440
|
+
seeAlso = seeTag;
|
|
441
|
+
}
|
|
442
|
+
const deprecatedInfo = this.extractJSDocTag(member, 'deprecated');
|
|
443
|
+
if (deprecatedInfo) {
|
|
444
|
+
deprecated = {
|
|
445
|
+
message: deprecatedInfo,
|
|
446
|
+
since: since || ''
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
const experimentalInfo = this.extractJSDocTag(member, 'experimental');
|
|
450
|
+
if (experimentalInfo) {
|
|
451
|
+
experimental = true;
|
|
452
|
+
}
|
|
453
|
+
// Extract examples from JSDoc
|
|
454
|
+
examples = this.extractJSDocExamples(member);
|
|
455
|
+
}
|
|
456
|
+
// Extract union values for enum-like types
|
|
457
|
+
const values = member.type ? this.extractUnionValues(member.type) : undefined;
|
|
458
|
+
const prop = {
|
|
459
|
+
name: propName,
|
|
460
|
+
type: propType,
|
|
461
|
+
required: isRequired,
|
|
462
|
+
description,
|
|
463
|
+
source: { type: 'direct' } // Will be updated by caller
|
|
464
|
+
};
|
|
465
|
+
// Add optional fields
|
|
466
|
+
if (defaultValue)
|
|
467
|
+
prop.defaultValue = defaultValue;
|
|
468
|
+
if (values && values.length > 0)
|
|
469
|
+
prop.values = values;
|
|
470
|
+
if (examples && examples.length > 0)
|
|
471
|
+
prop.examples = examples;
|
|
472
|
+
if (deprecated)
|
|
473
|
+
prop.deprecated = deprecated;
|
|
474
|
+
if (experimental)
|
|
475
|
+
prop.experimental = experimental;
|
|
476
|
+
if (since)
|
|
477
|
+
prop.since = since;
|
|
478
|
+
if (seeAlso)
|
|
479
|
+
prop.seeAlso = seeAlso;
|
|
480
|
+
return prop;
|
|
481
|
+
}
|
|
482
|
+
// ==========================================
|
|
483
|
+
// INHERITANCE HANDLING
|
|
484
|
+
// ==========================================
|
|
485
|
+
async extractInheritedProps(heritageType, sourceFile) {
|
|
486
|
+
const typeName = heritageType.expression.getText();
|
|
487
|
+
console.log(`🔍 Processing inheritance from: ${typeName}`);
|
|
488
|
+
// Handle different inheritance patterns
|
|
489
|
+
if (this.isVariantInterface(typeName)) {
|
|
490
|
+
return this.handleVariantInheritance(typeName);
|
|
491
|
+
}
|
|
492
|
+
if (this.isOmitPattern(heritageType)) {
|
|
493
|
+
return this.handleOmitPattern(heritageType, sourceFile);
|
|
494
|
+
}
|
|
495
|
+
if (this.isHTMLAttributes(typeName)) {
|
|
496
|
+
return this.handleHTMLAttributes(typeName);
|
|
497
|
+
}
|
|
498
|
+
// Try to find local interface
|
|
499
|
+
const localInterface = this.findInterface(sourceFile, typeName);
|
|
500
|
+
if (localInterface) {
|
|
501
|
+
return this.extractPropsFromLocalInterface(localInterface, typeName);
|
|
502
|
+
}
|
|
503
|
+
// Create placeholder for unknown inheritance
|
|
504
|
+
console.log(`❓ Creating placeholder for unknown inheritance: ${typeName}`);
|
|
505
|
+
return [
|
|
506
|
+
{
|
|
507
|
+
name: `...${typeName}`,
|
|
508
|
+
type: 'inherited',
|
|
509
|
+
required: false,
|
|
510
|
+
description: `Properties inherited from ${typeName}`,
|
|
511
|
+
source: {
|
|
512
|
+
type: 'inherited',
|
|
513
|
+
name: typeName,
|
|
514
|
+
package: this.getPackageForType(typeName) || ''
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
];
|
|
518
|
+
}
|
|
519
|
+
handleVariantInheritance(typeName) {
|
|
520
|
+
// This will be handled by VariantsExtractor in the coordination phase
|
|
521
|
+
console.log(`🎨 Variant inheritance detected: ${typeName} - will be processed by VariantsExtractor`);
|
|
522
|
+
return [
|
|
523
|
+
{
|
|
524
|
+
name: `...${typeName}`,
|
|
525
|
+
type: 'VariantProps',
|
|
526
|
+
required: false,
|
|
527
|
+
description: `Styling variants from ${typeName}`,
|
|
528
|
+
source: {
|
|
529
|
+
type: 'variant',
|
|
530
|
+
name: typeName
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
];
|
|
534
|
+
}
|
|
535
|
+
handleOmitPattern(heritageType, sourceFile) {
|
|
536
|
+
const fullType = heritageType.getText();
|
|
537
|
+
// Omit pattern detected; filter extracted props based on omitted keys
|
|
538
|
+
// Extract base type and omitted keys
|
|
539
|
+
const match = fullType.match(/Omit<([^,]+),\s*([^>]+)>/);
|
|
540
|
+
if (match?.[1] && match[2]) {
|
|
541
|
+
const baseType = match[1].trim();
|
|
542
|
+
const omittedKeys = match[2].trim();
|
|
543
|
+
if (this.isHTMLAttributes(baseType)) {
|
|
544
|
+
return [
|
|
545
|
+
{
|
|
546
|
+
name: `...${baseType}`,
|
|
547
|
+
type: 'HTMLAttributes',
|
|
548
|
+
required: false,
|
|
549
|
+
description: `HTML attributes${omittedKeys ? ` (excluding: ${omittedKeys})` : ''}`,
|
|
550
|
+
source: {
|
|
551
|
+
type: 'inherited',
|
|
552
|
+
name: fullType,
|
|
553
|
+
package: 'svelte/elements'
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
];
|
|
557
|
+
}
|
|
558
|
+
// *Variants type alias — `findInterface` returns null because
|
|
559
|
+
// *Variants is a `type` alias over `VariantProps<typeof xVariants>`,
|
|
560
|
+
// not an interface. Emit the variant-inheritance placeholder AND
|
|
561
|
+
// omit-markers for each literal-string key in the omitted list.
|
|
562
|
+
// APIDataGenerator filters those markers (and the named keys) out
|
|
563
|
+
// of the final variant-props list so they don't leak into the
|
|
564
|
+
// public API table.
|
|
565
|
+
if (this.isVariantInterface(baseType)) {
|
|
566
|
+
const result = this.handleVariantInheritance(baseType);
|
|
567
|
+
const literalKeys = this.extractOmittedLiteralKeys(omittedKeys);
|
|
568
|
+
for (const key of literalKeys) {
|
|
569
|
+
result.push({
|
|
570
|
+
name: `__OMIT_VARIANT__${key}`,
|
|
571
|
+
type: 'omit-marker',
|
|
572
|
+
required: false,
|
|
573
|
+
description: `Suppress variant prop "${key}" from the public API surface (declared via Omit<${baseType}, ...>).`,
|
|
574
|
+
source: { type: 'inherited', name: fullType }
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
return result;
|
|
578
|
+
}
|
|
579
|
+
// Local interface Omit pattern, e.g., Omit<MenuSpecificProps, keyof MenuVariants>
|
|
580
|
+
const localInterface = this.findInterface(sourceFile, baseType);
|
|
581
|
+
if (localInterface) {
|
|
582
|
+
// Extract props from the local interface
|
|
583
|
+
const extracted = this.extractPropsFromLocalInterface(localInterface, baseType);
|
|
584
|
+
// If omittedKeys references keyof Variants, exclude variant keys provided by VariantsExtractor
|
|
585
|
+
const shouldExcludeVariantKeys = /keyof\s+\w*Variants/.test(omittedKeys);
|
|
586
|
+
const variantKeyBlacklist = shouldExcludeVariantKeys
|
|
587
|
+
? new Set(this.currentVariantKeys)
|
|
588
|
+
: new Set();
|
|
589
|
+
return extracted
|
|
590
|
+
.filter((p) => !variantKeyBlacklist.has(p.name))
|
|
591
|
+
.map((p) => ({
|
|
592
|
+
...p,
|
|
593
|
+
source: {
|
|
594
|
+
type: 'inherited',
|
|
595
|
+
name: fullType
|
|
596
|
+
}
|
|
597
|
+
}));
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return [];
|
|
601
|
+
}
|
|
602
|
+
handleHTMLAttributes(typeName) {
|
|
603
|
+
const elementType = this.extractElementType(typeName);
|
|
604
|
+
return [
|
|
605
|
+
{
|
|
606
|
+
name: `...${elementType}Attributes`,
|
|
607
|
+
type: 'HTMLAttributes',
|
|
608
|
+
required: false,
|
|
609
|
+
description: `All standard HTML ${elementType.toLowerCase()} attributes`,
|
|
610
|
+
source: {
|
|
611
|
+
type: 'inherited',
|
|
612
|
+
name: typeName,
|
|
613
|
+
package: 'svelte/elements',
|
|
614
|
+
url: `https://developer.mozilla.org/en-US/docs/Web/HTML/Element/${elementType.toLowerCase()}`
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
];
|
|
618
|
+
}
|
|
619
|
+
extractPropsFromLocalInterface(localInterface, interfaceName) {
|
|
620
|
+
const props = [];
|
|
621
|
+
for (const member of localInterface.members) {
|
|
622
|
+
if (ts.isPropertySignature(member)) {
|
|
623
|
+
const prop = this.extractPropFromMember(member);
|
|
624
|
+
if (prop) {
|
|
625
|
+
prop.source = {
|
|
626
|
+
type: 'inherited',
|
|
627
|
+
name: interfaceName
|
|
628
|
+
};
|
|
629
|
+
props.push(prop);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
// extracted local interface props
|
|
634
|
+
return props;
|
|
635
|
+
}
|
|
636
|
+
// ==========================================
|
|
637
|
+
// UTILITY METHODS
|
|
638
|
+
// ==========================================
|
|
639
|
+
isVariantInterface(typeName) {
|
|
640
|
+
return typeName.endsWith('Variants') || typeName.endsWith('VariantProps');
|
|
641
|
+
}
|
|
642
|
+
isOmitPattern(heritageType) {
|
|
643
|
+
return heritageType.expression.getText().startsWith('Omit');
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Parse the keys argument of `Omit<X, K>` into a flat string-literal list.
|
|
647
|
+
* Handles `'a'`, `'a' | 'b' | 'c'`, double-quoted, and ignores `keyof T`
|
|
648
|
+
* patterns (those are handled separately via `currentVariantKeys`).
|
|
649
|
+
*/
|
|
650
|
+
extractOmittedLiteralKeys(omittedKeys) {
|
|
651
|
+
return Array.from(omittedKeys.matchAll(/['"]([A-Za-z_$][A-Za-z0-9_$]*)['"]/g))
|
|
652
|
+
.map((m) => m[1])
|
|
653
|
+
.filter((k) => Boolean(k));
|
|
654
|
+
}
|
|
655
|
+
isHTMLAttributes(typeName) {
|
|
656
|
+
return typeName.includes('HTML') && typeName.includes('Attributes');
|
|
657
|
+
}
|
|
658
|
+
extractElementType(typeName) {
|
|
659
|
+
const match = typeName.match(/HTML(\w+)Attributes/);
|
|
660
|
+
return match?.[1] ? match[1] : 'Element';
|
|
661
|
+
}
|
|
662
|
+
getPackageForType(typeName) {
|
|
663
|
+
const packageMap = {
|
|
664
|
+
Snippet: 'svelte',
|
|
665
|
+
ComponentEvents: 'svelte',
|
|
666
|
+
MintProp: '@urbicon-ui/blocks',
|
|
667
|
+
ComponentIntent: '@urbicon-ui/blocks',
|
|
668
|
+
ComponentSize: '@urbicon-ui/blocks'
|
|
669
|
+
};
|
|
670
|
+
return packageMap[typeName];
|
|
671
|
+
}
|
|
672
|
+
extractJSDocExamples(member) {
|
|
673
|
+
const examples = [];
|
|
674
|
+
// Extract @example tags
|
|
675
|
+
const jsDocTags = ts.getJSDocTags(member);
|
|
676
|
+
let exampleCount = 1;
|
|
677
|
+
for (const tag of jsDocTags) {
|
|
678
|
+
if (tag.tagName.text === 'example' && tag.comment) {
|
|
679
|
+
const exampleText = this.getCommentText(tag.comment);
|
|
680
|
+
examples.push({
|
|
681
|
+
title: `Example ${exampleCount}`,
|
|
682
|
+
code: exampleText,
|
|
683
|
+
description: `Usage example for ${this.getPropertyName(member)}`
|
|
684
|
+
});
|
|
685
|
+
exampleCount++;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return examples;
|
|
689
|
+
}
|
|
690
|
+
sortProps(props) {
|
|
691
|
+
return props.sort((a, b) => {
|
|
692
|
+
// Within same category, sort required props first
|
|
693
|
+
if (a.required && !b.required)
|
|
694
|
+
return -1;
|
|
695
|
+
if (!a.required && b.required)
|
|
696
|
+
return 1;
|
|
697
|
+
// Finally, sort alphabetically
|
|
698
|
+
return a.name.localeCompare(b.name);
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
// ✅ Removed duplicate getCommentText method - now inherited from TypeScriptBaseExtractor
|
|
702
|
+
/**
|
|
703
|
+
* Clear any caches
|
|
704
|
+
*/
|
|
705
|
+
clearCache() {
|
|
706
|
+
// No persistent cache in this implementation
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
//# sourceMappingURL=PropsExtractor.js.map
|