@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
text/typescript
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 = [];
}
}