@dooboostore/dom-render
Version:
html view template engine
605 lines (604 loc) • 23.9 kB
JavaScript
import { RawSet } from "./rawsets/RawSet.js";
import { EventManager } from "./events/EventManager.js";
import { DomRenderFinalProxy, Shield } from "./types/Types.js";
import { RawSetType } from "./rawsets/RawSetType.js";
import { DrThisProperty } from "./operators/DrThisProperty.js";
import { RawSetOperatorType } from "./rawsets/RawSetOperatorType.js";
import { ComponentSet } from "./components/ComponentSet.js";
import { DomRenderNoProxyKey, isDomRenderNoProxy } from "./decorators/DomRenderNoProxy.js";
import { isOnCreateRender } from "./lifecycle/OnCreateRender.js";
import { isOnInitRender } from "./lifecycle/OnInitRender.js";
import { isOnCreateRenderData } from "./lifecycle/OnCreateRenderData.js";
import { isOnChangeAttrRender } from "./lifecycle/OnChangeAttrRender.js";
import { DocumentUtils } from "@dooboostore/core-web";
import { isOnBeforeReturnSet } from "./lifecycle/OnBeforeReturnSet.js";
import { isOnChildRenderedByProperty } from "./lifecycle/OnChildRenderedByProperty.js";
import { ObjectUtils } from "@dooboostore/core";
import { ValidUtils } from "@dooboostore/core";
import { isOnProxyDomRender } from "./lifecycle/OnProxyDomRender.js";
import { isOnRawSetRendered } from "./lifecycle/OnRawSetRendered.js";
import { isOnChildRawSetRendered } from "./lifecycle/OnChildRawSetRendered.js";
import { DomRenderComponentMetaKey, DomRenderProxyMetaKey } from "./rawsets/index.js";
const excludeGetSetPropertys = [
DomRenderComponentMetaKey.DOMRENDER_COMPONENTS_KEY,
DomRenderProxyMetaKey.onBeforeReturnGet,
DomRenderProxyMetaKey.onBeforeReturnSet,
DomRenderProxyMetaKey.__render,
DomRenderProxyMetaKey._rawSets,
DomRenderProxyMetaKey._targets,
DomRenderProxyMetaKey._domRender_proxy,
DomRenderProxyMetaKey._DomRender_isFinal,
DomRenderProxyMetaKey._domRender_ref,
DomRenderProxyMetaKey._DomRender_origin,
DomRenderProxyMetaKey._DomRender_ref,
DomRenderProxyMetaKey._DomRender_proxy
];
const isWrapProxyDomRenderProxy = (obj) => {
return obj && typeof obj === "object" && "_DomRender_isProxy" in obj;
};
const getDomRenderOriginObject = (obj) => {
if (isWrapProxyDomRenderProxy(obj)) {
return obj._domRender_origin;
}
return obj;
};
const getDomRenderConfig = (obj) => {
if (isWrapProxyDomRenderProxy(obj)) {
return obj._domRender_config;
}
return void 0;
};
const getDomRenderProxy = (obj) => {
if (isWrapProxyDomRenderProxy(obj)) {
return obj._domRender_proxy;
}
return void 0;
};
class DomRenderProxy {
// public name = Math.random();
constructor(_domRender_origin, target, _domRender_config) {
this._domRender_origin = _domRender_origin;
this._domRender_config = _domRender_config;
this._domRender_ref = /* @__PURE__ */ new Map();
this._rawSets = /* @__PURE__ */ new Map();
this._targets = /* @__PURE__ */ new Set();
if (target) {
this._targets.add(target);
}
}
static unFinal(obj) {
return DomRenderFinalProxy.unFinal(obj);
}
static final(obj) {
return DomRenderFinalProxy.final(obj);
}
static isFinal(obj) {
return ValidUtils.isNull(obj) || DomRenderFinalProxy.isFinal(obj);
}
static isProxy(obj) {
return ValidUtils.isNull(obj) || isWrapProxyDomRenderProxy(obj);
}
static getOriginalObject(obj) {
if (isWrapProxyDomRenderProxy(obj)) {
return obj._domRender_origin;
}
return obj;
}
async run(objProxy) {
this._domRender_proxy = objProxy;
if (isOnProxyDomRender(objProxy)) {
objProxy.onProxyDomRender(this._domRender_config);
}
const obj = getDomRenderOriginObject(objProxy);
if (obj) {
Object.keys(obj).forEach((it) => {
try {
const isNoProxy = Reflect.getMetadata(DomRenderNoProxyKey, obj, it) || Reflect.getMetadata(DomRenderNoProxyKey, Object.getPrototypeOf(obj).constructor);
if (isNoProxy) {
return obj;
}
} catch (e) {
}
const target = obj[it];
if (target !== void 0 && target !== null && typeof target === "object" && !DomRenderProxy.isFinal(target) && !Object.isFrozen(target) && !(obj instanceof Shield)) {
const isExcluded = this._domRender_config.proxyExcludeTyps?.some((it2) => target instanceof it2) ?? false;
if (!isExcluded) {
const proxyAfter = this.proxy(objProxy, target, it);
obj[it] = proxyAfter;
}
}
});
}
for (let target of this._targets) {
await this.initRender(target);
}
}
async initRender(target, rawSet) {
if (target instanceof Element) {
target.innerHTML = target.innerHTML.replace(/@/g, "this");
}
const onCreate = target.getAttribute?.(RawSet.DR_ON_CREATE_ARGUMENTS_OPTIONNAME);
let createParam = [];
if (onCreate) {
createParam = ObjectUtils.Script.evaluateReturn(onCreate, this._domRender_proxy);
if (!Array.isArray(createParam)) {
createParam = [createParam];
}
}
if (isOnCreateRenderData(this._domRender_proxy)) {
this._domRender_proxy?.onCreateRenderData({ rootParent: this._domRender_proxy });
}
if (isOnCreateRender(this._domRender_proxy)) {
this._domRender_proxy.onCreateRender(...createParam);
}
this._targets.add(target);
const rawSets = RawSet.checkPointCreates(target, this._domRender_proxy, this._domRender_config);
this._domRender_config.eventManager.applyEvent(
this._domRender_proxy,
this._domRender_config.eventManager.findAttrElements(target, this._domRender_config),
this._domRender_config
);
rawSets.forEach((it) => {
const variables = it.getUsingTriggerVariables(this._domRender_config);
if (variables.size <= 0) {
this.addRawSet("", it);
} else {
variables.forEach((sit) => {
this.addRawSet(sit, it);
});
}
});
const targetRawSets = await this.render(this.getRawSets());
for (let targetRawSet of targetRawSets) {
let initParam;
if (onCreate) {
initParam = ObjectUtils.Script.evaluateReturn(onCreate, this._domRender_proxy);
}
if (isOnInitRender(this._domRender_proxy)) {
await this._domRender_proxy.onInitRender(
initParam,
rawSet ?? { dataSet: { config: this._domRender_config } }
);
}
}
}
getRawSets() {
const set = /* @__PURE__ */ new Set();
this._rawSets.forEach((v, k) => {
v.forEach((it) => set.add(it));
});
return Array.from(set);
}
// 중요 important
async render(raws, fullPathStr) {
if (typeof raws === "string") {
const iter = this._rawSets.get(raws);
if (iter) {
raws = Array.from(iter);
} else {
raws = void 0;
}
}
const removeRawSets = [];
const rawSets = raws ?? this.getRawSets();
const CHUNK_SIZE = 50;
const chunks = [];
let rawSetsArray = Array.from(rawSets);
for (const rawSet of rawSetsArray) {
await this.renderExecute(rawSet, fullPathStr, removeRawSets);
}
if (isOnChildRawSetRendered(this._domRender_proxy)) {
await this._domRender_proxy.onChildRawSetRendered();
}
if (removeRawSets.length > 0) {
this.removeRawSet(...removeRawSets);
}
return this.getRawSets();
}
async renderExecute(it, fullPathStr, removeRawSets) {
it.getUsingTriggerVariables(this._domRender_config).forEach((path) => {
this.addRawSet(path, it);
});
let renderResult;
if (it.isConnected) {
const targetAttrMap = it.point.node?.getAttribute?.(EventManager.normalAttrMapAttrName);
if (it.detect?.action) {
it.detect.action();
} else if (it.type === RawSetType.TARGET_ELEMENT && it.dataSet?.render?.currentThis && fullPathStr && targetAttrMap) {
const attrStart = performance.now();
new Map(JSON.parse(targetAttrMap)).forEach((v, k) => {
var _a;
const isUsing = v.variablePaths.some((it2) => EventManager.isUsingThisVar(it2.inner, `this.${fullPathStr}`));
if (isUsing) {
const targetAttrObject = RawSet.getAttributeObject(it.point.node, {
script: it.dataSet?.render?.renderScript ?? "",
obj: Object.assign(this._domRender_proxy ?? {}, { __render: it.dataSet?.render })
});
(_a = it.dataSet).render ?? (_a.render = {});
it.dataSet.render.attribute = targetAttrObject;
if (isOnChangeAttrRender(it.dataSet?.render?.currentThis)) {
it.dataSet?.render?.currentThis?.onChangeAttrRender?.(k, targetAttrObject[k], { rawSet: it });
}
}
});
const attrEnd = performance.now();
} else {
const renderStart = performance.now();
const rawSets = await it.render(this._domRender_proxy, this._domRender_config);
const renderEnd = performance.now();
renderResult = rawSets;
if (rawSets && rawSets.raws.length > 0) {
await this.render(rawSets.raws);
}
}
} else {
removeRawSets.push(it);
}
const t = it.findNearThis(this._domRender_proxy);
if (isOnRawSetRendered(t)) {
await t.onRawSetRendered(it, { path: fullPathStr, value: ObjectUtils.Script.evaluateReturn(`this.${fullPathStr}`, this._domRender_proxy), root: this._domRender_proxy, renderResult });
}
}
// domrender_ref로 찾는걸로 바꿈
// public findRootDomRenderProxy(): DomRenderProxy<any> {
// let current:DomRenderProxy<any> = this;
// while (current.parentProxy) {
// current = current.parentProxy;
// }
// return current;
// }
root(pathInfos, value, lastDoneExecute = true) {
const rootStartTime = Date.now();
const pathKey = pathInfos.flat().map((i) => i.path).join(".");
const fullPaths = [];
if (this._domRender_ref.size > 0) {
this._domRender_ref.forEach((it, key) => {
if (DomRenderProxyMetaKey._DomRender_isProxy in key) {
it.forEach((sit) => {
try {
const recursiveStartTime = Date.now();
const concat = pathInfos.concat([[{ path: sit, obj: key }]]);
const items = key._DomRender_proxy?.root(
concat,
value,
lastDoneExecute
);
fullPaths.push(items.flat());
} catch (e) {
}
});
}
});
} else {
const pathProcessStartTime = Date.now();
const shortPaths = pathInfos.reverse().map((it) => it.map((it2) => it2.path).join(","));
const fullPathStr = shortPaths.map((it) => isNaN(Number(it)) ? "." + it : `[${it}]`).join("").slice(1);
if (lastDoneExecute) {
const iterable = this._rawSets.get(fullPathStr);
const front = shortPaths.slice(0, shortPaths.length - 1).map((it) => isNaN(Number(it)) ? "." + it : `[${it}]`).join("");
const lastPropertyName = shortPaths[shortPaths.length - 1];
const path = "this" + front;
const data = ObjectUtils.Script.evaluateReturn(ObjectUtils.Path.toOptionalChainPath(path), this._domRender_proxy);
const promiseStartTime = Date.now();
new Promise(async (resolve) => {
const promiseInnerStartTime = Date.now();
let rData = [];
const firstPathStr = front.slice(1);
const dictionaryCheckStartTime = Date.now();
const firstTargets = this._rawSets.get(firstPathStr);
const firstTargetDictionary = [];
firstTargets?.forEach((it) => {
const type = it.point.start?.getAttribute?.("type");
if (type === RawSetOperatorType.DR_THIS_PROPERTY) {
firstTargetDictionary.push(it);
}
});
if (firstTargetDictionary.length > 0) {
const rawSets = [];
let skip = false;
firstTargetDictionary.forEach((it) => {
const startElement = it.point.start;
const keys = startElement.getAttribute(RawSet.DR_HAS_KEYS_OPTIONNAME)?.split(",") ?? [];
if (value === void 0) {
const rawSet = it.getHasRawSet(lastPropertyName);
rawSet?.remove();
startElement.setAttribute(
RawSet.DR_HAS_KEYS_OPTIONNAME,
keys.filter((it2) => it2 !== lastPropertyName).join(",")
);
}
if (!keys.includes(lastPropertyName)) {
const raws = DrThisProperty.append(
this._domRender_proxy,
fullPathStr,
lastPropertyName,
it,
this._domRender_config
);
if (raws) {
rawSets.push(...raws);
}
} else {
skip = true;
}
});
if (skip === false || rawSets.length > 0) {
rData = await this.render(rawSets);
}
}
const renderStartTime = Date.now();
if (lastPropertyName === "length" && Array.isArray(data)) {
const aIterable = this._rawSets.get(firstPathStr);
if (aIterable) {
rData = await this.render(Array.from(aIterable));
}
} else if (iterable) {
rData = await this.render(Array.from(iterable), fullPathStr);
}
const eventProcessStartTime = Date.now();
const Node = this._domRender_config.window.Node;
this._targets.forEach((it) => {
if (it.nodeType === Node.DOCUMENT_FRAGMENT_NODE || it.nodeType === Node.ELEMENT_NODE) {
const findAttrStartTime = Date.now();
const targets = this._domRender_config.eventManager.findAttrElements(
it,
this._domRender_config
);
const targetCount = targets?.size || targets?.length || 0;
const changeVarStartTime = Date.now();
this._domRender_config.eventManager.changeVar(
this._domRender_proxy,
targets,
`this.${fullPathStr}`,
this._domRender_config
);
}
});
if (rData?.length > 0 && isOnChildRenderedByProperty(data)) {
const propertyValue = ObjectUtils.Script.evaluateReturn(`this.${lastPropertyName}`, data);
data.onChildRenderedByProperty(lastPropertyName, propertyValue);
}
resolve(rData);
}).then((it) => {
});
}
fullPaths.push([{ path: fullPathStr, obj: this._domRender_proxy }]);
}
return fullPaths;
}
set(target, p, value, receiver) {
if (typeof p === "string" && p !== DomRenderComponentMetaKey.DOMRENDER_COMPONENTS_KEY && excludeGetSetPropertys.includes(p) || Reflect.getMetadata(DomRenderNoProxyKey, target, p)) {
target[p] = value;
return true;
}
let start = performance.now();
if (typeof p === "string") {
value = this.proxy(receiver, value, p);
}
if (value instanceof ComponentSet && target[p] instanceof ComponentSet) {
value.config.beforeComponentSet = target[p];
}
target[p] = value;
let fullPathInfo = [];
if (typeof p === "string") {
fullPathInfo = this.root([[{ path: p, obj: this._domRender_proxy }]], value);
}
const rootElement = this._domRender_config.rootElement ?? this._domRender_config.window.document;
DocumentUtils.querySelectorAllByAttributeName(rootElement, EventManager.normalAttrMapAttrName)?.forEach(
(elementInfo) => {
const targets = new Map(
ObjectUtils.Script.evaluateReturn(elementInfo.value)
);
Array.from(targets.entries()).filter(ValidUtils.isDefined).flatMap(([key, valueScript]) => {
return fullPathInfo.flat().filter((it) => valueScript.variablePaths?.some((vit) => vit.inner.includes(`this.${it.path}`))).flatMap((it) => ({ key, valueScript, pathInfo: it }));
}).forEach((it) => {
const variablePaths = it.valueScript.variablePaths;
let targetScript = it.valueScript.originalAttrValue;
let script;
if (it.valueScript.isStringTemplate) {
variablePaths.forEach((it2) => {
let r = ObjectUtils.Path.toOptionalChainPath(it2.inner);
targetScript = targetScript.replaceAll(it2.origin, `\${${r}}`);
});
script = "`" + targetScript + "`";
} else {
variablePaths.forEach((it2) => {
let r = ObjectUtils.Path.toOptionalChainPath(it2.inner);
targetScript = targetScript.replaceAll(it2.origin, `${r}`);
});
script = targetScript;
}
const value1 = ObjectUtils.Script.evaluateReturn(script, it.pathInfo.obj);
if (value1 === null || value1 === void 0) {
elementInfo.element.removeAttribute(it.key);
} else {
elementInfo.element.setAttribute(it.key, value1);
elementInfo.element[it.key] = value1;
}
});
}
);
DocumentUtils.querySelectorAllByAttributeName(rootElement, EventManager.linkTargetMapAttrName)?.forEach(
(elementInfo) => {
const targets = new Map(ObjectUtils.Script.evaluateReturn(elementInfo.value));
Array.from(targets.entries()).filter(([key, value2]) => value2.trim()).filter(ValidUtils.isDefined).flatMap(([key, valueScript]) => {
return fullPathInfo.flat().filter((it) => valueScript.includes(`this.${it.path}`)).flatMap((it) => ({ key, valueScript, pathInfo: it }));
}).forEach((info) => {
const value1 = ObjectUtils.Script.evaluateReturn(info.valueScript, info.pathInfo.obj);
const applyAttributeName = EventManager.linkAttrs.find((it) => it.name === info.key);
if (applyAttributeName) {
if (value1 === null) {
elementInfo.element.removeAttribute(applyAttributeName.property);
} else {
elementInfo.element.setAttribute(applyAttributeName.property, value1);
}
}
});
Array.from(targets.entries()).filter(([key, value2]) => value2.trim()).filter(ValidUtils.isDefined).flatMap(([key, valueScript]) => {
return fullPathInfo.flat().filter((it) => valueScript.includes(`this.${it.path}`)).flatMap((it) => ({ key, valueScript, pathInfo: it }));
}).forEach((info) => {
const linkInfo = EventManager.linkAttrs.find((it) => it.name === info.key);
if (linkInfo) {
elementInfo.element[linkInfo.property] = value;
}
});
}
);
if (DomRenderProxyMetaKey.onBeforeReturnSet in receiver && typeof p === "string" && !(this._domRender_config.proxyExcludeOnBeforeReturnSets ?? []).concat(excludeGetSetPropertys).includes(p)) {
if (isOnBeforeReturnSet(receiver)) {
receiver.onBeforeReturnSet?.(
p,
value,
fullPathInfo.map((it) => it.map((it2) => it2.path).join())
);
}
}
return true;
}
get(target, p, receiver) {
if (p === DomRenderProxyMetaKey._DomRender_origin || p === DomRenderProxyMetaKey._domRender_origin) {
return this._domRender_origin;
} else if (p === DomRenderProxyMetaKey._DomRender_ref || p === DomRenderProxyMetaKey._domRender_ref) {
return this._domRender_ref;
} else if (p === DomRenderProxyMetaKey._DomRender_config || p === DomRenderProxyMetaKey._domRender_config) {
return this._domRender_config;
} else if (p === DomRenderProxyMetaKey._DomRender_proxy || p === DomRenderProxyMetaKey._domRender_proxy) {
return this;
} else {
let it = target[p];
if (it && typeof it === "object" && DomRenderProxyMetaKey._DomRender_isProxy in it && Object.prototype.toString.call(it._DomRender_origin) === "[object Date]") {
it = it._DomRender_origin;
}
if (DomRenderProxyMetaKey.onBeforeReturnGet in receiver && typeof p === "string" && !(this._domRender_config.proxyExcludeOnBeforeReturnGets ?? []).concat(excludeGetSetPropertys).includes(p)) {
receiver?.onBeforeReturnGet?.(
p,
it,
this.root([[{ path: p, obj: this._domRender_proxy }]], it, false)
);
}
return it;
}
}
deleteProperty(target, p) {
delete target[p];
if (typeof p === "string") {
this.root([[{ path: p, obj: this._domRender_proxy }]]);
}
return true;
}
has(target, p) {
return p === DomRenderProxyMetaKey._DomRender_isProxy || p in target;
}
proxy(parentProxy, obj, p) {
try {
const isObject = typeof obj === "object";
const isNoProxy = isObject && isDomRenderNoProxy(obj, p);
const isExclude = this._domRender_config.proxyExcludeTyps?.some((it) => obj instanceof it) ?? false;
const isFinal = isObject && DomRenderProxy.isFinal(obj);
const isFrozen = Object.isFrozen(obj);
const isShield = obj instanceof Shield;
if (obj === void 0 || obj === null || isNoProxy || isExclude || ValidUtils.isNullOrUndefined(obj) || !isObject || isFinal || isFrozen || isShield) {
return obj;
}
} catch (e) {
}
if (!isWrapProxyDomRenderProxy(obj)) {
const domRender = new DomRenderProxy(obj, void 0, this._domRender_config);
domRender.addRef(parentProxy, p);
const proxy = new Proxy(obj, domRender);
domRender.run(proxy);
return proxy;
}
if (isWrapProxyDomRenderProxy(obj)) {
const d = obj._DomRender_proxy;
d.addRef(this._domRender_proxy, p);
return obj;
} else {
return obj;
}
}
addRef(parent, path) {
if (!this._domRender_ref.get(parent)) {
this._domRender_ref.set(parent, /* @__PURE__ */ new Set());
}
this._domRender_ref.get(parent)?.add(path);
}
addRawSetAndRender(path, rawSet) {
this.addRawSet(path, rawSet);
this.render([rawSet]);
}
addRawSet(path, rawSet) {
if (!this._rawSets.get(path)) {
this._rawSets.set(path, /* @__PURE__ */ new Set());
}
this._rawSets.get(path)?.add(rawSet);
}
// public removeRawSet(...raws: RawSet[]) {
// this._rawSets.forEach(it => {
// raws.forEach(sit => it.delete(sit));
// })
// this.garbageRawSet();
// }
removeRawSet(...raws) {
this._rawSets.forEach((it) => {
it.forEach((sit) => {
if (!sit.isConnected) {
it.delete(sit);
} else if (raws.includes(sit)) {
it.delete(sit);
}
});
});
this.targetGarbageRawSet();
}
// 특정 경로로 시작하는 모든 child RawSet들을 제거
// 예: path가 'users'이면 'users[0]', 'users[0].imgUrl', 'users.length' 등 모두 제거
removeChildRawSetsByPath(parentPath) {
const pathsToRemove = [];
this._rawSets.forEach((rawSetSet, path) => {
if (path === parentPath || path.startsWith(parentPath + "[") || path.startsWith(parentPath + ".")) {
pathsToRemove.push(path);
}
});
if (pathsToRemove.length > 0) {
pathsToRemove.forEach((path) => {
const rawSetSet = this._rawSets.get(path);
if (rawSetSet) {
rawSetSet.forEach((rawSet) => {
rawSet.remove?.();
});
this._rawSets.delete(path);
}
});
}
}
targetGarbageRawSet() {
this._targets.forEach((it) => {
if (!it.isConnected) {
this._targets.delete(it);
}
});
}
garbageRawSet() {
this._targets.forEach((it) => {
if (!it.isConnected) {
this._targets.delete(it);
}
});
this._rawSets.forEach((it) => {
it.forEach((sit) => {
if (!sit.isConnected) {
it.delete(sit);
}
});
});
}
}
export {
DomRenderProxy,
getDomRenderConfig,
getDomRenderOriginObject,
getDomRenderProxy,
isWrapProxyDomRenderProxy
};
//# sourceMappingURL=DomRenderProxy.js.map