mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(site): add util reference pipeline (#537)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
c11395ece1
commit
78112fbefd
@@ -4,20 +4,22 @@ import * as ts from 'typescript';
|
||||
import * as tae from 'typescript-api-extractor';
|
||||
import { extractCore } from './core-handler.js';
|
||||
import { extractDataAttrs } from './data-attrs-handler.js';
|
||||
import { abbreviateType } from './formatter.js';
|
||||
import { extractHtml } from './html-handler.js';
|
||||
import { extractPartDescription, extractParts } from './parts-handler.js';
|
||||
import {
|
||||
type ComponentApiReference,
|
||||
ComponentApiReferenceSchema,
|
||||
type ComponentReference,
|
||||
ComponentReferenceSchema,
|
||||
type ComponentSource,
|
||||
type CoreExtraction,
|
||||
type DataAttrDef,
|
||||
type DataAttrsExtraction,
|
||||
type PartApiReference,
|
||||
type PartReference,
|
||||
type PartSource,
|
||||
type PropDef,
|
||||
type StateDef,
|
||||
} from './types.js';
|
||||
import { generateUtilReferences } from './util-handler.js';
|
||||
import { kebabToPascal, partKebabFromSource, sortProps } from './utils.js';
|
||||
|
||||
// Components whose PascalCase name doesn't match simple kebab-to-pascal conversion.
|
||||
@@ -30,13 +32,13 @@ function buildProps(coreData: CoreExtraction): Record<string, PropDef> {
|
||||
for (const prop of coreData.props) {
|
||||
props[prop.name] = {
|
||||
type: prop.type,
|
||||
shortType: prop.shortType,
|
||||
detailedType: prop.detailedType,
|
||||
description: prop.description,
|
||||
default: coreData.defaultProps[prop.name] ?? prop.default,
|
||||
required: prop.required,
|
||||
};
|
||||
|
||||
if (props[prop.name]!.shortType === undefined) delete props[prop.name]!.shortType;
|
||||
if (props[prop.name]!.detailedType === undefined) delete props[prop.name]!.detailedType;
|
||||
if (props[prop.name]!.description === undefined) delete props[prop.name]!.description;
|
||||
if (props[prop.name]!.default === undefined) delete props[prop.name]!.default;
|
||||
if (!props[prop.name]!.required) delete props[prop.name]!.required;
|
||||
@@ -49,10 +51,10 @@ function buildState(coreData: CoreExtraction): Record<string, StateDef> {
|
||||
for (const s of coreData.state) {
|
||||
state[s.name] = {
|
||||
type: s.type,
|
||||
shortType: s.shortType,
|
||||
detailedType: s.detailedType,
|
||||
description: s.description,
|
||||
};
|
||||
if (state[s.name]!.shortType === undefined) delete state[s.name]!.shortType;
|
||||
if (state[s.name]!.detailedType === undefined) delete state[s.name]!.detailedType;
|
||||
if (state[s.name]!.description === undefined) delete state[s.name]!.description;
|
||||
}
|
||||
return state;
|
||||
@@ -61,7 +63,17 @@ function buildState(coreData: CoreExtraction): Record<string, StateDef> {
|
||||
function buildDataAttrs(dataAttrsData: DataAttrsExtraction): Record<string, DataAttrDef> {
|
||||
const dataAttributes: Record<string, DataAttrDef> = {};
|
||||
for (const attr of dataAttrsData.attrs) {
|
||||
dataAttributes[attr.name] = { description: attr.description };
|
||||
const def: DataAttrDef = { description: attr.description };
|
||||
if (attr.type) {
|
||||
const abbreviated = abbreviateType(attr.name, attr.type);
|
||||
if (abbreviated) {
|
||||
def.type = abbreviated;
|
||||
def.detailedType = attr.type;
|
||||
} else {
|
||||
def.type = attr.type;
|
||||
}
|
||||
}
|
||||
dataAttributes[attr.name] = def;
|
||||
}
|
||||
return dataAttributes;
|
||||
}
|
||||
@@ -81,7 +93,8 @@ const MONOREPO_ROOT = path.resolve(import.meta.dirname, '../../../../');
|
||||
const CORE_UI_PATH = path.join(MONOREPO_ROOT, 'packages/core/src/core/ui');
|
||||
const HTML_UI_PATH = path.join(MONOREPO_ROOT, 'packages/html/src/ui');
|
||||
const REACT_UI_PATH = path.join(MONOREPO_ROOT, 'packages/react/src/ui');
|
||||
const OUTPUT_PATH = path.join(MONOREPO_ROOT, 'site/src/content/generated-api-reference');
|
||||
const COMPONENT_OUTPUT_PATH = path.join(MONOREPO_ROOT, 'site/src/content/generated-component-reference');
|
||||
const UTIL_OUTPUT_PATH = path.join(MONOREPO_ROOT, 'site/src/content/generated-util-reference');
|
||||
|
||||
/**
|
||||
* Discover all components by scanning the core/ui directory.
|
||||
@@ -191,7 +204,7 @@ function createProgram(sources: ComponentSource[]): ts.Program {
|
||||
/**
|
||||
* Build the API reference for a single-part component.
|
||||
*/
|
||||
function buildSingleComponentApiReference(source: ComponentSource, program: ts.Program): ComponentApiReference | null {
|
||||
function buildSingleComponentReference(source: ComponentSource, program: ts.Program): ComponentReference | null {
|
||||
// Extract from core
|
||||
const coreData = source.corePath ? extractCore(source.corePath, program, source.name) : null;
|
||||
|
||||
@@ -207,7 +220,7 @@ function buildSingleComponentApiReference(source: ComponentSource, program: ts.P
|
||||
const htmlData = source.htmlPath ? extractHtml(source.htmlPath, program, source.name) : null;
|
||||
|
||||
// Build result
|
||||
const result: ComponentApiReference = {
|
||||
const result: ComponentReference = {
|
||||
name: source.name,
|
||||
description: coreData.description,
|
||||
props: buildProps(coreData),
|
||||
@@ -312,12 +325,12 @@ function discoverParts(source: ComponentSource, program: ts.Program): PartSource
|
||||
* - Props, state, and data attributes are empty (no dedicated core file)
|
||||
* - HTML tag comes from their sub-part element file (`{name}-{part}-element.ts`)
|
||||
*/
|
||||
function buildMultiPartApiReference(
|
||||
function buildMultiPartReference(
|
||||
source: ComponentSource,
|
||||
program: ts.Program,
|
||||
parts: PartSource[]
|
||||
): ComponentApiReference | null {
|
||||
const partsRecord: Record<string, PartApiReference> = {};
|
||||
): ComponentReference | null {
|
||||
const partsRecord: Record<string, PartReference> = {};
|
||||
|
||||
for (const part of parts) {
|
||||
// Extract JSDoc description from React component file
|
||||
@@ -334,7 +347,7 @@ function buildMultiPartApiReference(
|
||||
const elementName = `${source.name}Element`;
|
||||
const htmlData = part.htmlPath ? extractHtml(part.htmlPath, program, source.name, elementName) : null;
|
||||
|
||||
const partRef: PartApiReference = {
|
||||
const partRef: PartReference = {
|
||||
name: part.name,
|
||||
description,
|
||||
props: coreData ? sortProps(buildProps(coreData)) : {},
|
||||
@@ -354,7 +367,7 @@ function buildMultiPartApiReference(
|
||||
const elementName = `${source.name}${part.name}Element`;
|
||||
const htmlData = part.htmlPath ? extractHtml(part.htmlPath, program, source.name, elementName) : null;
|
||||
|
||||
const partRef: PartApiReference = {
|
||||
const partRef: PartReference = {
|
||||
name: part.name,
|
||||
description,
|
||||
props: {},
|
||||
@@ -385,15 +398,15 @@ function buildMultiPartApiReference(
|
||||
/**
|
||||
* Build the API reference for a single component.
|
||||
*/
|
||||
function buildComponentApiReference(source: ComponentSource, program: ts.Program): ComponentApiReference | null {
|
||||
function buildComponentReference(source: ComponentSource, program: ts.Program): ComponentReference | null {
|
||||
if (source.partsIndexPath) {
|
||||
const parts = discoverParts(source, program);
|
||||
if (parts.length > 0) {
|
||||
return buildMultiPartApiReference(source, program, parts);
|
||||
return buildMultiPartReference(source, program, parts);
|
||||
}
|
||||
}
|
||||
|
||||
return buildSingleComponentApiReference(source, program);
|
||||
return buildSingleComponentReference(source, program);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,8 +425,8 @@ function main() {
|
||||
};
|
||||
|
||||
// Ensure output directory exists
|
||||
if (!fs.existsSync(OUTPUT_PATH)) {
|
||||
fs.mkdirSync(OUTPUT_PATH, { recursive: true });
|
||||
if (!fs.existsSync(COMPONENT_OUTPUT_PATH)) {
|
||||
fs.mkdirSync(COMPONENT_OUTPUT_PATH, { recursive: true });
|
||||
}
|
||||
|
||||
// Discover components
|
||||
@@ -434,14 +447,14 @@ function main() {
|
||||
|
||||
for (const source of components) {
|
||||
try {
|
||||
const apiRef = buildComponentApiReference(source, program);
|
||||
const apiRef = buildComponentReference(source, program);
|
||||
|
||||
if (apiRef) {
|
||||
// Sort props (top-level only for single-part)
|
||||
apiRef.props = sortProps(apiRef.props);
|
||||
|
||||
// Validate against schema before writing
|
||||
const validated = ComponentApiReferenceSchema.safeParse(apiRef);
|
||||
const validated = ComponentReferenceSchema.safeParse(apiRef);
|
||||
if (!validated.success) {
|
||||
log.error(`Schema validation failed for ${source.name}:`);
|
||||
for (const issue of validated.error.issues) {
|
||||
@@ -452,7 +465,7 @@ function main() {
|
||||
}
|
||||
|
||||
// Write JSON file
|
||||
const outputFile = path.join(OUTPUT_PATH, `${source.kebab}.json`);
|
||||
const outputFile = path.join(COMPONENT_OUTPUT_PATH, `${source.kebab}.json`);
|
||||
const json = `${JSON.stringify(validated.data, null, 2)}\n`;
|
||||
fs.writeFileSync(outputFile, json);
|
||||
|
||||
@@ -465,7 +478,14 @@ function main() {
|
||||
}
|
||||
}
|
||||
|
||||
log.info(`Done! Generated ${successCount} files.`);
|
||||
log.info(`Done! Generated ${successCount} component files.`);
|
||||
|
||||
// Generate util references
|
||||
const utilResult = generateUtilReferences(UTIL_OUTPUT_PATH, MONOREPO_ROOT);
|
||||
successCount += utilResult.success;
|
||||
errorCount += utilResult.errors;
|
||||
|
||||
log.info(`Done! Generated ${utilResult.success} util files.`);
|
||||
|
||||
console.warn = originalWarn;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user