UNPKG

@ibyar/cli

Version:

The Ibyar/Aurora CLI tool

197 lines (194 loc) 8.64 kB
import ts from 'typescript/lib/tsserverlibrary.js'; import { ToCamelCase } from '@ibyar/core/node.js'; export function convertToProperties(json) { const keys = Object.keys(json); return keys.map(key => createProperty(key, json[key])); } export function createProperty(name, value) { return ts.factory.createPropertyAssignment(name, createInitializer(value)); } export function createInitializer(value) { if (Array.isArray(value)) { const elements = value.map(createInitializer); return ts.factory.createArrayLiteralExpression(elements); } switch (typeof value) { case 'string': return ts.factory.createStringLiteral(value); case 'number': return ts.factory.createNumericLiteral(value); case 'bigint': return ts.factory.createBigIntLiteral(value.toString()); case 'boolean': return value ? ts.factory.createTrue() : ts.factory.createFalse(); case 'object': { if (!value) { return ts.factory.createNull(); } const properties = convertToProperties(value); return ts.factory.createObjectLiteralExpression(properties); } ; } return ts.factory.createNull(); } export function createInterfaceType(viewName, extendsType) { const identifier = ts.factory.createIdentifier(extendsType); const expressionWithTypeArguments = ts.factory.createExpressionWithTypeArguments(identifier, undefined); const heritageClause = ts.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [expressionWithTypeArguments]); return ts.factory.createInterfaceDeclaration(undefined, viewName, undefined, [heritageClause], []); // const exportModifier: ts.ModifierSyntaxKind = ts.SyntaxKind.ExportKeyword; // const modifier = ts.factory.createModifier(exportModifier); // return ts.factory.createInterfaceDeclaration([modifier], viewName, undefined, [heritageClause], []); } export function createTypeLiteral(typeName) { const members = [ ts.factory.createConstructSignature(undefined, [], ts.factory.createTypeReferenceNode(typeName)), ts.factory.createPropertySignature([ts.factory.createModifier(ts.SyntaxKind.ReadonlyKeyword)], 'prototype', undefined, ts.factory.createTypeReferenceNode(typeName)), ]; return ts.factory.createTypeLiteralNode(members); } /** * create view constructor interface of Type `T` * * ```ts * interface ConstructorOfView<T> { * new(): T; * readonly prototype: T; * } * ``` */ export function createConstructorOfViewInterfaceDeclaration() { const typeParameter = ts.factory.createTypeParameterDeclaration(undefined, 'T'); const members = [ ts.factory.createConstructSignature(undefined, [], ts.factory.createTypeReferenceNode('T')), ts.factory.createPropertySignature([ts.factory.createModifier(ts.SyntaxKind.ReadonlyKeyword)], 'prototype', undefined, ts.factory.createTypeReferenceNode('T')), ]; return ts.factory.createInterfaceDeclaration(undefined, 'ConstructorOfView', [typeParameter], undefined, members); } export function createStaticPropertyViewType(typeName) { const typeArgument = ts.factory.createTypeReferenceNode(typeName); return ts.factory.createTypeReferenceNode('ConstructorOfView', [typeArgument]); } export function generateStatements(sourceText, scriptTarget = ts.ScriptTarget.ES2020, scriptKind = ts.ScriptKind.TS) { return ts.createSourceFile('temp', sourceText, scriptTarget, false, scriptKind).statements; } export function generateNode(sourceText, scriptTarget = ts.ScriptTarget.ES2020, scriptKind = ts.ScriptKind.TS) { return generateStatements(sourceText, scriptTarget, scriptKind)[0]; } /** * declare global { interface HTMLElementTagNameMap { 'user-view': HTMLUserViewElement; } } */ export function updateGlobalHTMLElementTagNameMap(views) { const sourceCode = ` declare global { interface HTMLElementTagNameMap { ${views.map(view => `['${view.tagName}']: ${view.viewName};`).join('\n')} } }`; return generateStatements(sourceCode); } export function updateModuleTypeWithComponentView(classes) { const viewClassDeclarations = classes.map(c => { const inputs = c.inputs.map(input => input.aliasName); const outputs = c.outputs.map(input => input.aliasName).map(output => 'on' + ToCamelCase(output)); const attributes = [...inputs, ...outputs].map(s => `'${s}'`).join(' | '); const observedAttributes = attributes.length ? `public static observedAttributes: (${attributes})[];` : ''; const interfaceBody = ` ${observedAttributes} ${c.inputs.map(input => `public ${input.aliasName}${input.type ? `: ${input.type}` : ''};`).join('\n')} ${c.outputs.map(output => `public on${ToCamelCase(output.aliasName)}${output.type ? `: ${output.type}` : ''};`).join('\n')} `; // need to fix @FormValue type; return c.views.map(view => { const disabledFeatures = (Array.isArray(view.disabledFeatures) && view.disabledFeatures.length > 0) ? `public static disabledFeatures: ('${view.disabledFeatures.join(' | ')}')[];` : ''; return ` declare class ${view.viewName} extends ${view.extendsType} { ${disabledFeatures} ${interfaceBody.trim()} } declare interface ${view.viewName} extends ${view.formAssociated ? `BaseFormAssociatedComponent<${c.name}>, ` : ''}BaseComponent<${c.name}> {} `; }); }); const views = classes.flatMap(c => c.views.map(v => ({ tagName: v.selector, viewName: v.viewName }))); const sourceCode = ` import { BaseComponent, BaseFormAssociatedComponent, ConstructorOfView } from '@ibyar/core'; ${viewClassDeclarations.join('\n')} declare global { interface HTMLElementTagNameMap { ${views.map(view => `['${view.tagName}']: ${view.viewName};`).join('\n')} } }`; return generateStatements(sourceCode); } /** * create new type with name `ɵɵ0Directives0ɵɵ` * * example: * ```ts * export type ɵɵ0IfDirective0ɵɵ = { * selector: '*if'; * successor: '*else', * inputs: [ * { name: 'ifCondition', aliasName: 'if' }, * { name: 'thenTemplateRef', aliasName: 'then' }, * { name: 'elseTemplateRef', aliasName: 'else' }, * ], * outputs: [], * }; * ``` * @param classes directive class information * @returns new array of statements */ export function updateModuleTypeWithDirectives(classes) { const nodes = classes.map(directive => { const inputs = directive.inputs.map(input => `{name: '${input.name}', aliasName: '${input.aliasName}'}`); const outputs = directive.outputs.map(output => `{name: '${output.name}', aliasName: '${output.aliasName}'}`); const temp = [`selector: '${directive.name}'`]; if (directive.successors) { temp.push(`successors: [${directive.successors.map(successor => `'${successor}'`).join(',')}]`); } if (directive.inputs.length > 0) { temp.push(`inputs: [${inputs.join(',')}]`); } if (directive.outputs.length > 0) { temp.push(`outputs: [${outputs.join(',')}]`); } let directiveTypeName = directive.name.startsWith('*') ? directive.name.substring(1) : directive.name; directiveTypeName = directiveTypeName.replaceAll('-', ' ') .split(' ') .map(str => str.charAt(0).toUpperCase() + str.substring(1)) .join(''); return `export type ɵɵ0${ToCamelCase(directiveTypeName)}Directive0ɵɵ = {${temp.join(',')}};`; }); return generateStatements(nodes.join()); } export function convertToRuntimeMetadata(signalMetadata) { return Object.entries(signalMetadata).flatMap(([key, infos]) => { const optional = infos.filter(info => !info.necessity); const optionalItem = { signal: key, options: optional.map(info => ({ name: info.name, alias: info.aliasName })), }; const required = infos.filter(info => info.necessity); if (!required.length) { return [optionalItem]; } const requiredItem = { signal: key, necessity: 'required', options: required.map(info => ({ name: info.name, alias: info.aliasName })), }; return [optionalItem, requiredItem]; }); } export function createSignalsAssignment(signalMetadata) { return ts.factory.createPropertyAssignment('signals', createInitializer(convertToRuntimeMetadata(signalMetadata))); } //# sourceMappingURL=factory.js.map