prasi-bun/app/web/src/nova/vi/render/script/eval-prop.tsx

250 lines
6.9 KiB
TypeScript

import { createId } from "@paralleldrive/cuid2";
import type { Doc } from "yjs";
import { FMCompDef } from "../../../../utils/types/meta-fn";
import { IMeta, active } from "../../../ed/logic/ed-global";
import { VG } from "../global";
import { ViRender } from "../render";
import { viScriptArg } from "./arg";
import { replaceWithObject, replacement } from "./eval-script";
import { extractNavigate } from "./extract-nav";
export const w = window as any;
export const viEvalProps = (
vi: {
layout: VG["layout"];
mode: VG["mode"];
meta: VG["meta"];
site: { db: any; api: any };
page: VG["page"];
on_nav_loaded?: VG["on_preload"];
},
meta: IMeta,
is_layout: boolean,
passprop: any,
parent_key?: any
) => {
if (meta.item.component?.id) {
let script = meta.item.script;
if (parent_key) {
if (!meta.item.script_keyed) meta.item.script_keyed = {};
if (!meta.item.script_keyed[parent_key])
meta.item.script_keyed[parent_key] = {};
script = meta.item.script_keyed[parent_key];
} else {
if (!meta.item.script) {
meta.item.script = {};
}
script = meta.item.script;
}
if (!script) return;
const exports = (window as any).exports;
const arg = {
...exports,
db: vi.site.db,
api: vi.site.api,
...viScriptArg(vi),
...passprop,
params,
};
script.props = {};
let fails = new Set<string>();
if (!!meta.item.component.props) {
const _props: any = {};
for (const [name, prop] of Object.entries(meta.item.component.props)) {
try {
if (prop.meta?.type === "content-element") {
let val = {
_jsx: true,
fn: (arg: { passprop: any; meta: IMeta; prop_name: string }) => {
const id = prop.content?.id;
if (id) {
const m = is_layout ? vi.layout?.meta[id] : vi.meta[id];
if (!m) return null;
const instances = meta.instances;
if (!arg.meta.item.originalId || !instances) {
return null;
}
const instance = instances[meta.item.id];
if (!instance) return null;
const original_id = arg.meta.item.originalId;
if (
m.mitem &&
((prop.jsxCalledBy &&
(!prop.jsxCalledBy.includes(original_id) ||
prop.jsxCalledBy.length !== 2)) ||
!prop.jsxCalledBy)
) {
const mprop = meta.mitem
?.get("component")
?.get("props")
?.get(name);
if (mprop) {
let mjby = mprop.get("jsxCalledBy");
if (!mjby || typeof mjby !== "object") {
mprop.set("jsxCalledBy", [meta.item.id, original_id]);
} else {
if (
mjby &&
(!mjby.includes(original_id) ||
mjby.length !== 2 ||
mjby[0] !== meta.item.id ||
mjby[1] !== original_id)
) {
mprop.set("jsxCalledBy", [meta.item.id, original_id]);
}
}
}
}
return (
<ViRender
meta={m}
passprop={arg.passprop}
is_layout={is_layout}
/>
);
}
return null;
},
};
arg[name] = val;
if (passprop) {
passprop[name] = val;
}
continue;
}
if (prop.value) {
extractNavigate(vi, prop.value);
}
if (!prop.valueBuilt && prop.value && meta.mitem) {
const mprop = meta.mitem?.get("component")?.get("props")?.get(name);
if (mprop) {
updatePropValueBuilt(mprop, prop.value);
return;
}
}
const js = prop.valueBuilt || "";
const src = replaceWithObject(js, replacement) || "";
const fn = new Function(
...Object.keys(arg),
`// [${meta.item.name}] ${name}: ${meta.item.id}
return ${src}
`
);
script.props[name] = { value: src };
let val = fn(...Object.values(arg));
if (typeof val === "function") {
script.props[name].fn = val;
val = (...args: any[]) => {
if (script) return script.props?.[name].fn(...args);
};
}
arg[name] = val;
_props[name] = val;
if (passprop) {
passprop[name] = val;
}
} catch (e) {
fails.add(name);
}
}
if (location.pathname.startsWith("/ed/") && active.item_id) {
if (meta.item.id === active.item_id) {
active.scope = {};
for (const [k, v] of Object.entries(passprop)) {
active.scope[k] = v;
}
active.scope.self_props = _props;
}
}
}
}
};
const conf = {
timeout: null as any,
set: new WeakSet<FMCompDef>(),
map: {} as Record<string, { mprop: FMCompDef }>,
src: {} as Record<string, string>,
};
const updatePropValueBuilt = (mprop: FMCompDef, src: string) => {
if (!conf.set.has(mprop)) {
conf.set.add(mprop);
const id = createId();
conf.map[id] = { mprop };
conf.src[id] = src;
}
clearTimeout(conf.timeout);
conf.timeout = setTimeout(async () => {
const result = await _api.code_build(conf.src);
let doc = null as unknown as Doc;
for (const [k, v] of Object.entries(result)) {
const mprop = conf.map[k].mprop;
if (!doc && mprop.doc) {
doc = mprop.doc;
break;
}
}
if (doc) {
doc.transact(() => {
for (const [k, v] of Object.entries(result)) {
const mprop = conf.map[k].mprop;
mprop.set("valueBuilt", v);
}
});
conf.set = new WeakSet();
conf.map = {};
conf.src = {};
}
}, 300);
};
export const updatePropScope = (
vi: { site: { db: any; api: any } },
meta: IMeta,
scope: any,
parent_key?: any
) => {
const script = parent_key
? meta.item.script_keyed?.[parent_key]
: meta.item.script;
if (!script) return;
if (script.props) {
const scopes = { ...scope, api: vi.site.api, db: vi.site.db };
for (const [name, prop] of Object.entries(script.props)) {
if (prop.fn) {
const fn = new Function(
...Object.keys(scopes),
`// [${meta.item.name}] ${name}: ${meta.item.id}
return ${prop.value || ""}
`
);
prop.fn = fn(...Object.values(scopes));
}
}
}
};