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.

187 lines (157 loc) 5.2 kB
import { RenderContent, RenderPath, RenderSelection } from "../types/renderer"; import { UDraft } from "./draft"; import { UModel } from "./model"; import { $attr } from "../shortcuts/queries"; import { _ref, _rootModule } from "../shortcuts/attributes"; import { UModule } from "./module"; import { UFeature } from "./feature"; export class URenderer { private _draft?: UDraft; private _selection: RenderSelection = {}; private _contents: RenderContent[] = []; private _outputs: RenderContent[] = []; private _name: string; constructor(name: string) { this._name = name; } $name() { return this._name; } $draft() { return this._draft as UDraft; } $selection() { return { ...this._selection }; } $paths() { return [...(this._selection.paths || [])]; } $contents() { return [...this._contents]; } $outputs() { return [...this._outputs]; } $path(key: string) { return this.$paths().find((f) => f.key == key) || null; } $content(key: string) { return this.$contents().find((f) => f.key == key) || null; } $output(key: string) { return this.$outputs().find((f) => f.key == key) || null; } $modules(where?: (module: UModule) => boolean) { const modules: UModule[] = this.$draft() .$modules() .filter((mod) => (where ? where(mod) : true)); return modules; } $features(where?: (module: UModule, feature: UFeature) => boolean) { const modules = this.$draft().$modules(); const features: UFeature[] = []; modules.forEach((mod) => { let foundFeatures = mod.$features(); if (where) foundFeatures = foundFeatures.filter((feature) => where(mod, feature)); features.push(...foundFeatures); }); return features; } $models(where?: (module: UModule, model: UModel) => boolean) { const modules = this.$draft().$modules(); let models: UModel[] = []; modules.forEach((mod) => { let foundModels: UModel[] = [...mod.$models()]; mod.$features().forEach((feature) => { const root: UModel[] = []; if (feature.$input()) root.push(feature.$input() as UModel); if (feature.$output()) root.push(feature.$output() as UModel); const modelIsAlreadyRegistered = (model: UModel) => foundModels.some((m) => m.$name() == model.$name()) || models.some((m) => m.$name() == model.$name()); const deepSearch = (model: UModel, tail: string[] = []) => { if (tail.includes(model.$name())) return; tail.push(model.$name()); const modelRootModule = $attr(model, _rootModule()); const isAlreadyRegistered = modelIsAlreadyRegistered(model); if ( !isAlreadyRegistered || (modelRootModule && modelRootModule.$name() == mod.$name()) ) { if (isAlreadyRegistered) { foundModels = foundModels.filter( (m) => m.$name() != model.$name() ); models = models.filter((m) => m.$name() != model.$name()); } foundModels.push(model); model.$fields().forEach((field) => { if (field.$type() == "nested" || field.$type() == "reference") { const referencedModel = $attr(field, _ref()); if (referencedModel && referencedModel.$name() != model.$name()) deepSearch(referencedModel, tail); } }); } }; root.forEach((model) => deepSearch(model)); }); if (where) foundModels = foundModels.filter((model) => where(mod, model)); models.push(...foundModels); }); return models; } $resolveRelativePath(from: string, to: string): string { const toFileName = to.split("/").slice(-1)[0]; const fromParts = from .split("/") .filter((v) => !!v) .slice(0, -1); const toParts = to .split("/") .filter((v) => !!v) .slice(0, -1); for (let i = 0; i <= fromParts.length; i++) { const pathIsSplitting = fromParts.slice(0, i).join("/") !== toParts.slice(0, i).join("/"); if ( pathIsSplitting || (i == fromParts.length && fromParts.length < toParts.length) ) { let returns = ""; if (pathIsSplitting) for (let j = 0; j <= fromParts.length - i; j++) returns += "../"; return `${returns ? returns : "./"}${toParts .slice(pathIsSplitting ? i - 1 : i) .join("/")}/${toFileName}`; } } return `./${toFileName}`; } async init(draft: UDraft) { this._draft = draft; this._selection = await this.select(); return this; } async run(contents: RenderContent[]) { this._contents = contents; this._outputs = await this.render(); this._outputs.forEach((output) => { output.meta = this.$content(output.key)?.meta || {}; }); return this; } async select(): Promise<RenderSelection> { return {}; } async render(): Promise<RenderContent[]> { return []; } clear() { this._draft = undefined; this._selection = {}; this._contents = []; this._outputs = []; } }