UNPKG

@udraft/core

Version:

uDraft is a language and stack agnostic code-generation tool that simplifies full-stack development by converting a single YAML file into code for rapid development.

245 lines (224 loc) 8.31 kB
import * as Case from "case"; import { UModel } from "../entities/model"; import { URenderer } from "../entities/renderer"; import { $attr } from "../shortcuts/queries"; import { RenderContent, RenderPath, RenderSelection } from "../types/renderer"; import { _array, _enum, _ref, _required, _rootModule, } from "../shortcuts/attributes"; import { UAttribute } from "../entities/attribute"; import { UFeature } from "../entities/feature"; const KEYS = { draft: "draft", }; export default class TSDraftRenderer extends URenderer { private _draftPath = "src/core/draft.ts"; constructor(options?: { draftPath?: string }) { super("ts@draft"); if (options?.draftPath) this._draftPath = options.draftPath; } async select(): Promise<RenderSelection> { const models = this.$models(); const features = this.$features(); const modules = this.$modules(); const paths: RenderPath[] = [ { key: KEYS.draft, path: this._draftPath, }, ]; return { paths, modules, features: features, models: models, }; } async render(): Promise<RenderContent[]> { const output: RenderContent[] = []; const models = this.$selection().models || []; const features = this.$selection().features || []; const modules = this.$selection().modules || []; // type UAttribute = { name: string; value?: any }; let content = `export type UAttribute = { name: string; value?: any };\n` + `export type UField = {name: string; type: string; attributes: UAttribute[], ref?: MODELS; isArray: boolean; };\n` + `export type UModel = {name: string; module: MODULES; attributes: UAttribute[]; fields?: UField[]; enum?: Record<string, any> };\n` + `export type UFeature = {name: string; module: MODULES; attributes: UAttribute[]; input?: UModel; output?: UModel; };\n` + `export type UModule = {name: string; attributes: UAttribute[]; features: Record<string, UFeature>; models: Record<string, UModel> };\n`; content += `export enum MODULES {\n${modules .map((mod) => ` ${Case.pascal(mod.$name())} = "${mod.$name()}",`) .join("\n")}\n};\n\nexport enum FEATURES {\n${features .map( (feature) => ` ${ Case.pascal($attr(feature, _rootModule())?.$name() ?? "") + Case.pascal(feature.$name()) } = "${ $attr(feature, _rootModule())?.$name() + "." + feature.$name() }",` ) .join("\n")}\n};\n\nexport enum MODELS {\n${models .map((model) => ` ${Case.pascal(model.$name())} = "${model.$name()}",`) .join("\n")}\n};\n\n`; const attrToStr = (attributes: UAttribute<any>[], tab = " ") => { if (attributes.length == 0) return `[]`; return ( "[\n" + attributes .filter( (a) => !["enum", "rootModule", "ref", "array"].includes(a.$name()) ) .map((a) => { let value = ""; if (a.$value() !== null) { if (a.$name() == "regex") { value = a.$value().toString(); } else value = JSON.stringify(a.$value(), null, 2) .split("\n") .map((line, i) => (i > 0 ? tab + " " : "") + line) .join("\n"); } return value ? `${tab}{\n${tab} name: "${a.$name()}"${ ",\n" + tab + " value: " + value }\n${tab}},` : `${tab}{ name: "${a.$name()}" },`; }) .join("\n") + `\n${tab.slice(2)}]` ); }; const modelToStr = (model: UModel, tab = " ") => { let fields = ""; let enumValues = ""; let enumAttr = $attr(model, _enum()); let rootModule = $attr(model, _rootModule()); if (!enumAttr) { fields = `fields: [\n${model .$fields() .map((field) => { let ref = ""; const refAttr = $attr(field, _ref()); if (refAttr) { ref = `MODELS.${Case.pascal(refAttr.$name())}`; } return `${tab} {\n${tab} name: "${field.$name()}",\n${tab} isArray: ${$attr( field, _array() )},\n${tab} type: "${field.$type()}",\n${ ref ? `${tab} ref: ${ref},\n` : "" }${tab} attributes: ${attrToStr( field.$attributes(), tab + " " )},\n${tab} },`; }) .join("\n")}\n${tab} ]`; } else { enumValues = `enum: {\n${Object.keys(enumAttr) .map( (k) => `${tab} "${k}": ${JSON.stringify((enumAttr as any)[k])},` ) .join("\n")}\n${tab} }`; } return `{\n${tab} name: "${model.$name()}",\n${ !!rootModule ? `${tab} module: MODULES.${Case.pascal(rootModule.$name())},` : "" }\n${tab} attributes: ${attrToStr( model.$attributes(), tab + " " )},\n${tab} ${fields || enumValues},\n${tab}}`; }; const featureToStr = (feature: UFeature, tab = " ") => { let input = ""; let output = ""; const modelInput = feature.$input(); const modelOutput = feature.$output(); const rootModule = $attr(feature, _rootModule()); if (modelInput) { input = `\n${tab} input: uModels["${modelInput.$name()}"],`; } if (modelOutput) { output = `\n${tab} output: uModels["${modelOutput.$name()}"],`; } return `{\n${tab} name: "${feature.$name()}",\n${ !!rootModule ? `${tab} module: MODULES.${Case.pascal(rootModule.$name())},` : "" }\n${tab} attributes: ${attrToStr( feature.$attributes(), tab + " " )},${input}${output}\n }`; }; content += `export const uModel = (name: MODELS | string) =>\n uModels[name as keyof typeof uModels] as any as UModel;\n`; content += `export const uFeature = (name: FEATURES | string) =>\n uFeatures[name as keyof typeof uFeatures] as any as UFeature;\n`; content += `export const uModule = (name: MODULES | string) =>\n uModules[name as keyof typeof uModules] as any as UModule;\n`; content += `export const uAttribute = <Type>( src: { attributes: UAttribute[] }, name: string, defaultValue?: Type, defaultWhenNotPresent?: Type ): Type | null | undefined => { const attr = src.attributes.find((x) => x.name === name); if (attr) { if (attr.value !== undefined) return attr.value as Type; else return defaultValue ?? null; } return defaultWhenNotPresent ?? undefined; };\n\n`; content += `const uModels = {\n${models .map((model) => ` "${model.$name()}": ` + modelToStr(model, " ") + ",") .join("\n")}\n};\n\n`; content += `const uFeatures = {\n${features .map( (feature) => ` "${ $attr(feature, _rootModule())?.$name() + "." + feature.$name() }": ` + featureToStr(feature, " ") + "," ) .join("\n")}\n};\n\n`; content += `const uModules = {\n${modules .map( (mod) => ` "${mod.$name()}": {\n name: "${mod.$name()}",\n attributes: ${attrToStr( mod.$attributes(), " " )},\n features: {\n${mod .$features() .map( (feature) => ` "${feature.$name()}": uFeatures["${ $attr(feature, _rootModule())?.$name() + "." + feature.$name() }"],` ) .join("\n")}\n },\n models: {\n${models .filter((model) => $attr(model, _rootModule()) == mod) .map( (model) => ` ["${model.$name()}"]: uModels["${model.$name()}"],` ) .join("\n")}\n },\n },` ) .join("\n")}\n};\n\n`; content += `export const uDraft = {\n attributes: ${attrToStr( this.$draft().$attributes(), " " )},\n modules: uModules,\n features: uFeatures,\n models: uModels,\n};\n\n`; output.push({ key: KEYS.draft, content, }); return output; } } export type MODULES = { t: "80"; };