@dooboostore/dom-render
Version:
html view template engine
946 lines (882 loc) • 96.4 kB
text/typescript
import { RandomUtils } from '@dooboostore/core';
import { StringUtils } from '@dooboostore/core';
import { EventManager, NormalAttrDataType } from '../events/EventManager';
import type { DomRenderConfig } from '../configs/DomRenderConfig';
import { Range } from '@dooboostore/core';
import { ElementUtils } from '@dooboostore/core-web';
import { ComponentSet } from '../components/ComponentSet';
import { DrPre } from '../operators/DrPre';
import { Dr } from '../operators/Dr';
import { DrIf } from '../operators/DrIf';
import { ExecuteState, OperatorExecuter } from '../operators/OperatorExecuter';
import { DrThis } from '../operators/DrThis';
import { DrForm } from '../operators/DrForm';
import { DrInnerText } from '../operators/DrInnerText';
import { DrInnerHTML } from '../operators/DrInnerHTML';
import { DrFor } from '../operators/DrFor';
import { DrForOf } from '../operators/DrForOf';
import { DrAppender } from '../operators/DrAppender';
import { DrRepeat } from '../operators/DrRepeat';
import { DrTargetElement } from '../operators/DrTargetElement';
import { DrTargetAttr } from '../operators/DrTargetAttr';
import { DrStripElement } from '../operators/DrStripElement';
import { TargetElement } from '../configs/TargetElement';
import { TargetAttr } from '../configs/TargetAttr';
import { DestroyOptionType } from './DestroyOptionType';
import { Attrs } from './Attrs';
import { AttrInitCallBack } from './AttrInitCallBack';
import { ElementInitCallBack } from './ElementInitCallBack';
import { RawSetType } from './RawSetType';
import { Render } from './Render';
import { DrThisProperty } from '../operators/DrThisProperty';
import { RawSetOperatorType } from './RawSetOperatorType';
import { CssParser, CssRule, isStyleRule, isAtRule } from '@dooboostore/core';
import { isOnCreateRenderData, OnCreateRenderDataParams } from '../lifecycle/OnCreateRenderData';
import { isOnCreateRender } from '../lifecycle/OnCreateRender';
import { isOnChangeAttrRender } from '../lifecycle/OnChangeAttrRender';
import { isOnInitRender } from '../lifecycle/OnInitRender';
import { isOnCreatedThisChild } from '../lifecycle/OnCreatedThisChild';
import { isOnDestroyRender, OnDestroyRenderParams } from '../lifecycle/OnDestroyRender';
import { DrTargetElementIsElement } from '../operators/DrTargetElementIsElement';
import { ObjectUtils } from '@dooboostore/core';
import { NodeUtils } from '@dooboostore/core-web';
import { ConvertUtils } from '@dooboostore/core';
import { DomRenderComponentMetaKey } from './index';
export type RenderResult = { raws: RawSet[]; executedOperators: OperatorExecuter[] };
// let renderCnt = 0;
export class RawSet {
public static readonly DR_NAME = 'dr';
public static readonly DR_IF_NAME = 'dr-if';
public static readonly DR_FOR_NAME = 'dr-for';
public static readonly DR_FOR_OF_NAME = 'dr-for-of';
public static readonly DR_THIS_PROPERTY_NAME = 'dr-this-property';
public static readonly DR_REPEAT_NAME = 'dr-repeat';
public static readonly DR_THIS_NAME = 'dr-this';
public static readonly DR_FORM_NAME = 'dr-form';
public static readonly DR_PRE_NAME = 'dr-pre';
public static readonly DR_APPENDER_NAME = 'dr-appender';
public static readonly DR_INNERHTML_NAME = 'dr-inner-html';
public static readonly DR_INNERTEXT_NAME = 'dr-inner-text';
public static readonly DR_DETECT_NAME = 'dr-detect';
public static readonly DR_STRIP_NAME = 'dr-strip';
public static readonly DR_REPLACE_TARGET_ELEMENT_IS_NAME = 'dr-replace-target-element-is';
// elementName
public static readonly DR_TARGET_ELEMENT_ELEMENTNAME = 'dr-target-element';
// 감지처리되는 옵션
public static readonly DR_DETECT_IF_OPTIONNAME = 'dr-detect-option-if';
public static readonly DR_DETECT_FILTER_OPTIONNAME = 'dr-detect-option-filter';
public static readonly DR_DETECT_ATTR_OPTIONNAME = 'dr-detect-option-attr';
/**
* @deprecated
*/
public static readonly DR_THIS_OPTIONNAME = 'dr-option-this';
public static readonly DR_ATTR_OPTIONNAME = 'dr-option-attr';
public static readonly DR_IF_OPTIONNAME = 'dr-option-if';
public static readonly DR_IT_OPTIONNAME = 'dr-option-it';
public static readonly DR_VAR_OPTIONNAME = 'dr-option-var';
public static readonly DR_AFTER_OPTIONNAME = 'dr-option-after';
public static readonly DR_NEXT_OPTIONNAME = 'dr-option-next';
public static readonly DR_BEFORE_OPTIONNAME = 'dr-option-before';
public static readonly DR_COMPLETE_OPTIONNAME = 'dr-option-complete';
public static readonly DR_STRIP_OPTIONNAME = 'dr-option-strip';
public static readonly DR_DESTROY_OPTIONNAME = 'dr-option-destroy';
public static readonly DR_THIS_NAME_OPTIONNAME = 'dr-option-this-name';
public static readonly DR_VARIABLE_NAME_OPTIONNAME = 'dr-option-variable-name';
public static readonly DR_ITEM_VARIABLE_NAME_OPTIONNAME = 'dr-option-item-variable-name';
public static readonly DR_ITEM_VARIABLE_NAME_OPTIONNAME_DEFAULT = 'it';
public static readonly DR_ITEM_INDEX_VARIABLE_NAME_OPTIONNAME = 'dr-option-item-index-variable-name';
public static readonly DR_ITEM_OFFSET_INDEX_VARIABLE_NAME_OPTIONNAME = 'dr-option-item-offset-index-variable-name';
public static readonly DR_ITEM_INDEX_OFFSET_OPTIONNAME = 'dr-option-item-index-offset';
public static readonly DR_ITEM_INDEX_VARIABLE_NAME_OPTIONNAME_DEFAULT = 'index';
public static readonly DR_ITEM_OFFSET_INDEX_VARIABLE_NAME_OPTIONNAME_DEFAULT = 'offsetIndex';
public static readonly DR_INNER_HTML_NAME_OPTIONNAME = 'dr-option-inner-html-name';
public static readonly DR_INNER_TEXT_NAME_OPTIONNAME = 'dr-option-inner-text-name';
public static readonly DR_INNER_HTML_ESCAPED_NAME_OPTIONNAME = 'dr-option-inner-html-escaped-name';
public static readonly DR_KEY_OPTIONNAME = 'dr-option-key';
public static readonly DR_HAS_KEYS_OPTIONNAME = 'dr-option-has-keys';
public static readonly DR_ON_CREATE_ARGUMENTS_OPTIONNAME = 'dr-on-create:arguments';
public static readonly DR_ON_CREATED_CALLBACK_OPTIONNAME = 'dr-on-create:callback';
public static readonly DR_ON_CONSTRUCTOR_ARGUMENTS_OPTIONNAME = 'dr-on-constructor:arguments';
public static readonly drAttrsOriginName: Attrs = {
dr: RawSet.DR_NAME,
drIf: RawSet.DR_IF_NAME,
drFor: RawSet.DR_FOR_NAME,
drForOf: RawSet.DR_FOR_OF_NAME,
drThisProperty: RawSet.DR_THIS_PROPERTY_NAME,
drAppender: RawSet.DR_APPENDER_NAME,
drRepeat: RawSet.DR_REPEAT_NAME,
drThis: RawSet.DR_THIS_NAME,
drForm: RawSet.DR_FORM_NAME,
drPre: RawSet.DR_PRE_NAME,
drInnerHTML: RawSet.DR_INNERHTML_NAME,
drInnerText: RawSet.DR_INNERTEXT_NAME,
drItOption: RawSet.DR_IT_OPTIONNAME,
drVarOption: RawSet.DR_VAR_OPTIONNAME,
drAfterOption: RawSet.DR_AFTER_OPTIONNAME,
drNextOption: RawSet.DR_NEXT_OPTIONNAME,
drBeforeOption: RawSet.DR_BEFORE_OPTIONNAME,
drCompleteOption: RawSet.DR_COMPLETE_OPTIONNAME,
drStripElement: RawSet.DR_STRIP_NAME,
drReplaceTargetElementIs: RawSet.DR_REPLACE_TARGET_ELEMENT_IS_NAME,
drStripOption: RawSet.DR_STRIP_OPTIONNAME,
drIfOption: RawSet.DR_IF_OPTIONNAME,
drDetectIfOption: RawSet.DR_DETECT_IF_OPTIONNAME,
drDestroyOption: RawSet.DR_DESTROY_OPTIONNAME,
drHasKeysOption: RawSet.DR_HAS_KEYS_OPTIONNAME,
drKeyOption: RawSet.DR_KEY_OPTIONNAME,
drItemIndexOffsetOption: RawSet.DR_ITEM_INDEX_OFFSET_OPTIONNAME,
drVariableNameOption: RawSet.DR_VARIABLE_NAME_OPTIONNAME,
drItemVariableNameOption: RawSet.DR_ITEM_VARIABLE_NAME_OPTIONNAME,
drItemIndexVariableNameOption: RawSet.DR_ITEM_INDEX_VARIABLE_NAME_OPTIONNAME,
drItemOffsetIndexVariableNameOption: RawSet.DR_ITEM_OFFSET_INDEX_VARIABLE_NAME_OPTIONNAME
};
public static readonly DR_TAGS = [];
// public static readonly DR_REPLACE_ACTION_ATTRIBUTES = [
// RawSet.DR_REPLACE_TARGET_ELEMENT_IS_NAME
// ]
public static readonly DR_ELEMENTS = [RawSet.DR_TARGET_ELEMENT_ELEMENTNAME];
public static readonly DR_ATTRIBUTES = [
RawSet.DR_NAME,
RawSet.DR_APPENDER_NAME,
RawSet.DR_IF_NAME,
RawSet.DR_FOR_OF_NAME,
RawSet.DR_THIS_PROPERTY_NAME,
RawSet.DR_FOR_NAME,
RawSet.DR_THIS_NAME,
RawSet.DR_FORM_NAME,
RawSet.DR_PRE_NAME,
RawSet.DR_INNERHTML_NAME,
RawSet.DR_INNERTEXT_NAME,
RawSet.DR_REPEAT_NAME,
// RawSet.DR_DETECT_NAME, // TODO: DrDetect 오퍼레이터 구현 필요
RawSet.DR_STRIP_NAME
] as const;
constructor(
public uuid: string,
public type: RawSetType,
public point: {
start: Comment | Text | HTMLMetaElement;
node: Node;
end: Comment | Text | HTMLMetaElement;
innerHTML?: string;
// thisVariableName?: string | null,
parent?: Node | null;
parentRawSet?: RawSet;
childrenRawSets?: RawSet[];
},
public dataSet: {
config: DomRenderConfig;
fragment: DocumentFragment;
render?: Render;
},
public detect?: { action: Function }
) {
// point.start.rawSet = this;
// console.log('rawset constructor->', (this.point.node as Element).getAttributeNames());
}
get isConnected() {
// console.log('isConnect???', this, this.point.start.isConnected, this.point.end.isConnected);
return this.point && this.point.start && this.point.end && this.point.start.isConnected && this.point.end.isConnected;
}
// 중요
getUsingTriggerVariables(config?: DomRenderConfig): Set<string> {
const usingTriggerVariables = new Set<string>();
// console.log('------data', this.dataSet)
this.dataSet.fragment.childNodes.forEach((cNode, key) => {
// console.log('-----aaaa-data', cNode, key)
let script = '';
if (cNode.nodeType === Node.TEXT_NODE) {
script = `\`${(cNode as Text).textContent ?? ''}\``;
const expressionGroups = RawSet.expressionGroups(script);
// console.log('???????', script, expressionGroups)
if (expressionGroups[0].length > 1) {
script = expressionGroups[0][1] as string;
}
// console.log('--', expressionGroups, script)
} else if (cNode.nodeType === Node.ELEMENT_NODE) {
const element = cNode as Element;
const targetAttrNames = (config?.targetAttrs?.map(it => it.name) ?? []).concat(RawSet.DR_ATTRIBUTES, RawSet.DR_DETECT_FILTER_OPTIONNAME, RawSet.DR_DETECT_IF_OPTIONNAME, RawSet.DR_DETECT_ATTR_OPTIONNAME); // .concat(EventManager.normalAttrMapAttrName);
const targetScripts = targetAttrNames
.map(it => element.getAttribute(it))
.filter(it => it)
.map(it => `(${it})`);
const targetAttrMap = element.getAttribute(EventManager.normalAttrMapAttrName);
// console.log('targetAttrMap-->', targetAttrMap)
if (targetAttrMap) {
new Map<string, NormalAttrDataType>(JSON.parse(targetAttrMap)).forEach((v, k) => {
const variablePaths = v.variablePaths;
// let targetScript = v.originalAttrValue;
// variablePaths.forEach(it => {
// let r = ObjectUtils.Path.toOptionalChainPath(it.inner);
// targetScript = targetScript.replaceAll(it.origin,`\${${r}}`);
// })
// targetScripts.push(`(${targetScript})`);
// 변수 체크만 하는거니깐 이렇게
variablePaths.forEach(it => targetScripts.push(`(${it.inner})`));
// console.log('targetScripts', targetScripts);
});
}
// console.log('targetScriptstargetScripts', targetScripts)
script = targetScripts.join(';');
// script = targetScripts.map(it=>`try{${it}}catch(e){}`).join(';');
// attribute쪽 체크하는거 추가
// console.log('----!!!!!-->', targetAttrNames)
// const otherAttrs = element.getAttributeNames()
// .filter(it => !targetAttrNames.includes(it.toLowerCase()) && RawSet.isExporesion(element.getAttribute(it)))
// .map(it => {
// return `\`${element.getAttribute(it) ?? ''}\``;
// }).join(';');
// script += ';' + otherAttrs
}
if (script) {
// console.log('------1-',script)
script = script.replace(/#[^#]*#/g, '({})');
// console.log('------2-',script)
// script = script.replace('}$','}');
// script = script.replace('@this@','this');
// console.log('----------->', script)
// TODO: 훔.. 꼭필요한가..? 트리거될때 스크립트변수 까지 감지해야될까?
EventManager.VARNAMES.forEach(it => {
// script = script.replace(RegExp(it.replace('$', '\\$'), 'g'), `this?.___${it}`);
// script = script.replace(RegExp(it.replace('$', '\\$'), 'g'), `this.___${it}`);
script = script.replace(RegExp(it.replace('$', '\\$'), 'g'), `this.___${it}`);
// console.log('scripts-->', script)
});
// console.log('--------1--', script);
// script = script.replaceAll('#{','${').replaceAll('}#', '}')
// console.log('--------2--', script);
// TODO: 훔.. 꼭필요한가..? 트리거될때 스크립트변수 까지 감지해야될까?
Array.from(ObjectUtils.Path.detectPathFromScript(script, { excludeThis: true }))
.filter(it => !it.startsWith(`___${EventManager.SCRIPTS_VARNAME}`))
.forEach(it => usingTriggerVariables.add(it));
}
});
// console.log('usingTriggerVariable----------->', usingTriggerVariables)
return usingTriggerVariables;
}
// 중요 render 처리 부분
// public async render(obj: any, config: DomRenderConfig): Promise<RawSet[]> {
// }
private lastRenderedValue: string | undefined;
public async render(obj: any, config: DomRenderConfig): Promise<RenderResult> {
// console.log('render!!!!!!!!!!!!!!')
// renderCnt++;
// const t0 = performance.now();
// console.group('RawSet render', this);
// Dirty Checking Optimization for Text Nodes
if (this.dataSet.fragment.childNodes.length === 1 && this.dataSet.fragment.childNodes[0].nodeType === Node.TEXT_NODE) {
const textContent = this.dataSet.fragment.childNodes[0].textContent;
if (textContent) {
const runText = RawSet.expressionGroups(textContent)[0][1];
const __render = {
rawSet: this,
scripts: EventManager.setBindProperty(config?.scripts, obj),
router: config?.router,
range: Range.range,
element: this.dataSet.fragment.childNodes[0],
attribute: {},
rootObject: obj,
scriptUtils: ObjectUtils.Script,
nearThis: this.findNearThis(obj),
parentThis: this.findParentThis(obj),
bindScript: ` /** render **/
const ${EventManager.SCRIPTS_VARNAME} = this.__render.scripts;
const ${EventManager.RAWSET_VARNAME} = this.__render.rawSet;
const ${EventManager.ELEMENT_VARNAME} = this.__render.element;
const ${EventManager.ATTRIBUTE_VARNAME} = this.__render.attribute;
const ${EventManager.RANGE_VARNAME} = this.__render.range;
const ${EventManager.ROUTER_VARNAME} = this.__render.router;
const ${EventManager.NEAR_THIS_VARNAME} = this.__render.nearThis;
const ${EventManager.PARENT_THIS_VARNAME} = this.__render.parentThis;
const ${EventManager.SCRIPT_UTILS_VARNAME} = this.__render.scriptUtils;
const ${EventManager.ROOT_OBJECT_VARNAME} = this.__render.rootObject;
`
};
try {
const newValue = ObjectUtils.Script.evaluateReturn(
{
bodyScript: __render.bindScript,
returnScript: runText
},
Object.assign(obj, { __render })
);
const newValueStr = String(newValue);
if (this.lastRenderedValue === newValueStr) {
// console.log('✓ Skipping render: Value unchanged', newValue);
return { raws: [], executedOperators: [] };
}
// console.log('✗ Rendering: Value changed', this.lastRenderedValue, '->', newValue);
// Update lastRenderedValue BEFORE proceeding with render
// This ensures the value is cached even if render fails partway through
this.lastRenderedValue = newValueStr;
} catch (e) {
// Evaluation failed, proceed with normal render
// console.log('⚠ Dirty check failed, proceeding with render', e);
}
}
}
const genNode = config.window.document.importNode(this.dataSet.fragment, true);
const raws: RawSet[] = [];
const onAttrInitCallBacks: AttrInitCallBack[] = [];
const onElementInitCallBacks: ElementInitCallBack[] = [];
const onThisComponentSetCallBacks: ComponentSet[] = [];
const drAttrs: Attrs[] = [];
// console.log('rawSet render!!', obj, config, this, Array.from(genNode.childNodes.values()));
const executedOperators: OperatorExecuter[] = [];
for (const cNode of Array.from(genNode.childNodes.values())) {
// console.log('cNodecNodecNode', cNode);
let attribute = {};
if (cNode.nodeType === Node.ELEMENT_NODE) {
attribute = ElementUtils.getAttributeToObject(cNode as Element);
}
const __render = Object.freeze({
rawSet: this,
scripts: EventManager.setBindProperty(config?.scripts, obj),
router: config?.router,
range: Range.range,
element: cNode,
attribute: attribute,
rootObject: obj,
scriptUtils: ObjectUtils.Script,
nearThis: this.findNearThis(obj),
parentThis: this.findParentThis(obj),
bindScript: ` /** render **/
const ${EventManager.SCRIPTS_VARNAME} = this.__render.scripts;
const ${EventManager.RAWSET_VARNAME} = this.__render.rawSet;
const ${EventManager.ELEMENT_VARNAME} = this.__render.element;
const ${EventManager.ATTRIBUTE_VARNAME} = this.__render.attribute;
const ${EventManager.RANGE_VARNAME} = this.__render.range;
const ${EventManager.ROUTER_VARNAME} = this.__render.router;
const ${EventManager.NEAR_THIS_VARNAME} = this.__render.nearThis;
const ${EventManager.PARENT_THIS_VARNAME} = this.__render.parentThis;
const ${EventManager.SCRIPT_UTILS_VARNAME} = this.__render.scriptUtils;
const ${EventManager.ROOT_OBJECT_VARNAME} = this.__render.rootObject;
`
}) as unknown as Render;
// const normalAttribute = attribute[EventManager.normalAttrMapAttrName];
// if (normalAttribute) {
// new Map<string, string>(JSON.parse(normalAttribute)).forEach((v, k) => {
// const cval = ScriptUtils.evalReturn({ bodyScript: __render.bindScript, returnScript: v }, Object.assign(obj, { __render: __render }));
// console.log('-----------',v,k, cval);
// attribute[v] = cval;
// });
// }
const fag = config.window.document.createDocumentFragment();
if (cNode.nodeType === Node.TEXT_NODE && cNode.textContent) {
// console.log('text-->', this, obj, config, cNode.textContent);
// console.log('text-->', Array.from(this.fragment.childNodes))
const textContent = cNode.textContent;
const runText = RawSet.expressionGroups(textContent)[0][1];
// console.log('--->', RawSet.exporesionGrouops(textContent), textContent,runText, runText[0][1])
let newNode: Node;
if (textContent?.startsWith('#')) {
const r = ObjectUtils.Script.evaluateReturn({ bodyScript: __render.bindScript, returnScript: runText }, Object.assign(obj, { __render }));
const template = config.window.document.createElement('template') as HTMLTemplateElement;
template.innerHTML = r;
newNode = template.content;
} else {
const r = ObjectUtils.Script.evaluateReturn({ bodyScript: __render.bindScript, returnScript: runText }, Object.assign(obj, { __render }));
newNode = config.window.document.createTextNode(r);
}
cNode.parentNode?.replaceChild(newNode, cNode);
// console.log('-------', this.point.start.parentNode.nodeName)
// 중요 style value change 됐을때 다시 처리해야되기떄문에: 마지막에 completed 없는 attr 가지고 판단 하니깐
if (this.type === RawSetType.STYLE_TEXT && this.point.parent) {
(this.point.parent as Element).removeAttribute('completed');
}
} else if (cNode.nodeType === Node.ELEMENT_NODE) {
const element = cNode as Element;
// console.log('target-->', element)
const drAttr: Attrs = {
dr: this.getAttributeAndDelete(element, RawSet.DR_NAME),
drIf: this.getAttributeAndDelete(element, RawSet.DR_IF_NAME),
drFor: this.getAttributeAndDelete(element, RawSet.DR_FOR_NAME),
drForOf: this.getAttributeAndDelete(element, RawSet.DR_FOR_OF_NAME),
drThisProperty: this.getAttributeAndDelete(element, RawSet.DR_THIS_PROPERTY_NAME),
drAppender: this.getAttributeAndDelete(element, RawSet.DR_APPENDER_NAME),
drRepeat: this.getAttributeAndDelete(element, RawSet.DR_REPEAT_NAME),
drThis: this.getAttributeAndDelete(element, RawSet.DR_THIS_NAME),
drForm: this.getAttributeAndDelete(element, RawSet.DR_FORM_NAME),
drPre: this.getAttributeAndDelete(element, RawSet.DR_PRE_NAME),
drInnerHTML: this.getAttributeAndDelete(element, RawSet.DR_INNERHTML_NAME),
drInnerText: this.getAttributeAndDelete(element, RawSet.DR_INNERTEXT_NAME),
drStripElement: this.getAttributeAndDelete(element, RawSet.DR_STRIP_NAME),
drReplaceTargetElementIs: this.getAttributeAndDelete(element, RawSet.DR_REPLACE_TARGET_ELEMENT_IS_NAME),
drItOption: this.getAttributeAndDelete(element, RawSet.DR_IT_OPTIONNAME),
drIfOption: this.getAttributeAndDelete(element, RawSet.DR_IF_OPTIONNAME),
drVarOption: this.getAttributeAndDelete(element, RawSet.DR_VAR_OPTIONNAME),
drNextOption: this.getAttributeAndDelete(element, RawSet.DR_NEXT_OPTIONNAME),
drAfterOption: this.getAttributeAndDelete(element, RawSet.DR_AFTER_OPTIONNAME),
drBeforeOption: this.getAttributeAndDelete(element, RawSet.DR_BEFORE_OPTIONNAME),
drCompleteOption: this.getAttributeAndDelete(element, RawSet.DR_COMPLETE_OPTIONNAME),
drStripOption: this.getAttributeAndDelete(element, RawSet.DR_STRIP_OPTIONNAME),
drDestroyOption: this.getAttributeAndDelete(element, RawSet.DR_DESTROY_OPTIONNAME),
drKeyOption: this.getAttributeAndDelete(element, RawSet.DR_KEY_OPTIONNAME),
drDetectIfOption: this.getAttribute(element, RawSet.DR_DETECT_IF_OPTIONNAME),
drHasKeysOption: this.getAttribute(element, RawSet.DR_HAS_KEYS_OPTIONNAME),
drItemIndexOffsetOption: this.getAttributeAndDelete(element, RawSet.DR_ITEM_INDEX_OFFSET_OPTIONNAME),
drVariableNameOption: this.getAttributeAndDelete(element, RawSet.DR_VARIABLE_NAME_OPTIONNAME),
drItemVariableNameOption: this.getAttributeAndDelete(element, RawSet.DR_ITEM_VARIABLE_NAME_OPTIONNAME),
drItemIndexVariableNameOption: this.getAttributeAndDelete(element, RawSet.DR_ITEM_INDEX_VARIABLE_NAME_OPTIONNAME),
drItemOffsetIndexVariableNameOption: this.getAttributeAndDelete(element, RawSet.DR_ITEM_OFFSET_INDEX_VARIABLE_NAME_OPTIONNAME)
};
drAttrs.push(drAttr);
// 아래 순서 중요
const operators = [
new DrPre(this, __render, { raws, fag }, { element, attrName: RawSet.DR_PRE_NAME, attr: drAttr.drPre, attrs: drAttr }, { config, obj, operatorAround: config.operatorAround?.drPre }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
new Dr(this, __render, { raws, fag }, { element, attrName: RawSet.DR_NAME, attr: drAttr.dr, attrs: drAttr }, { config, obj, operatorAround: config.operatorAround?.dr }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
// new Dr(this, __render, {raws, fag}, {element, attrName: EventManager.onRenderedInitAttrName, attr: drAttr.dr, attrs: drAttr}, {config, obj, operatorAround: config.operatorAround?.dr}, {onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks}),
new DrIf(this, __render, { raws, fag }, { element, attrName: RawSet.DR_IF_NAME, attr: drAttr.drIf, attrs: drAttr }, { config, obj, operatorAround: config.operatorAround?.drIf }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
new DrStripElement(this, __render, { raws, fag }, { element, attrName: RawSet.DR_STRIP_NAME, attr: drAttr.drStripElement, attrs: drAttr }, { config, obj, operatorAround: config.operatorAround?.drThis }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
new DrThis(this, __render, { raws, fag }, { element, attrName: RawSet.DR_THIS_NAME, attr: drAttr.drThis, attrs: drAttr }, { config, obj, operatorAround: config.operatorAround?.drThis }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
new DrForm(this, __render, { raws, fag }, { element, attrName: RawSet.DR_FOR_NAME, attr: drAttr.drForm, attrs: drAttr }, { config, obj, operatorAround: config.operatorAround?.drForm }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
new DrInnerText(this, __render, { raws, fag }, { element, attrName: RawSet.DR_INNERTEXT_NAME, attr: drAttr.drInnerText, attrs: drAttr }, { config, obj, operatorAround: config.operatorAround?.drInnerText }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
new DrInnerHTML(this, __render, { raws, fag }, { element, attrName: RawSet.DR_INNERHTML_NAME, attr: drAttr.drInnerHTML, attrs: drAttr }, { config, obj, operatorAround: config.operatorAround?.drInnerHTML }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
new DrFor(this, __render, { raws, fag }, { element, attrName: RawSet.DR_FOR_NAME, attr: drAttr.drFor, attrs: drAttr }, { config, obj, operatorAround: config.operatorAround?.drFor }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
new DrForOf(this, __render, { raws, fag }, { element, attrName: RawSet.DR_FOR_OF_NAME, attr: drAttr.drForOf, attrs: drAttr }, { config, obj, operatorAround: config.operatorAround?.drForOf }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
new DrThisProperty(this, __render, { raws, fag }, { element, attrName: RawSet.DR_THIS_PROPERTY_NAME, attr: drAttr.drThisProperty, attrs: drAttr }, { config, obj, operatorAround: config.operatorAround?.drThisProperty }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
new DrAppender(this, __render, { raws, fag }, { element, attrName: RawSet.DR_APPENDER_NAME, attr: drAttr.drAppender, attrs: drAttr }, { config, obj, operatorAround: config.operatorAround?.drAppender }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
new DrRepeat(this, __render, { raws, fag }, { element, attrName: RawSet.DR_REPEAT_NAME, attr: drAttr.drRepeat, attrs: drAttr }, { config, obj, operatorAround: config.operatorAround?.drRepeat }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
new DrTargetElementIsElement(this, __render, { raws, fag }, { element, attrName: RawSet.DR_REPLACE_TARGET_ELEMENT_IS_NAME, attr: drAttr.drReplaceTargetElementIs, attrs: drAttr }, { config, obj }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
new DrTargetElement(this, __render, { raws, fag }, { element, attrs: drAttr }, { config, obj }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks }),
new DrTargetAttr(this, __render, { raws, fag }, { element, attrs: drAttr }, { config, obj }, { onAttrInitCallBacks, onElementInitCallBacks, onThisComponentSetCallBacks })
];
for (const operator of operators) {
const state = await operator.start();
if (state === ExecuteState.EXECUTE) {
executedOperators.push(operator);
break;
} else if (state === ExecuteState.STOP) {
return { raws, executedOperators };
}
}
}
}
this.point.childrenRawSets = raws;
raws.forEach(it => {
it.point.parentRawSet = this;
});
// console.log('pppppppppppppp', this);
// replaceBody와 applyEvent 순서를 바꿨다 20250511
const childrenNodes = Array.from(genNode.childNodes);
// console.log(`RawSet aa took ${performance.now() - t0} milliseconds. children`, childrenNodes);
this.applyEvent(obj, genNode, config);
// console.log(`RawSet xxxxxxxxxx took ${performance.now() - t0} milliseconds.`);
this.replaceBody(genNode); // 중요 여기서 마지막에 연션된 값을 그려준다.
// console.log(`RawSet replaceBody took ${performance.now() - t0} milliseconds.`);
// console.log('rawSEt!!!!!!!', obj, this.near)
// console.log('rawSEt!!!!!!!', obj, this.findNearThis(obj),childrenNodes,config)
// console.log('rawSEt!!!!!!!', obj, drAttrs)
this.onRenderedEvent(obj, childrenNodes, config);
// console.log(`RawSet onRenderedEvent took ${performance.now() - t0} milliseconds.`);
drAttrs.forEach(it => {
if (it.drCompleteOption) {
// genNode.childNodes
const render = Object.freeze({
rawSet: this,
fag: genNode,
scripts: EventManager.setBindProperty(config?.scripts, obj)
} as Render);
ObjectUtils.Script.evaluate(
`
const ${EventManager.FAG_VARNAME} = this.__render.fag;
const ${EventManager.SCRIPTS_VARNAME} = this.__render.scripts;
const ${EventManager.RAWSET_VARNAME} = this.__render.rawSet;
${it.drCompleteOption}`,
Object.assign(obj, { __render: render })
);
}
});
// console.log(`RawSet drAttrs.forEach took ${performance.now() - t0} milliseconds.`);
// 중요 style isolation 나중에 :scope로 대체 가능할듯.
// 2023.9.4일 없앰 style 처음들어올때 처리하는걸로 바꿈
// RawSet.generateStyleSheetsLocal(config);
for (const it of onThisComponentSetCallBacks) {
if (isOnInitRender(it.obj)) {
await it.obj?.onInitRender?.({}, this);
}
}
// console.log(`RawSet const it of onThisComponentSetCallBacks took ${performance.now() - t0} milliseconds.`);
for (const it of onElementInitCallBacks) {
if (isOnInitRender(this.dataSet.render?.currentThis)) {
// TODO: 나중에 파라미터 들어가야될듯. 지금은 리팩토링하느라 빠짐
// console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!', it, this.dataSet);
// dr-on-init:arguments
// if (isOnInitRender()) {
// it.targetElement.__render.component.onInitRender?.(...param);
// }
// if (it.targetElement?.__render?.element && it.targetElement?.__render?.component) {
// const oninit = it.targetElement.__render.element.getAttribute(RawSet.DR_ON_INIT_ARGUMENTS_OPTIONNAME); // dr-on-component-init
// let param = [];
// if (oninit) {
// const script = `${it.targetElement.__render.renderScript} return ${oninit} `;
// param = ScriptUtils.eval(script, Object.assign(obj, {
// __render: it.targetElement.__render
// }));
// if (!Array.isArray(param)) {
// param = [param];
// }
// }
//
// if (isOnInitRender(it.targetElement.__render.component)) {
// it.targetElement.__render.component.onInitRender?.(...param);
// }
// }
await this.dataSet.render.currentThis.onInitRender({}, this);
}
config?.onElementInit?.(it.name, obj, this, it.targetElement);
}
// console.log(`RawSet for (const it of onElementInitCallBacks) { took ${performance.now() - t0} milliseconds.`);
// TODO: 이부분도 위에 targetElement 처럼 해야될까?
for (const it of onAttrInitCallBacks) {
config?.onAttrInit?.(it.attrName, it.attrValue, obj, this);
}
// console.log(`RawSet for (const it of onAttrInitCallBacks) { took ${performance.now() - t0} milliseconds.`);
// component destroy
if (obj[DomRenderComponentMetaKey.DOMRENDER_COMPONENTS_KEY]) {
Object.entries(obj[DomRenderComponentMetaKey.DOMRENDER_COMPONENTS_KEY]).forEach(([key, value]) => {
const rawSet = (value as any).__rawSet as RawSet;
const drAttrs: Attrs | undefined = rawSet?.dataSet.render?.attribute;
if (rawSet && !rawSet.isConnected) {
const destroyOptions = drAttrs?.drDestroyOption?.split(',') ?? [];
RawSet.destroy(obj[DomRenderComponentMetaKey.DOMRENDER_COMPONENTS_KEY][key], { rawSet: rawSet }, config, destroyOptions);
delete obj[DomRenderComponentMetaKey.DOMRENDER_COMPONENTS_KEY][key];
}
});
}
// console.log(`RawSet if (obj.__domrender_components) { took ${performance.now() - t0} milliseconds.`);
// console.log('-------raws',raws)
// console.log(`RawSet render took ${performance.now() - t0} milliseconds. END!!`, renderCnt);
console.groupEnd();
return { raws: raws, executedOperators: executedOperators };
}
// 중요 스타일 적용 부분
private static generateRuleSelector(start: string, end: string, sit: string) {
const selectorText = `:is(${start} ~ *:not(${start} ~ ${end} ~ *))`;
if (sit.startsWith('## ')) {
return sit.replace(/^## /, '');
} else if (sit.startsWith('.')) {
return `${selectorText}${sit}, ${selectorText} ${sit}`;
} else if (sit.includes('::')) {
const reg = sit.match(/(.*)::(.*)$/);
const first = reg?.[1];
const extracted = reg?.[2];
const divText = `${start} ~ ${first}:not(${start} ~ ${end} ~ *)`;
return `${selectorText} ${first}, ${divText}::${extracted}`;
} else {
const divText = `${start} ~ ${sit}:not(${start} ~ ${end} ~ *)`;
return `${selectorText} ${sit}, ${divText}`;
}
}
public static generateStyleTransform(styleBody: string | string[], componentKey: string, styleTagWrap = true) {
// console.log('style!!!!!!!!!!!!!!!!1', styleBody, componentKey, styleTagWrap);
if (Array.isArray(styleBody)) {
styleBody = styleBody.join('\n');
}
styleBody = styleBody.replaceAll('#uuid#', componentKey);
const { cleanedCss, importRules } = RawSet.extractCssImports(styleBody);
styleBody = cleanedCss;
const start = `#${componentKey}-start`;
const end = `#${componentKey}-end`;
// 이거뭐하는지 까먹었네 뭐였지??..
// 동적으로 처리하는것같은데.. css 에서는 동적으로 처리하는걸 지향 해야될것같은데 이기능 deprecated 할지 고민중
// const before = StringUtils.regexExecArrayReplace(styleBody, /(\$\{.*?\}\$)/g, (data) => {
// // console.log('동적?', `var(--domrender-${data[0]})`)
// return `var(--domrender-${data[0]})`;
// });
// const before = styleBody;
// console.log('before@@@@@@@@@@@', before)
const cssParser = new CssParser(styleBody);
const processRule = (rule: CssRule) => {
if (isStyleRule(rule)) {
const isRoot = rule.selector.includes(':root');
if (!isRoot) {
// Handle comma-separated selectors
const selectors = rule.selector.split(',').map(s => s.trim());
const transformedSelectors = selectors.map(selector => this.generateRuleSelector(start, end, selector));
rule.selector = transformedSelectors.join(', ');
}
} else if (isAtRule(rule)) {
if (rule.name === 'media') {
const hasRoot = rule.children.some(childRule => isStyleRule(childRule) && childRule.selector.includes(':root'));
if (!hasRoot) {
rule.children.forEach(childRule => {
if (isStyleRule(childRule)) {
const selectors = childRule.selector.split(',').map(s => s.trim());
const transformedSelectors = selectors.map(selector => this.generateRuleSelector(start, end, selector));
childRule.selector = transformedSelectors.join(', ');
}
});
}
}
}
};
cssParser.rules.forEach(processRule);
let after = cssParser.stringify();
// let after = StringUtils.regexExecArrayReplace(stringify as string, /(var\(--domrender-(\$\{.*?\}\$)?\))/g, (data) => {
// return data[2];
// });
// console.log('after@@@@@@@@@@@', after)
// 여기서 선언 selector 자체에도 처리가능하도록 한다 20250518
after = after.replaceAll('/*$', '${');
after = after.replaceAll('$*/', '}$');
if (importRules.length) {
after = `${importRules.join('\n')}\n${after}`;
}
if (styleTagWrap) {
styleBody = `<style id='${componentKey}-style' domstyle>${after}</style>`;
}
// console.log('style!!!!!!!!!!!!!!2', styleBody);
return styleBody;
}
private static extractCssImports(styleBody: string) {
const importRules: string[] = [];
let result = '';
let i = 0;
while (i < styleBody.length) {
const importIndex = styleBody.indexOf('@import', i);
if (importIndex === -1) {
result += styleBody.slice(i);
break;
}
result += styleBody.slice(i, importIndex);
let j = importIndex + '@import'.length;
let inSingleQuote = false;
let inDoubleQuote = false;
let parenDepth = 0;
while (j < styleBody.length) {
const ch = styleBody[j];
if (ch === '\\') {
j += 2;
continue;
}
if (!inDoubleQuote && ch === "'") {
inSingleQuote = !inSingleQuote;
} else if (!inSingleQuote && ch === '"') {
inDoubleQuote = !inDoubleQuote;
} else if (!inSingleQuote && !inDoubleQuote) {
if (ch === '(') {
parenDepth += 1;
} else if (ch === ')' && parenDepth > 0) {
parenDepth -= 1;
} else if (ch === ';' && parenDepth === 0) {
j += 1;
break;
}
}
j += 1;
}
const importRule = styleBody.slice(importIndex, j).trim();
if (importRule) {
importRules.push(importRule);
}
i = j;
}
return { cleanedCss: result, importRules };
}
public applyEvent(obj: any, fragment = this.dataSet.fragment, config?: DomRenderConfig) {
// const t0 = performance.now();
this.dataSet.config.eventManager.applyEvent(obj, this.dataSet.config.eventManager.findAttrElements(fragment, config), config);
// console.log(`RawSet applyEvent took ${performance.now() - t0} milliseconds.`);
}
public onRenderedEvent(obj: any, nodes: Node[], config?: DomRenderConfig) {
this.dataSet.config.eventManager.onRenderedEvent(obj, nodes, config);
}
public getAttribute(element: Element, attr: string) {
const data = element.getAttribute(attr);
return data;
}
public getAttributeAndDelete(element: Element, attr: string) {
const data = element.getAttribute(attr);
// if (data)
// element.setAttribute(`origin-${attr}`, data);
element.removeAttribute(attr);
return data;
}
public getDrAppendAttributeAndDelete(element: Element, obj: any) {
let data = element.getAttribute(RawSet.DR_APPENDER_NAME);
// if (data && !/\[[0-9]+\]/g.test(data)) {
if (data && !/\[.+\]/g.test(data)) {
const currentIndex = ObjectUtils.Script.evaluateReturn(`${ObjectUtils.Path.toOptionalChainPath(data)}?.length -1`, obj);
// console.log('------?', currentIndex)
// if (currentIndex === undefined || isNaN(currentIndex)) {
// return undefined;
// }
// const currentIndex = ScriptUtils.evalReturn(`${data}.length`, obj);
data = `${data}[${currentIndex}]`;
element.setAttribute(RawSet.DR_APPENDER_NAME, data);
// element.setAttribute(RawSet.DR_IF_NAME, data);
// element.setAttribute('dr-id', data);
// console.log('-->', element)
}
// if (data && !/\.childs\[[0-9]+\]/g.test(data)) {
// const currentIndex = ScriptUtils.evalReturn(`${data}.currentIndex`, obj);
// data = `${data}.childs[${currentIndex}]`;
// element.setAttribute(RawSet.DR_APPENDER_NAME, data)
// }
element.removeAttribute(RawSet.DR_APPENDER_NAME);
return data;
}
public replaceBody(genNode: Node) {
this.childAllRemove();
this.point.start.parentNode?.insertBefore(genNode, this.point.start.nextSibling); // 중요 start checkpoint 다음인 end checkpoint 앞에 넣는다. 즉 중간 껴넣기 (나중에 meta tag로 변경을 해도될듯하긴한데..)
}
// 중요 important
public static checkPointCreates(element: Node, obj: any, config: DomRenderConfig): RawSet[] {
// console.log('!@@@@@@@@@@@@@@@@', obj);
// const NodeFilter = (config.window as any).NodeFilter;
// const thisVariableName = (element as any).__domrender_this_variable_name;
// let html = element instanceof DocumentFragment ? ElementUtils.toInnerHTML(element, {document: config.window.document}) : (element as HTMLElement).outerHTML;
// console.log('==============html-->', element, html);
const NodeFilter = (config.window as any).NodeFilter;
const Node = (config.window as any).Node;
const processedNodes = new Set<Node>();
const findNodes = NodeUtils.findNodes(element, node => {
// console.log('nodeType', node.nodeType, node, (node as any).tagName, (node as any).data);
// for (const processedNode of processedNodes) {
// console.log('pre check!!', Array.from(processedNodes),processedNode !== node , processedNode.contains(node));
// // if(processedNode.contains(node)){
// console.log('---prechecker--html', (processedNode as HTMLElement).outerHTML,'------', (node as HTMLElement).outerHTML);
// // }
// if (processedNode !== node && processedNode.contains(node)) {
// console.log('acceptNodeReturn contain', NodeFilter.FILTER_REJECT);
// return NodeFilter.FILTER_REJECT;
// }
// }
if (node.nodeType === Node.TEXT_NODE) {
// console.log('nodeText--->', node.textContent)
// console.log('????????', node.parentElement, node.parentElement?.getAttribute('dr-pre'));
// console.log('???????/',node.textContent, node.parentElement?.getAttribute('dr-pre'))
// TODO: 나중에
// const between = StringUtils.betweenRegexpStr('[$#]\\{', '\\}', StringUtils.deleteEnter((node as Text).data ?? ''))
const between = RawSet.expressionGroups(StringUtils.deleteEnter((node as Text).data ?? ''));
// console.log('bbbb', between)
const r = between?.length > 0 ? NodeUtils.FindNodesFilterResult.MATCH_AND_SKIP_CHILDREN : NodeUtils.FindNodesFilterResult.NO_MATCH_AND_SKIP_CHILDREN;
// console.log('acceptNodeReturn node', r);
return r;
// return /\$\{.*?\}/g.test(StringUtils.deleteEnter((node as Text).data ?? '')) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
// return /[$#]\{.*?\}/g.test(StringUtils.deleteEnter((node as Text).data ?? '')) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
} else if (node.nodeType === Node.ELEMENT_NODE) {
const element = node as Element;
// if (typeof Window === 'undefined' && element.getAttribute('ttt') === 'visual') {
// return NodeFilter.FILTER_REJECT;
// }
// console.log('nodeHTML-->', element.outerHTML);
if (element.hasAttribute(RawSet.DR_PRE_NAME)) {
let r = NodeUtils.FindNodesFilterResult.NO_MATCH_AND_SKIP_CHILDREN;
// console.log('acceptNodeReturn', r);
return r;
}
if (element.hasAttribute(EventManager.attrAttrName)) {
// console.log('elementAttrAttrName-->', element.outerHTML);
const script = element.getAttribute(EventManager.attrAttrName) ?? '';
// console.log('scriptscriptscriptscriptscriptscript,', script)
// const keyValuePairs = Array.from(script.matchAll(/['"]?(\w+)['"]?:\s*([^,}]+)/g))
const keyValuePairs = Array.from(script.matchAll(/['"]?([\w-_]+)['"]?:\s*([^,}]+)/g)).map(match => ({
key: match[1],
value: match[2]
}));
(keyValuePairs ?? []).forEach(it => {
element.setAttribute(it.key, '${' + it.value + '}$');
});
}
// element.setAttribute('dr-event-click', 'console.log(11)');
const targetElementIs = element.getAttribute(RawSet.DR_REPLACE_TARGET_ELEMENT_IS_NAME);
const targetElementNames = config.targetElements?.map(it => it.name.toLowerCase()) ?? [];
targetElementNames.push(...RawSet.DR_ELEMENTS);
const isElement = targetElementNames.includes(element.tagName.toLowerCase()) || targetElementNames.includes(targetElementIs?.toLowerCase() as any);
// if (isElement) {
// (element as HTMLElement).style.display = 'none';
// (element as HTMLElement).style.width = '100px';
// (element as HTMLElement).style.height = '100px';
// console.log((element as HTMLElement).outerHTML)
// }
// console.log('------targetElementIstargetElementIs>', targetElementNames, '---', isElement, targetElementIs);
// const targetAttrNames = (config.targetAttrs?.map(it => it.name) ?? []).concat([...RawSet.DR_ATTRIBUTES,...EventManager.RAWSET_CHECK_ATTRIBUTE]);
const targetAttrNames = (config.targetAttrs?.map(it => it.name) ?? []).concat([...RawSet.DR_ATTRIBUTES]);
const normalAttrs = new Map<string, NormalAttrDataType>();
const linkVariables = new Map<string, string>();
const linkNames = EventManager.linkAttrs.map(it => it.name);
const isAttr =
element
.getAttributeNames()
.filter(it => !EventManager.noDetectAttr.includes(it))
.filter(it => {
const value = element.getAttribute(it)?.trim();
// link일때
if (value && linkNames.includes(it)) {
linkVariables.set(it, value);
} else if (value && RawSet.isExpression(value)) {
// 표현식있을떄
const variablePaths: { origin: string; inner: string }[] = RawSet.expressionGroups(value).map(it => ({
origin: it[0].trim(),
inner: it[1].trim()
}));
const isStringTemplate = variablePaths.length > 1 || (variablePaths[0] && variablePaths[0].origin !== value);
// normal Attribute 초반에 셋팅해주기.
// TODO: 이거 하긴했는데 사이드 이팩트?
// let variablePath: string = RawSet.expressionGroups(value)[0][1];
// const originVariable = variablePath;
// variablePath = variablePath.replace(/#[^#]*#/g, '({})');
// const optionalChainPath = ObjectUtils.Path.toOptionalChainPath(variablePath);
// const cval = ObjectUtils.Script.evaluateReturn(optionalChainPath, Object.assign(obj));
let targetScript = value;
variablePaths.forEach(it => {
let r = it.inner.replace(/#[^#]*#/g, '({})');
r = ObjectUtils.Path.toOptionalChainPath(r);
targetScript = targetScript.replaceAll(it.origin, `\${${r}}`);
});
const cval = ObjectUtils.Script.evaluateReturn('`' + targetScript + '`', Object.assign(obj));
if (cval === null) {
element.removeAttribute(it);
/* TODO: 여기 더 추가되어야될듯
TODO: 훔..... 없어도될듯?
*
<select dr-event-change="@this@.onMarketChange($event)" class="market-select">
<option>마켓 선택 (${@this@.markets?.length??0}$개)</option>
<option dr-for-of="@this@.markets" dr-option-item-variable-name="market" value="${#market#.uuid}$" dr-attr="{selected: #market#.uuid === @this@.marketUUID ? 'selected' : null}">
${#market#.name}$
</option>
</select>
*/
// } else if (
// !(element.hasAttribute(RawSet.DR_VARIABLE_NAME_OPTIONNAME) || element.hasAttribute(RawSet.DR_ITEM_VARIABLE_NAME_OPTIONNAME) || element.hasAttribute(RawSet.DR_ITEM_INDEX_VARIABLE_NAME_OPTIONNAME))
// ) {
// element.setAttribute(it, cval);
} else {
element.setAttribute(it, cval);
}
normalAttrs.set(it, { originalAttrValue: value, variablePaths: variablePaths, isStringTemplate });
// normalAttrs.set(it, originVariable);
// console.log('normalAttribute', it, variablePath);
}
// console.log(element.getAttribute(it), attrExpresion);
const isTargetAttr = targetAttrNames.includes(it.toLowerCase());
return isTargetAttr;
}).length > 0;
if (linkVariables.size) {
element.setAttribute(EventManager.linkTargetMapAttrName, JSON.stringify(Array.from(linkVariables.entries())));
}
// 기본 attribute를 처리하기위해
if (normalAttrs.size) {
element.setAttribute(EventManager.normalAttrMapAttrName, JSON.stringify(Array.from(normalAttrs.entries())));
}
// if (isElement) {
// element.setAttribute('www', '@this@');
// }
const r = isAttr || isElement ? NodeUtils.FindNodesFilterResult.MATCH_AND_SKIP_CHILDREN : NodeUtils.FindNodesFilterResult.NO_MATCH_AND_CONTINUE;
// console.log('acceptNodeReturn element', r);
return r;
}
const r = NodeUtils.FindNodesFilterResult.NO_MATCH_AND_CONTINUE;
// console.log('acceptNodeReturn nothing', r);
return r;
});
const pars: RawSet[] = [];
let currentNode: Node | null;
const name = RandomUtils.uuid();
// console.log('name==', name);
// eslint-disable-next-line no-cond-assign
// while ((currentNode = nodeIterator?.nextNode())) {
// console.log('findNodes~~', findNodes);
for (const currentNode of