// src/util.ts function stringToElement(template) { const parser = new DOMParser(); const doc = parser.parseFromString(template, "text/html"); return doc.body.firstChild; } var isText = (node) => { return node.nodeType === Node.TEXT_NODE; }; var isTemplate = (node) => { return node.nodeName === "TEMPLATE"; }; var isElement = (node) => { return node.nodeType === Node.ELEMENT_NODE; }; function isObject(value) { return value !== null && typeof value === "object"; } function isArray(value) { return Array.isArray(value); } function checkAndRemoveAttribute(el, attrName) { const attributeValue = el.getAttribute(attrName); if (attributeValue !== null) { el.removeAttribute(attrName); } return attributeValue; } function findSlotNodes(element) { const slots = []; const findSlots = (node) => { Array.from(node.childNodes).forEach((node2) => { if (isElement(node2)) { if (node2.nodeName === "SLOT") { slots.push({ node: node2, name: node2.getAttribute("name") || "default" }); } if (node2.hasChildNodes()) { findSlots(node2); } } }); }; findSlots(element); return slots; } function findTemplateNodes(element) { const templates = []; const findTemplates = (element2) => { let defaultContentNodes = []; Array.from(element2.childNodes).forEach((node) => { if (isElement(node) || isText(node)) { if (isElement(node) && node.nodeName === "TEMPLATE" && isTemplate(node)) { templates.push({ targetSlotName: node.getAttribute("slot") || "", node }); } else { defaultContentNodes.push(node); } } }); if (defaultContentNodes.length > 0) { const defaultTemplate = document.createElement("template"); defaultTemplate.setAttribute("slot", "default"); defaultContentNodes.forEach((node) => { defaultTemplate.content.appendChild(node); }); templates.push({ targetSlotName: "default", node: defaultTemplate }); } }; findTemplates(element); return templates; } var nextTick = async (f) => { await new Promise( (r) => setTimeout( (_) => requestAnimationFrame((_2) => { f && f(); r(); }) ) ); }; function html(strings, ...values) { const selfClosingTags = ["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]; let result = strings.reduce((acc, str, i) => acc + str + (values[i] || ""), ""); result = result.replace(/<([a-zA-Z][^\s/>]*)\s*([^>]*?)\/>/g, (match, tagName, attributes) => { if (selfClosingTags.includes(tagName.toLowerCase())) { return match; } return `<${tagName} ${attributes}>${tagName}>`; }); return result; } function toDisplayString(value) { return value == null ? "" : isObject(value) ? JSON.stringify(value, null, 2) : String(value); } function insertAfter(newNode, existingNode) { if (existingNode.nextSibling) { existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling); } else { existingNode?.parentNode?.appendChild(newNode); } } function isPropAttribute(attrName) { if (attrName.startsWith(".")) { return true; } if (attrName.startsWith("{") && attrName.endsWith("}")) { return true; } return false; } function isSpreadProp(attr) { return attr.startsWith("..."); } function isMirrorProp(attr) { return attr.startsWith("{") && attr.endsWith("}"); } function isRegularProp(attr) { return attr.startsWith("."); } function isEventAttribute(attrName) { return attrName.startsWith("@"); } function componentHasPropByName(name, component) { return Object.keys(component?.props ?? {}).some((prop) => prop === name); } function extractAttributeName(attrName) { return attrName.replace(/^\.\.\./, "").replace(/^\./, "").replace(/^{/, "").replace(/}$/, "").replace(/:bind$/, ""); } function dashToCamel(str) { return str.toLowerCase().replace(/-([a-z])/g, (g) => g[1].toUpperCase()); } function extractPropName(attrName) { return dashToCamel(extractAttributeName(attrName)); } function classNames(_) { const classes = []; for (let i = 0; i < arguments.length; i++) { const arg = arguments[i]; if (!arg) continue; const argType = typeof arg; if (argType === "string" || argType === "number") { classes.push(arg); } else if (Array.isArray(arg)) { if (arg.length) { const inner = classNames.apply(null, arg); if (inner) { classes.push(inner); } } } else if (argType === "object") { if (arg.toString === Object.prototype.toString) { for (let key in arg) { if (Object.hasOwnProperty.call(arg, key) && arg[key]) { classes.push(key); } } } else { classes.push(arg.toString()); } } } return classes.join(" "); } // src/directives/attribute.ts var AttributeDirective = class { element; context; expression; attr; extractedAttributeName; previousClasses = []; previousStyles = {}; is = { sameNameProperty: false, bound: false, spread: false, componentProp: false }; constructor({ element, context, attr }) { this.element = element; this.context = context; this.expression = attr.value; this.attr = attr; this.extractedAttributeName = extractAttributeName(attr.name); this.is = { sameNameProperty: attr.name.startsWith("{") && attr.name.endsWith("}"), bound: attr.name.includes(":bind"), spread: attr.name.startsWith("..."), componentProp: false }; if (this.is.sameNameProperty) { this.expression = this.extractedAttributeName; } if (this.is.spread) { this.expression = this.extractedAttributeName; } element.removeAttribute(attr.name); if (this.is.bound) { context.effect(this.update.bind(this)); } else { this.update(); } } update() { let value = evalGet(this.context.scope, this.expression); if (this.is.spread && typeof value === "object") { for (const [key, val] of Object.entries(value)) { this.element.setAttribute(key, String(val)); } } else if ((typeof value === "object" || Array.isArray(value)) && this.extractedAttributeName === "class") { value = classNames(value); const next = value.split(" "); const diff = next.filter((c) => !this.previousClasses.includes(c)).filter(Boolean); const rm = this.previousClasses.filter((c) => !next.includes(c)); diff.forEach((c) => { this.previousClasses.push(c); this.element.classList.add(c); }); rm.forEach((c) => { this.previousClasses = this.previousClasses.filter((addedClass) => addedClass !== c); this.element.classList.remove(c); }); } else if (typeof value === "object" && this.extractedAttributeName === "style") { const next = Object.keys(value); const rm = Object.keys(this.previousStyles).filter((style) => !next.includes(style)); next.forEach((style) => { this.previousStyles[style] = value[style]; this.element.style[style] = value[style]; }); rm.forEach((style) => { this.previousStyles[style] = ""; this.element.style[style] = ""; }); this.previousStyles = value; } else { this.element.setAttribute(this.extractedAttributeName, value); } } }; // src/directives/event.ts var EventDirective = class { element; context; expression; attr; eventCount = 0; constructor({ element, context, attr }) { this.element = element; this.context = context; this.expression = attr.value; this.attr = attr; const eventName = attr.name.replace(/^@/, ""); const parts = eventName.split("."); this.element.addEventListener(parts[0], (event) => { if (parts.includes("prevent")) event.preventDefault(); if (parts.includes("stop")) event.stopPropagation(); if (parts.includes("once") && this.eventCount > 0) return; this.eventCount++; const handler = evalGet(context.scope, attr.value); if (typeof handler === "function") { handler(event); } }); element.removeAttribute(attr.name); } }; // src/directives/for.ts var forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/; var forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/; var stripParensRE = /^\(|\)$/g; var destructureRE = /^[{[]\s*((?:[\w_$]+\s*,?\s*)+)[\]}]$/; var _for = (el, exp, ctx, component, componentProps, allProps) => { const inMatch = exp.match(forAliasRE); if (!inMatch) { console.warn(`invalid :for expression: ${exp}`); return; } const nextNode = el.nextSibling; const parent = el.parentElement; const anchor = new Text(""); parent.insertBefore(anchor, el); parent.removeChild(el); const sourceExp = inMatch[2].trim(); let valueExp = inMatch[1].trim().replace(stripParensRE, "").trim(); let destructureBindings; let isArrayDestructure = false; let indexExp; let objIndexExp; let keyAttr = "key"; let keyExp = el.getAttribute(keyAttr) || el.getAttribute(keyAttr = ":key") || el.getAttribute(keyAttr = ":key:bind"); if (keyExp) { el.removeAttribute(keyAttr); if (keyAttr === "key") keyExp = JSON.stringify(keyExp); } let match; if (match = valueExp.match(forIteratorRE)) { valueExp = valueExp.replace(forIteratorRE, "").trim(); indexExp = match[1].trim(); if (match[2]) { objIndexExp = match[2].trim(); } } if (match = valueExp.match(destructureRE)) { destructureBindings = match[1].split(",").map((s) => s.trim()); isArrayDestructure = valueExp[0] === "["; } let mounted = false; let blocks; let childCtxs; let keyToIndexMap; const createChildContexts = (source) => { const map = /* @__PURE__ */ new Map(); const ctxs = []; if (isArray(source)) { for (let i = 0; i < source.length; i++) { ctxs.push(createChildContext(map, source[i], i)); } } else if (typeof source === "number") { for (let i = 0; i < source; i++) { ctxs.push(createChildContext(map, i + 1, i)); } } else if (isObject(source)) { let i = 0; for (const key in source) { ctxs.push(createChildContext(map, source[key], i++, key)); } } return [ctxs, map]; }; const createChildContext = (map, value, index, objKey) => { const data = {}; if (destructureBindings) { destructureBindings.forEach((b, i) => data[b] = value[isArrayDestructure ? i : b]); } else { data[valueExp] = value; } if (objKey) { indexExp && (data[indexExp] = objKey); objIndexExp && (data[objIndexExp] = index); } else { indexExp && (data[indexExp] = index); } const childCtx = createScopedContext(ctx, data); const key = keyExp ? evalGet(childCtx.scope, keyExp) : index; map.set(key, index); childCtx.key = key; return childCtx; }; const mountBlock = (ctx2, ref2) => { const block = new Block({ element: el, parentContext: ctx2, replacementType: "replace", component, componentProps, allProps }); block.key = ctx2.key; block.insert(parent, ref2); return block; }; ctx.effect(() => { const source = evalGet(ctx.scope, sourceExp); const prevKeyToIndexMap = keyToIndexMap; [childCtxs, keyToIndexMap] = createChildContexts(source); if (!mounted) { blocks = childCtxs.map((s) => mountBlock(s, anchor)); mounted = true; } else { for (let i2 = 0; i2 < blocks.length; i2++) { if (!keyToIndexMap.has(blocks[i2].key)) { blocks[i2].remove(); } } const nextBlocks = []; let i = childCtxs.length; let nextBlock; let prevMovedBlock; while (i--) { const childCtx = childCtxs[i]; const oldIndex = prevKeyToIndexMap.get(childCtx.key); let block; if (oldIndex == null) { block = mountBlock(childCtx, nextBlock ? nextBlock.element : anchor); } else { block = blocks[oldIndex]; Object.assign(block.context.scope, childCtx.scope); if (oldIndex !== i) { if (blocks[oldIndex + 1] !== nextBlock || // If the next has moved, it must move too prevMovedBlock === nextBlock) { prevMovedBlock = block; block.insert(parent, nextBlock ? nextBlock.element : anchor); } } } nextBlocks.unshift(nextBlock = block); } blocks = nextBlocks; } }); return nextNode; }; // src/directives/if.ts function _if(el, exp, ctx, component, componentProps, allProps) { const parent = el.parentElement; const anchor = new Comment(":if"); parent.insertBefore(anchor, el); const branches = [{ exp, el }]; let elseEl; let elseExp; while (elseEl = el.nextElementSibling) { elseExp = null; if (checkAndRemoveAttribute(elseEl, ":else") === "" || (elseExp = checkAndRemoveAttribute(elseEl, ":else-if"))) { parent.removeChild(elseEl); branches.push({ exp: elseExp, el: elseEl }); } else { break; } } const nextNode = el.nextSibling; parent.removeChild(el); let block; let activeBranchIndex = -1; const removeActiveBlock = () => { if (block) { parent.insertBefore(anchor, block.element); block.remove(); block = void 0; } }; ctx.effect(() => { for (let i = 0; i < branches.length; i++) { const { exp: exp2, el: el2 } = branches[i]; if (!exp2 || evalGet(ctx.scope, exp2)) { if (i !== activeBranchIndex) { removeActiveBlock(); block = new Block({ element: el2, parentContext: ctx, replacementType: "replace", component, componentProps, allProps }); block.insert(parent, anchor); parent.removeChild(anchor); activeBranchIndex = i; } return; } } activeBranchIndex = -1; removeActiveBlock(); }); return nextNode; } // src/directives/interpolation.ts var delims = /{{\s?(.*?)\s?}}/g; var InterpolationDirective = class { element; context; textNodes = /* @__PURE__ */ new Map(); constructor({ element, context }) { this.element = element; this.context = context; this.findNodes(); this.textNodes.forEach((nodes, expression) => { const trimmedExpression = expression.slice(2, -2).trim(); nodes.forEach((node) => { const getter = (exp = trimmedExpression) => evalGet(this.context.scope, exp, node); context.effect(() => { node.textContent = toDisplayString(getter()); }); }); }); } findNodes() { const textContent = this.element.textContent.trim(); if (textContent?.match(delims)) { const textNodes = textContent.split(/(\{\{\s?[^}]+\s?\}\})/g).filter(Boolean); if (textNodes) { let previousNode = this.element; for (let i = 0; i < textNodes.length; i++) { const textNode = textNodes[i]; if (textNode.match(/\{\{\s?.+\s?\}\}/)) { const newNode = document.createTextNode(textNode); if (i === 0) { this.element.replaceWith(newNode); } else { insertAfter(newNode, previousNode); } previousNode = newNode; if (this.textNodes.has(textNode)) { this.textNodes.get(textNode).push(newNode); } else { this.textNodes.set(textNode, [newNode]); } } else { const newNode = document.createTextNode(textNodes[i]); if (i === 0) { this.element.replaceWith(newNode); } else { insertAfter(newNode, previousNode); } previousNode = newNode; } } } } } update() { } }; // src/directives/show.ts var ShowDirective = class { element; context; expression; originalDisplay; constructor({ element, context, expression }) { this.element = element; this.context = context; this.expression = expression; this.originalDisplay = getComputedStyle(this.element).display; context.effect(this.update.bind(this)); } update() { const shouldShow = Boolean(evalGet(this.context.scope, this.expression)); this.element.style.display = shouldShow ? this.originalDisplay : "none"; } }; // src/directives/teleport.ts function _teleport(el, exp, ctx) { const anchor = new Comment(":teleport"); el.replaceWith(anchor); const target = document.querySelector(exp); if (!target) { console.warn(`teleport target not found: ${exp}`); return; } nextTick(() => { target.appendChild(el); const observer = new MutationObserver((mutationsList) => { mutationsList.forEach((mutation) => { mutation.removedNodes.forEach((removedNode) => { if (removedNode.contains(anchor)) { el.remove(); observer.disconnect(); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); new Block({ element: el, parentContext: ctx }); }); return anchor; } // src/directives/value.ts function isInput(element) { return element instanceof HTMLInputElement; } function isTextarea(element) { return element instanceof HTMLTextAreaElement; } function isSelect(element) { return element instanceof HTMLSelectElement; } var ValueDirective = class { element; context; expression; inputType; constructor({ element, context, expression }) { this.element = element; this.context = context; this.expression = expression; this.inputType = element.getAttribute("type"); if (isInput(element)) { switch (this.inputType) { case "text": case "password": case "number": case "color": element.addEventListener("input", () => { const value = this.inputType === "number" ? element.value ? parseFloat(element.value) : 0 : element.value; evalSet(this.context.scope, expression, value); }); break; case "checkbox": element.addEventListener("change", (e) => { evalSet(this.context.scope, expression, !!e.currentTarget.checked); }); break; case "radio": element.addEventListener("change", (e) => { if (e.currentTarget.checked) { evalSet(this.context.scope, expression, element.getAttribute("value")); } }); break; default: break; } } if (isTextarea(element)) { element.addEventListener("input", () => { evalSet(this.context.scope, expression, element.value); }); } if (isSelect(element)) { element.addEventListener("change", () => { evalSet(this.context.scope, expression, element.value); }); } context.effect(this.updateElementValue.bind(this)); } updateElementValue() { const value = evalGet(this.context.scope, this.expression, this.element); if (isInput(this.element)) { switch (this.inputType) { case "text": case "password": case "number": case "color": this.element.value = value; break; case "checkbox": this.element.checked = !!value; break; case "radio": this.element.checked = this.element.value === value; break; default: break; } } if (isTextarea(this.element)) { this.element.value = value; } if (isSelect(this.element)) { this.element.value = value; } } }; // src/reactivity/effect.ts var targetMap = /* @__PURE__ */ new WeakMap(); var effectStack = []; function track(target, key) { const activeEffect = effectStack[effectStack.length - 1]; if (!activeEffect) return; let effectsMap = targetMap.get(target); if (!effectsMap) targetMap.set(target, effectsMap = /* @__PURE__ */ new Map()); let effects = effectsMap.get(key); if (!effects) effectsMap.set(key, effects = /* @__PURE__ */ new Set()); if (!effects.has(activeEffect)) { effects.add(activeEffect); activeEffect.refs.push(effects); } } function trigger(target, key) { const effectsMap = targetMap.get(target); if (!effectsMap) return; const scheduled = /* @__PURE__ */ new Set(); effectsMap.get(key)?.forEach((effect2) => { scheduled.add(effect2); }); scheduled.forEach(run); } function stop2(effect2) { if (effect2.active) cleanup(effect2); effect2.active = false; } function start(effect2) { if (!effect2.active) { effect2.active = true; run(effect2); } } function run(effect2) { if (!effect2.active) return; if (effectStack.includes(effect2)) return; cleanup(effect2); let val; try { effectStack.push(effect2); val = effect2.handler(); } finally { effectStack.pop(); } return val; } function cleanup(effect2) { const { refs } = effect2; if (refs.length) { for (const ref2 of refs) { ref2.delete(effect2); } } refs.length = 0; } function effect(handler, opts = {}) { const { lazy } = opts; const newEffect = { active: !lazy, handler, refs: [] }; run(newEffect); return { start: () => { start(newEffect); }, stop: () => { stop2(newEffect); }, toggle: () => { if (newEffect.active) { stop2(newEffect); } else { start(newEffect); } return newEffect.active; } }; } // src/reactivity/computed.ts var $computed = Symbol("computed"); function isComputed(value) { return isObject(value) && value[$computed]; } // src/reactivity/ref.ts var $ref = Symbol("ref"); function isRef(value) { return isObject(value) && !!value[$ref]; } function ref(value = null) { if (isObject(value)) { return isRef(value) ? value : reactive(value); } const result = { value, [$ref]: true }; return new Proxy(result, { get(target, key, receiver) { const val = Reflect.get(target, key, receiver); track(result, "value"); return val; }, set(target, key, value2) { const oldValue = target[key]; if (oldValue !== value2) { const success = Reflect.set(target, key, value2); if (success) { trigger(result, "value"); } } return true; } }); } // src/reactivity/reactive.ts var $reactive = Symbol("reactive"); function reactive(value) { if (!isObject(value)) return ref(value); if (value[$reactive]) return value; value[$reactive] = true; Object.keys(value).forEach((key) => { if (isObject(value[key])) { value[key] = reactive(value[key]); } }); return new Proxy(value, reactiveProxyHandler()); } function reactiveProxyHandler() { return { deleteProperty(target, key) { const had = Reflect.has(target, key); const result = Reflect.deleteProperty(target, key); if (had) trigger(target, key); return result; }, get(target, key) { track(target, key); return Reflect.get(target, key); }, set(target, key, value) { if (target[key] === value) return true; let newObj = false; if (isObject(value) && !isObject(target[key])) { newObj = true; } if (Reflect.set(target, key, value)) { trigger(target, key); } if (newObj) { target[key] = reactive(target[key]); } return true; } }; } // src/index.ts function provide(key, value) { if (!current.componentBlock) { console.warn("Can't provide: no current component block"); } current.componentBlock.provides.set(key, value); } function inject(key) { if (!current.componentBlock) { console.warn("Can't inject: no current component block"); } let c = current.componentBlock; while (c) { if (c.provides.has(key)) { return c.provides.get(key); } c = c.parentComponentBlock; } return void 0; } var App = class { rootBlock; registry = /* @__PURE__ */ new Map(); plugins = /* @__PURE__ */ new Set(); register(name, component) { this.registry.set(name, component); } use(plugin, ...config) { this.plugins.add(plugin); plugin.use(this, ...config); } getComponent(tag) { return this.registry.get(tag); } mount(component, target = "body", props = {}) { const root = typeof target === "string" ? document.querySelector(target) : target; const display = root.style.display; root.style.display = "none"; this.rootBlock = this._mount(component, root, props, true); root.style.display = display; return this.rootBlock; } _mount(component, target, props, isRoot = false) { const parentContext = createContext({ app: this }); if (props) { parentContext.scope = reactive(props); bindContextMethods(parentContext.scope); } parentContext.scope.$isRef = isRef; parentContext.scope.$isComputed = isComputed; const block = new Block({ element: target, parentContext, component, isRoot, componentProps: props, replacementType: "replaceChildren" }); return block; } unmount() { this.rootBlock.teardown(); } }; function createContext({ parentContext, app: app2 }) { const context = { app: app2 ? app2 : parentContext && parentContext.app ? parentContext.app : null, scope: parentContext ? parentContext.scope : reactive({}), blocks: [], effects: [], slots: [], templates: parentContext ? parentContext.templates : [], effect: (handler) => { const e = effect(handler); context.effects.push(e); return e; } }; return context; } var createScopedContext = (ctx, data = {}) => { const parentScope = ctx.scope; const mergedScope = Object.create(parentScope); Object.defineProperties(mergedScope, Object.getOwnPropertyDescriptors(data)); let proxy; proxy = reactive( new Proxy(mergedScope, { set(target, key, val, receiver) { if (receiver === proxy && !target.hasOwnProperty(key)) { return Reflect.set(parentScope, key, val); } return Reflect.set(target, key, val, receiver); } }) ); bindContextMethods(proxy); const out = { ...ctx, scope: { ...ctx.scope, ...proxy } }; return out; }; function bindContextMethods(scope) { for (const key of Object.keys(scope)) { if (typeof scope[key] === "function") { scope[key] = scope[key].bind(scope); } } } function mergeProps(props, defaultProps) { const merged = {}; Object.keys(defaultProps).forEach((defaultProp) => { const propValue = props.hasOwnProperty(defaultProp) ? props[defaultProp] : defaultProps[defaultProp]?.default; merged[defaultProp] = reactive(typeof propValue === "function" ? propValue() : propValue); }); return merged; } var current = { componentBlock: void 0 }; var Block = class { element; context; parentContext; component; provides = /* @__PURE__ */ new Map(); parentComponentBlock; componentProps; allProps; isFragment; start; end; key; constructor(opts) { this.isFragment = opts.element instanceof HTMLTemplateElement; this.parentComponentBlock = opts.parentComponentBlock; if (opts.component) { current.componentBlock = this; this.element = stringToElement(opts.component.template); } else { if (this.isFragment) { this.element = opts.element.content.cloneNode(true); } else if (typeof opts.element === "string") { this.element = stringToElement(opts.element); } else { this.element = opts.element.cloneNode(true); opts.element.replaceWith(this.element); } } if (opts.isRoot) { this.context = opts.parentContext; } else { this.parentContext = opts.parentContext ? opts.parentContext : createContext({}); this.parentContext.blocks.push(this); this.context = createContext({ parentContext: opts.parentContext }); } if (opts.component) { this.componentProps = mergeProps(opts.componentProps ?? {}, opts.component.props ?? {}); if (opts.component.main) { this.context.scope = { ...opts.component.main(this.componentProps) || {} }; } } opts.allProps?.forEach((prop) => { if (prop.isBind) { this.context.effect(() => { let newValue; if (prop.isSpread) { const spreadProps = evalGet(this.parentContext.scope, prop.extractedName); if (isObject(spreadProps)) { Object.keys(spreadProps).forEach((key) => { newValue = spreadProps[key]; this.setProp(key, newValue); }); } } else { newValue = prop.isMirror ? evalGet(this.parentContext.scope, prop.extractedName) : evalGet(this.parentContext.scope, prop.exp); this.setProp(prop.extractedName, newValue); } }); } }); this.context.slots = findSlotNodes(this.element); this.context.templates = opts.templates ?? []; this.context.slots.forEach((slot) => { const template = this.context.templates.find((t) => t.targetSlotName === slot.name); if (template) { const templateContents = template.node.content.cloneNode(true); slot.node.replaceWith(templateContents); } }); this.context.scope.$isRef = isRef; this.context.scope.$isComputed = isComputed; walk(this.element, this.context); if (opts.component) { if (opts.replacementType === "replace") { if (opts.element instanceof HTMLElement) { opts.element.replaceWith(this.element); } } else { if (opts.element instanceof HTMLElement) { opts.element.replaceChildren(this.element); } } } } setProp(name, value) { if (isRef(this.componentProps[name])) { this.componentProps[name].value = value; } else { this.componentProps[name] = value; } } insert(parent, anchor = null) { if (this.isFragment) { if (this.start) { let node = this.start; let next; while (node) { next = node.nextSibling; parent.insertBefore(node, anchor); if (node === this.end) { break; } node = next; } } else { this.start = new Text(""); this.end = new Text(""); parent.insertBefore(this.end, anchor); parent.insertBefore(this.start, this.end); parent.insertBefore(this.element, this.end); } } else { parent.insertBefore(this.element, anchor); } } remove() { if (this.parentContext) { const i = this.parentContext.blocks.indexOf(this); if (i > -1) { this.parentContext.blocks.splice(i, 1); } } if (this.start) { const parent = this.start.parentNode; let node = this.start; let next; while (node) { next = node.nextSibling; parent.removeChild(node); if (node === this.end) { break; } node = next; } } else { this.element.remove(); } this.teardown(); } teardown() { this.context.blocks.forEach((block) => { block.teardown(); }); this.context.effects.forEach(stop); } }; function isComponent(element, context) { return !!context.app.getComponent(element.tagName.toLowerCase()); } function walk(node, context) { if (isText(node)) { new InterpolationDirective({ element: node, context }); return; } if (isElement(node)) { let exp; if (isComponent(node, context)) { const component = context.app.getComponent(node.tagName.toLowerCase()); const allProps = Array.from(node.attributes).filter((attr) => isSpreadProp(attr.name) || isMirrorProp(attr.name) || isRegularProp(attr.name) && componentHasPropByName(extractPropName(attr.name), component)).map((attr) => ({ isMirror: isMirrorProp(attr.name), isSpread: isSpreadProp(attr.name), isBind: attr.name.includes("bind"), originalName: attr.name, extractedName: extractPropName(attr.name), exp: attr.value, value: isMirrorProp(attr.name) ? evalGet(context.scope, extractPropName(attr.name)) : attr.value ? evalGet(context.scope, attr.value) : void 0 })); const componentProps = allProps.reduce((acc, { isSpread, isMirror, extractedName, value }) => { if (isSpread) { const spread = evalGet(context.scope, extractedName); if (isObject(spread)) Object.assign(acc, spread); } else if (isMirror) { acc[extractedName] = evalGet(context.scope, extractedName); } else { acc[extractedName] = value; } return acc; }, {}); if (exp = checkAndRemoveAttribute(node, ":teleport")) { return _teleport(node, exp, context); } if (exp = checkAndRemoveAttribute(node, ":if")) { return _if(node, exp, context, component, componentProps, allProps); } if (exp = checkAndRemoveAttribute(node, ":for")) { return _for(node, exp, context, component, componentProps, allProps); } if (exp = checkAndRemoveAttribute(node, ":show")) { new ShowDirective({ element: node, context, expression: exp }); } const templates = findTemplateNodes(node); const block = new Block({ element: node, parentContext: context, component, replacementType: "replace", parentComponentBlock: current.componentBlock, templates, componentProps, allProps }); return block.element; } if (exp = checkAndRemoveAttribute(node, ":teleport")) { return _teleport(node, exp, context); } if (exp = checkAndRemoveAttribute(node, ":if")) { return _if(node, exp, context); } if (exp = checkAndRemoveAttribute(node, ":for")) { return _for(node, exp, context); } if (exp = checkAndRemoveAttribute(node, ":ref")) { context.scope[exp].value = node; } if (exp = checkAndRemoveAttribute(node, ":value")) { new ValueDirective({ element: node, context, expression: exp }); } if (exp = checkAndRemoveAttribute(node, ":show")) { new ShowDirective({ element: node, context, expression: exp }); } Array.from(node.attributes).forEach((attr) => { if (isPropAttribute(attr.name)) { new AttributeDirective({ element: node, context, attr }); } if (isEventAttribute(attr.name)) { new EventDirective({ element: node, context, attr }); } }); walkChildren(node, context); } } function walkChildren(node, context) { let child = node.firstChild; while (child) { child = walk(child, context) || child.nextSibling; } } var evalFuncCache = {}; function evalGet(scope, exp, el) { if (!exp.trim()) return void 0; return execute(scope, `const ___value = (${exp.trim()}); return ___value;`, el); } function evalSet(scope, exp, value) { value = typeof value === "string" ? `"${value}"` : value; return execute(scope, `const ___target = (${exp.trim()}); return $isRef(___target) ? ___target.value = ${value} : ___target = ${value};`, null, false); } function execute(scope, exp, el, flatRefs = true) { const newScope = flatRefs ? flattenRefs(scope) : scope; const fn = evalFuncCache[exp] || (evalFuncCache[exp] = toFunction(exp)); try { return fn(newScope, el); } catch (e) { console.warn(`Error evaluating expression: "${exp}":`); console.error(e); } } function toFunction(exp) { try { return new Function("$data", "$el", `with($data){${exp}}`); } catch (e) { console.error(`${e.message} in expression: ${exp}`); return () => { }; } } function flattenRefs(scope) { const mapped = {}; for (const key in scope) { if (scope.hasOwnProperty(key)) { if (isRef(scope[key])) { mapped[key] = scope[key].value; } else { mapped[key] = scope[key]; } } } return mapped; } var counter = { template: html`