@zenithcore/core
Version:
Core functionality for ZenithKernel framework
641 lines (561 loc) • 19 kB
text/typescript
/**
* ZenithKernel Advanced JSX Type Generator
*
* Extends Archipelago's JSX type generator with ZenithKernel-specific features:
* - Auto-generates TypeScript definitions for ZenithKernel islands
* - Supports Archipelago-style .zenith.tsx syntax with <Template> wrapper
* - Supports ZK, ECS, and hydration directives and attributes
* - Generates both PascalCase and kebab-case component variants
* - Includes comprehensive documentation and IntelliSense support
* - Watch mode for development workflow
*/
import ts from "typescript";
import fs from "fs";
import path from "path";
import chokidar from "chokidar";
interface ZenithGeneratorConfig {
islandsDir: string;
outputDir: string;
outputFile: string;
watchMode: boolean;
debugMode: boolean;
}
interface IslandInfo {
name: string;
propsInterface?: string;
metadata?: any;
dependencies?: string[];
trustLevel?: string;
execType?: string;
ecsComponents?: string[];
filePath?: string;
}
interface PropInfo {
name: string;
type: string;
optional: boolean;
documentation?: string;
}
class ZenithJSXGenerator {
private config: ZenithGeneratorConfig;
private discoveredIslands = new Map<string, IslandInfo>();
private islandProps = new Map<string, PropInfo[]>();
private templateDirectives = new Set<string>();
constructor(config: ZenithGeneratorConfig) {
this.config = config;
}
/**
* Main entry point for type generation
*/
public async generate(): Promise<void> {
try {
console.log('🌊 ZenithKernel JSX Type Generator');
console.log(`📁 Scanning: ${this.config.islandsDir}`);
// Clear previous data
this.discoveredIslands.clear();
this.islandProps.clear();
this.templateDirectives.clear();
// Scan for islands
await this.scanIslands();
// Generate type definitions
const typeDefinitions = this.generateZenithTypeDefinitions();
// Write output file
await this.writeTypeDefinitions(typeDefinitions);
console.log(`✅ Generated JSX types → ${this.config.outputFile}`);
console.log(`📊 Found ${this.discoveredIslands.size} islands with ${this.templateDirectives.size} unique directives`);
} catch (error) {
console.error('❌ Error generating JSX types:', error);
throw error;
}
}
/**
* Enable watch mode for development
*/
public async startWatchMode(): Promise<void> {
console.log('👀 Watching for changes in .zenith.tsx files...');
const watcher = chokidar.watch(
path.join(this.config.islandsDir, '**/*.zenith.tsx'),
{ ignored: /node_modules/, persistent: true }
);
watcher
.on('change', (filePath) => {
console.log(`🔄 File changed: ${path.relative(this.config.islandsDir, filePath)}`);
this.generate();
})
.on('add', (filePath) => {
console.log(`➕ File added: ${path.relative(this.config.islandsDir, filePath)}`);
this.generate();
})
.on('unlink', (filePath) => {
console.log(`➖ File removed: ${path.relative(this.config.islandsDir, filePath)}`);
this.generate();
});
// Initial generation
await this.generate();
}
/**
* Scan islands directory for .zenith.tsx files
*/
private async scanIslands(): Promise<void> {
await this.scanDirectory(this.config.islandsDir);
}
/**
* Recursively scan directory for island files
*/
private async scanDirectory(dir: string): Promise<void> {
if (!fs.existsSync(dir)) {
console.warn(`⚠️ Islands directory not found: ${dir}`);
return;
}
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
await this.scanDirectory(fullPath);
} else if (entry.name.endsWith('.zenith.tsx')) {
await this.parseIslandFile(fullPath);
}
}
}
/**
* Parse individual island file
*/
private async parseIslandFile(filePath: string): Promise<void> {
try {
const content = fs.readFileSync(filePath, 'utf-8');
const sourceFile = ts.createSourceFile(
filePath,
content,
ts.ScriptTarget.Latest,
true,
ts.ScriptKind.TSX
);
const islandInfo = this.extractIslandInfo(sourceFile, filePath);
if (islandInfo) {
this.discoveredIslands.set(islandInfo.name, islandInfo);
if (this.config.debugMode) {
console.log(`🏝️ Found island: ${islandInfo.name}`);
}
}
// Parse template directives and components
this.parseTemplateDirectives(sourceFile);
} catch (error) {
console.error(`❌ Error parsing ${filePath}:`, error);
}
}
/**
* Extract island information from TypeScript AST
*/
private extractIslandInfo(sourceFile: ts.SourceFile, filePath: string): IslandInfo | null {
let islandName = path.basename(filePath, '.zenith.tsx');
let metadata: any = {};
let propsInterface: string | undefined;
const visit = (node: ts.Node) => {
// Look for metadata export
if (ts.isVariableStatement(node)) {
for (const declaration of node.declarationList.declarations) {
if (ts.isIdentifier(declaration.name) && declaration.name.text === 'metadata') {
metadata = this.extractMetadata(declaration.initializer);
}
}
}
// Look for props interface
if (ts.isInterfaceDeclaration(node)) {
const interfaceName = node.name.text;
if (interfaceName.includes('Props')) {
propsInterface = interfaceName;
this.extractPropsFromInterface(node, islandName);
}
}
ts.forEachChild(node, visit);
};
visit(sourceFile);
if (metadata.name) {
islandName = metadata.name;
}
return {
name: islandName,
propsInterface,
metadata,
dependencies: metadata.dependencies || [],
trustLevel: metadata.trustLevel || 'local',
execType: metadata.execType || 'local',
ecsComponents: metadata.ecsComponents || [],
filePath
};
}
/**
* Extract props from TypeScript interface
*/
private extractPropsFromInterface(node: ts.InterfaceDeclaration, islandName: string): void {
const props: PropInfo[] = [];
for (const member of node.members) {
if (ts.isPropertySignature(member)) {
const propName = (member.name as ts.Identifier)?.text;
const isOptional = member.questionToken !== undefined;
const typeNode = member.type;
if (propName && typeNode) {
const propType = this.getTypeString(typeNode);
const documentation = this.getJSDocComment(member);
props.push({
name: propName,
type: propType,
optional: isOptional,
documentation
});
}
}
}
this.islandProps.set(islandName, props);
}
/**
* Parse template directives from JSX
*/
private parseTemplateDirectives(sourceFile: ts.SourceFile): void {
const visit = (node: ts.Node) => {
if (ts.isJsxSelfClosingElement(node) || ts.isJsxOpeningElement(node)) {
// Collect all directive attributes
for (const attribute of node.attributes.properties) {
if (ts.isJsxAttribute(attribute)) {
const attrName = (attribute.name as ts.Identifier)?.text;
if (this.isDirectiveAttribute(attrName)) {
this.templateDirectives.add(attrName);
}
}
}
}
ts.forEachChild(node, visit);
};
visit(sourceFile);
}
/**
* Check if attribute is a ZenithKernel directive
*/
private isDirectiveAttribute(name: string): boolean {
return typeof name == "string" && (
name.startsWith('zk-') ||
name.startsWith('data-zk-') ||
name.startsWith('ecs-') ||
name.startsWith('data-ecs-') ||
name.startsWith('hydration-') ||
name.startsWith('data-hydration-') ||
name.startsWith('v-') ||
name.startsWith(':')
);
}
/**
* Generate comprehensive TypeScript definitions
*/
private generateZenithTypeDefinitions(): string {
const lines: string[] = [
'// AUTO-GENERATED BY ZENITHKERNEL JSX TYPE GENERATOR',
'// DO NOT EDIT THIS FILE MANUALLY',
'',
'import { HydraContext } from "../lib/hydra-runtime";',
'import { Entity } from "../core/ECSManager";',
'',
'// ================================================',
'// ZENITHKERNEL-SPECIFIC TYPE DEFINITIONS',
'// ================================================',
''
];
// Generate ZenithKernel base types
lines.push(...this.generateZenithTypes());
lines.push('');
// Generate island-specific types
lines.push(...this.generateIslandTypes());
lines.push('');
// Generate JSX namespace declaration
lines.push(...this.generateJSXNamespace());
return lines.join('\n');
}
/**
* Generate ZenithKernel base types
*/
private generateZenithTypes(): string[] {
return [
'// ================================================',
'// ZENITHKERNEL BASE TYPES',
'// ================================================',
'',
'/** ZenithKernel Hydra context for islands */',
'export interface HydraProps {',
' /** Peer ID for ZK verification */',
' peerId?: string;',
' /** ZK proof for verification */',
' zkProof?: string;',
' /** Trust level requirement */',
' trustLevel?: "unverified" | "local" | "community" | "verified";',
' /** ECS entity binding */',
' ecsEntity?: Entity | string;',
' /** Manifest URL for remote components */',
' manifestUrl?: string;',
' /** Additional context data */',
' [key: string]: any;',
'}',
'',
'/** ZK Gate component props */',
'export interface ZKGateProps {',
' /** Required ZK proof */',
' zkProof: string;',
' /** Trust level requirement */',
' trustLevel?: "verified" | "community";',
' /** Fallback content when verification fails */',
' fallback?: React.ReactNode;',
' /** Children to render when verified */',
' children?: React.ReactNode;',
'}',
'',
'/** ECS Binder component props */',
'export interface ECSBinderProps {',
' /** Entity ID to bind */',
' entityId: Entity | string;',
' /** Component types to observe */',
' components?: string[];',
' /** Auto-create entity if not found */',
' autoCreate?: boolean;',
' /** Update strategy */',
' updateStrategy?: "reactive" | "polling" | "manual";',
' /** Children render function */',
' children?: (data: Record<string, any>) => React.ReactNode;',
'}',
'',
'/** Template component props */',
'export interface TemplateProps {',
' /** ZK proof requirement */',
' "zk-proof"?: string;',
' /** ZK trust level */',
' "zk-trust"?: "unverified" | "local" | "community" | "verified";',
' /** ZK entity binding */',
' "zk-entity"?: string;',
' /** ZK verification strategy */',
' "zk-strategy"?: "eager" | "lazy" | "manual";',
' /** ECS entity binding */',
' "ecs-entity"?: string;',
' /** ECS components to observe */',
' "ecs-components"?: string | string[];',
' /** ECS auto-create entity */',
' "ecs-auto-create"?: boolean;',
' /** ECS update strategy */',
' "ecs-update-strategy"?: "reactive" | "polling" | "manual";',
' /** Hydration strategy */',
' "hydration-strategy"?: "immediate" | "visible" | "interaction" | "idle" | "manual";',
' /** Hydration priority */',
' "hydration-priority"?: "high" | "normal" | "low";',
' /** Lazy hydration */',
' "hydration-lazy"?: boolean;',
' /** Hydration trigger */',
' "hydration-trigger"?: string;',
' /** Hydration debounce */',
' "hydration-debounce"?: number;',
' /** Template directives */',
' "v-if"?: string;',
' "v-for"?: string;',
' /** Dynamic bindings */',
' [key: `:${string}`]: any;',
' /** Data attributes */',
' [key: `data-${string}`]: any;',
' /** Children content */',
' children?: React.ReactNode;',
'}',
''
];
}
/**
* Generate island-specific types
*/
private generateIslandTypes(): string[] {
const lines: string[] = [
'// ================================================',
'// ISLAND-SPECIFIC TYPES',
'// ================================================',
''
];
for (const [islandName, props] of this.islandProps.entries()) {
const island = this.discoveredIslands.get(islandName);
if (!island) continue;
lines.push(`/** Props for ${islandName} island */`);
lines.push(`export interface ${islandName}Props {`);
for (const prop of props) {
if (prop.documentation) {
lines.push(` /** ${prop.documentation} */`);
}
const optional = prop.optional ? '?' : '';
lines.push(` ${prop.name}${optional}: ${prop.type};`);
}
// Add standard island props
lines.push(' /** ZenithKernel Hydra context */');
lines.push(' context?: HydraContext;');
lines.push(' /** Additional props */');
lines.push(' [key: string]: any;');
lines.push('}');
lines.push('');
}
return lines;
}
/**
* Generate JSX namespace with intrinsic elements
*/
private generateJSXNamespace(): string[] {
const lines: string[] = [
'// ================================================',
'// JSX NAMESPACE EXTENSIONS',
'// ================================================',
'',
'declare global {',
' namespace JSX {',
' interface IntrinsicElements {'
];
// Add ZenithKernel-specific elements
lines.push(' /** ZenithKernel Template wrapper */');
lines.push(' Template: TemplateProps;');
lines.push(' template: TemplateProps;');
lines.push('');
lines.push(' /** ZenithKernel Hydra component */');
lines.push(' Hydra: HydraProps;');
lines.push(' hydra: HydraProps;');
lines.push('');
lines.push(' /** ZK Gate component for conditional rendering */');
lines.push(' ZKGate: ZKGateProps;');
lines.push(' "zk-gate": ZKGateProps;');
lines.push('');
lines.push(' /** ECS Binder component for data binding */');
lines.push(' ECSBinder: ECSBinderProps;');
lines.push(' "ecs-binder": ECSBinderProps;');
lines.push('');
// Add discovered islands
for (const [islandName, island] of this.discoveredIslands.entries()) {
const propsType = `${islandName}Props`;
const kebabName = this.toKebabCase(islandName);
lines.push(` /** ${islandName} island component */`);
lines.push(` ${islandName}: ${propsType};`);
lines.push(` "${kebabName}": ${propsType};`);
}
lines.push(' }');
lines.push(' }');
lines.push('}');
lines.push('');
lines.push('export {};');
return lines;
}
/**
* Convert PascalCase to kebab-case
*/
private toKebabCase(name: string): string {
return name
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
.replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
.toLowerCase();
}
/**
* Extract metadata from AST node
*/
private extractMetadata(node: ts.Expression | undefined): any {
if (!node) return {};
if (ts.isObjectLiteralExpression(node)) {
const metadata: any = {};
for (const property of node.properties) {
if (ts.isPropertyAssignment(property)) {
const key = (property.name as ts.Identifier)?.text;
const value = this.extractLiteralValue(property.initializer);
if (key) {
metadata[key] = value;
}
}
}
return metadata;
}
return {};
}
/**
* Extract literal value from AST node
*/
private extractLiteralValue(node: ts.Expression): any {
if (ts.isStringLiteral(node)) {
return node.text;
}
if (ts.isNumericLiteral(node)) {
return parseFloat(node.text);
}
if (node.kind === ts.SyntaxKind.TrueKeyword) {
return true;
}
if (node.kind === ts.SyntaxKind.FalseKeyword) {
return false;
}
if (ts.isArrayLiteralExpression(node)) {
return node.elements.map(element => this.extractLiteralValue(element));
}
return node.getText();
}
/**
* Get type string from TypeScript type node
*/
private getTypeString(typeNode: ts.TypeNode): string {
if (ts.isTypeLiteralNode(typeNode)) {
return 'object';
}
if (ts.isArrayTypeNode(typeNode)) {
return `${this.getTypeString(typeNode.elementType)}[]`;
}
return typeNode.getText();
}
/**
* Get JSDoc comment from node
*/
private getJSDocComment(node: ts.Node): string | undefined {
const jsDocTags = ts.getJSDocTags(node);
if (jsDocTags.length > 0) {
return jsDocTags[0].comment?.toString();
}
return undefined;
}
/**
* Write type definitions to file
*/
private async writeTypeDefinitions(content: string): Promise<void> {
// Ensure output directory exists
fs.mkdirSync(this.config.outputDir, { recursive: true });
// Write the file
fs.writeFileSync(this.config.outputFile, content, 'utf-8');
}
}
// ================================================
// CLI INTERFACE
// ================================================
function createConfig(): ZenithGeneratorConfig {
const args = process.argv.slice(2);
const islandsDir = args.find(arg => !arg.startsWith('--')) ||
path.resolve('src/modules/Rendering/islands');
const outputDir = path.resolve('src/types');
const outputFile = path.join(outputDir, 'zenith-jsx.d.ts');
return {
islandsDir: path.resolve(islandsDir),
outputDir,
outputFile,
watchMode: args.includes('--watch'),
debugMode: args.includes('--debug')
};
}
async function main() {
const config = createConfig();
const generator = new ZenithJSXGenerator(config);
try {
if (config.watchMode) {
await generator.startWatchMode();
// Keep process alive in watch mode
process.stdin.resume();
} else {
await generator.generate();
}
} catch (error) {
console.error('❌ Failed to generate JSX types:', error);
process.exit(1);
}
}
// Run if called directly
if (require.main === module) {
main().catch(console.error);
}
export type { ZenithJSXGenerator, ZenithGeneratorConfig };