// 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}>`; }); 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, this.element); 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") { console.log("value is object", value); 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, element); 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, el) : 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, el); 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, el2)) { 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, element); 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)); 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; } }; } // node_modules/path-to-regexp/dist.es2015/index.js function lexer(str) { var tokens = []; var i = 0; while (i < str.length) { var char = str[i]; if (char === "*" || char === "+" || char === "?") { tokens.push({ type: "MODIFIER", index: i, value: str[i++] }); continue; } if (char === "\\") { tokens.push({ type: "ESCAPED_CHAR", index: i++, value: str[i++] }); continue; } if (char === "{") { tokens.push({ type: "OPEN", index: i, value: str[i++] }); continue; } if (char === "}") { tokens.push({ type: "CLOSE", index: i, value: str[i++] }); continue; } if (char === ":") { var name = ""; var j = i + 1; while (j < str.length) { var code = str.charCodeAt(j); if ( // `0-9` code >= 48 && code <= 57 || // `A-Z` code >= 65 && code <= 90 || // `a-z` code >= 97 && code <= 122 || // `_` code === 95 ) { name += str[j++]; continue; } break; } if (!name) throw new TypeError("Missing parameter name at " + i); tokens.push({ type: "NAME", index: i, value: name }); i = j; continue; } if (char === "(") { var count = 1; var pattern = ""; var j = i + 1; if (str[j] === "?") { throw new TypeError('Pattern cannot start with "?" at ' + j); } while (j < str.length) { if (str[j] === "\\") { pattern += str[j++] + str[j++]; continue; } if (str[j] === ")") { count--; if (count === 0) { j++; break; } } else if (str[j] === "(") { count++; if (str[j + 1] !== "?") { throw new TypeError("Capturing groups are not allowed at " + j); } } pattern += str[j++]; } if (count) throw new TypeError("Unbalanced pattern at " + i); if (!pattern) throw new TypeError("Missing pattern at " + i); tokens.push({ type: "PATTERN", index: i, value: pattern }); i = j; continue; } tokens.push({ type: "CHAR", index: i, value: str[i++] }); } tokens.push({ type: "END", index: i, value: "" }); return tokens; } function parse(str, options) { if (options === void 0) { options = {}; } var tokens = lexer(str); var _a = options.prefixes, prefixes = _a === void 0 ? "./" : _a; var defaultPattern = "[^" + escapeString(options.delimiter || "/#?") + "]+?"; var result = []; var key = 0; var i = 0; var path = ""; var tryConsume = function(type) { if (i < tokens.length && tokens[i].type === type) return tokens[i++].value; }; var mustConsume = function(type) { var value2 = tryConsume(type); if (value2 !== void 0) return value2; var _a2 = tokens[i], nextType = _a2.type, index = _a2.index; throw new TypeError("Unexpected " + nextType + " at " + index + ", expected " + type); }; var consumeText = function() { var result2 = ""; var value2; while (value2 = tryConsume("CHAR") || tryConsume("ESCAPED_CHAR")) { result2 += value2; } return result2; }; while (i < tokens.length) { var char = tryConsume("CHAR"); var name = tryConsume("NAME"); var pattern = tryConsume("PATTERN"); if (name || pattern) { var prefix = char || ""; if (prefixes.indexOf(prefix) === -1) { path += prefix; prefix = ""; } if (path) { result.push(path); path = ""; } result.push({ name: name || key++, prefix, suffix: "", pattern: pattern || defaultPattern, modifier: tryConsume("MODIFIER") || "" }); continue; } var value = char || tryConsume("ESCAPED_CHAR"); if (value) { path += value; continue; } if (path) { result.push(path); path = ""; } var open = tryConsume("OPEN"); if (open) { var prefix = consumeText(); var name_1 = tryConsume("NAME") || ""; var pattern_1 = tryConsume("PATTERN") || ""; var suffix = consumeText(); mustConsume("CLOSE"); result.push({ name: name_1 || (pattern_1 ? key++ : ""), pattern: name_1 && !pattern_1 ? defaultPattern : pattern_1, prefix, suffix, modifier: tryConsume("MODIFIER") || "" }); continue; } mustConsume("END"); } return result; } function escapeString(str) { return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, "\\$1"); } function flags(options) { return options && options.sensitive ? "" : "i"; } function regexpToRegexp(path, keys) { if (!keys) return path; var groupsRegex = /\((?:\?<(.*?)>)?(?!\?)/g; var index = 0; var execResult = groupsRegex.exec(path.source); while (execResult) { keys.push({ // Use parenthesized substring match if available, index otherwise name: execResult[1] || index++, prefix: "", suffix: "", modifier: "", pattern: "" }); execResult = groupsRegex.exec(path.source); } return path; } function arrayToRegexp(paths, keys, options) { var parts = paths.map(function(path) { return pathToRegexp(path, keys, options).source; }); return new RegExp("(?:" + parts.join("|") + ")", flags(options)); } function stringToRegexp(path, keys, options) { return tokensToRegexp(parse(path, options), keys, options); } function tokensToRegexp(tokens, keys, options) { if (options === void 0) { options = {}; } var _a = options.strict, strict = _a === void 0 ? false : _a, _b = options.start, start2 = _b === void 0 ? true : _b, _c = options.end, end = _c === void 0 ? true : _c, _d = options.encode, encode = _d === void 0 ? function(x) { return x; } : _d; var endsWith = "[" + escapeString(options.endsWith || "") + "]|$"; var delimiter = "[" + escapeString(options.delimiter || "/#?") + "]"; var route = start2 ? "^" : ""; for (var _i = 0, tokens_1 = tokens; _i < tokens_1.length; _i++) { var token = tokens_1[_i]; if (typeof token === "string") { route += escapeString(encode(token)); } else { var prefix = escapeString(encode(token.prefix)); var suffix = escapeString(encode(token.suffix)); if (token.pattern) { if (keys) keys.push(token); if (prefix || suffix) { if (token.modifier === "+" || token.modifier === "*") { var mod = token.modifier === "*" ? "?" : ""; route += "(?:" + prefix + "((?:" + token.pattern + ")(?:" + suffix + prefix + "(?:" + token.pattern + "))*)" + suffix + ")" + mod; } else { route += "(?:" + prefix + "(" + token.pattern + ")" + suffix + ")" + token.modifier; } } else { route += "(" + token.pattern + ")" + token.modifier; } } else { route += "(?:" + prefix + suffix + ")" + token.modifier; } } } if (end) { if (!strict) route += delimiter + "?"; route += !options.endsWith ? "$" : "(?=" + endsWith + ")"; } else { var endToken = tokens[tokens.length - 1]; var isEndDelimited = typeof endToken === "string" ? delimiter.indexOf(endToken[endToken.length - 1]) > -1 : ( // tslint:disable-next-line endToken === void 0 ); if (!strict) { route += "(?:" + delimiter + "(?=" + endsWith + "))?"; } if (!isEndDelimited) { route += "(?=" + delimiter + "|" + endsWith + ")"; } } return new RegExp(route, flags(options)); } function pathToRegexp(path, keys, options) { if (path instanceof RegExp) return regexpToRegexp(path, keys); if (Array.isArray(path)) return arrayToRegexp(path, keys, options); return stringToRegexp(path, keys, options); } // src/reactivity/unwrap.ts function unwrap(value) { if (isRef(value) || isComputed(value)) { return value.value; } if (typeof value === "function") { return value(); } return value; } // src/plugins/router/plugin.ts var activeRouters = /* @__PURE__ */ new Set(); var link = { template: html``, props: { href: { default: "#" } }, main({ href }) { const go = (e) => { e.preventDefault(); activeRouters.forEach((router) => { router.doRouteChange(unwrap(href)); }); }; const classes = reactive({ "router-link": true }); return { go, classes, href }; } }; async function runEnterTransition(enter) { return await enter(); } var canEnterRoute = async (route) => { if (route.beforeEnter) { return await runEnterTransition(route.beforeEnter); } return true; }; var maybeRedirectRoute = (route) => { if (route.redirectTo) { activeRouters.forEach((plugin) => plugin.doRouteChange(route.redirectTo)); } }; var RouterPlugin = class { app; routes = []; pathExpressions = /* @__PURE__ */ new Map(); lastPath = "/"; knownRouterViews = /* @__PURE__ */ new Map(); knownRouterViewNames = /* @__PURE__ */ new Map(); populatedRouterViews = /* @__PURE__ */ new Map(); constructor(routes = []) { this.routes = routes; } use(app, ..._) { this.app = app; this.app.register("router-link", link); window.addEventListener("popstate", this.onHistoryEvent.bind(this)); window.addEventListener("pushstate", this.onHistoryEvent.bind(this)); window.addEventListener("load", this.onHistoryEvent.bind(this)); for (const route of this.routes) { this.cacheRouteExpression(route); } this.lastPath = `${location.pathname}${location.search}`; window.history.replaceState({}, "", this.lastPath); activeRouters.add(this); } compile(element) { if (element.nodeType === Node.ELEMENT_NODE && element.nodeName === "ROUTER-VIEW" && !this.knownRouterViews.has(element) && current.componentBlock) { this.knownRouterViews.set(element, current.componentBlock); this.knownRouterViewNames.set(element.getAttribute("name")?.trim() || "", element); } } onHistoryEvent(e) { e.preventDefault(); e.stopImmediatePropagation(); const path = new URL(e.currentTarget.location.href).pathname; if (e.type === "load") { window.history.replaceState({}, "", this.lastPath); } else if (e.type === "pushstate") { window.history.replaceState({}, "", path); } else if (e.type === "popstate") { window.history.replaceState({}, "", path); } this.lastPath = path; const matches = this.getMatchesForURL(path); this.applyMatches(matches); } doRouteChange(to) { window.history.pushState({}, "", to); const matches = this.getMatchesForURL(`${location.pathname}${location.search}`); this.applyMatches(matches); } getMatchesForURL(url) { let matches = []; const matchRoutes = (routes, parentPath = "", previousParents = []) => { let parents = []; for (const route of routes) { parents.push(route); const path = `${parentPath}${route.path}`.replace(/\/\//g, "/"); const match = this.getPathMatch(path, url); if (match) matches.push({ match, parents: [...previousParents, ...parents] }); if (route.children?.length) { matchRoutes(route.children, path, [...previousParents, ...parents]); parents = []; } } return matches; }; matches = matchRoutes(this.routes); return matches; } /** * getRouteExpression takes a path like "/users/:id" and returns a regex * and an array of params that match the path. * "/users/:id" => { regex: /^\/users\/([^\/]+)\?jwt=(\w)$/, params: ["id"], query: ["jwt"] } */ getRouteExpression(path, route) { if (this.pathExpressions.has(path)) return this.pathExpressions.get(path); const params = []; const regex = pathToRegexp(path, params, { strict: false, sensitive: false, end: true }); const expression = { regex, params, path, route }; this.pathExpressions.set(path, expression); return expression; } /** * * @param path A path like /foo/bar/:id * @param url A url like /foo/bar/1234 * @returns A RouteExpression if the URL matches the regex cached for @param path, null otherwise. */ getPathMatch(path, url) { if (this.pathExpressions.get(path)) { const match = this.pathExpressions.get(path).regex.exec(url); if (match) { return this.pathExpressions.get(path); } } return null; } async applyMatches(matches) { if (!matches) return; const usedRouterViews = /* @__PURE__ */ new Set(); const renderRoutes = async (routeChain, rootNode) => { for (const route of routeChain) { if (route.view) { const viewNode = this.knownRouterViewNames.get(route.view); if (viewNode && await canEnterAndRenderRoute(viewNode, route)) { continue; } } else if (rootNode && await canEnterAndRenderRoute(rootNode, route)) { continue; } } }; const canEnterAndRenderRoute = async (node, route) => { const canEnter = await canEnterRoute(route); if (canEnter) { renderRouteAtNode(node, route); return true; } else { if (route.componentFallback) { renderRouteAtNode(node, route, route.componentFallback); } else { maybeRedirectRoute(route); } return false; } }; const renderRouteAtNode = (node, route, component) => { if (!usedRouterViews.has(node) || this.populatedRouterViews.get(node)?.route !== route) { const div = document.createElement("div"); node.replaceChildren(div); const target = div.parentElement; const block = new Block({ element: div, component: component ? component : route.component, replacementType: "replaceChildren", parentContext: current.componentBlock.context }); target.replaceChild(block.element, div); this.populatedRouterViews.set(node, { block, route }); usedRouterViews.add(node); } }; for (const match of matches) { const routeChain = [...match.parents, match.match.route]; const uniqueRouteChain = routeChain.filter((route, index, self) => index === self.findIndex((r) => r.path === route.path)); const rootNode = this.knownRouterViewNames.get("") ?? null; await renderRoutes(uniqueRouteChain, rootNode); } for (const node of this.knownRouterViews.keys()) { if (!usedRouterViews.has(node) && this.populatedRouterViews.has(node)) { const entry = this.populatedRouterViews.get(node); if (entry) { entry.block.teardown(); this.populatedRouterViews.delete(node); } } } } cacheRouteExpression(route, parentPath = "") { const path = `${parentPath}${route.path}`.replace(/\/\//g, "/"); this.getRouteExpression(path, route); if (route.children?.length) { route.children.forEach((child) => { this.cacheRouteExpression(child, path); }); } } destroy() { window.removeEventListener("popstate", this.onHistoryEvent.bind(this)); window.removeEventListener("pushstate", this.onHistoryEvent.bind(this)); window.removeEventListener("load", this.onHistoryEvent.bind(this)); } }; // 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 App2 = class { root; 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.root = this._mount(component, root, props); root.style.display = display; return this.root; } _mount(component, target, props) { 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: true, componentProps: props, replacementType: "replaceChildren" }); return block; } unmount() { this.root.teardown(); } }; function createContext({ parentContext, app }) { const context = { app: app ? app : parentContext && parentContext.app ? parentContext.app : null, scope: parentContext ? parentContext.scope : reactive({}), // 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 warnInvalidDirectives(node, directives) { if (directives.every((d) => node.hasAttribute(d))) { console.warn(`These directives cannot be used together on the same node:`, directives); console.warn("Node ignored:", node); return true; } return false; } function walk(node, context) { if (isText(node)) { new InterpolationDirective({ element: node, context }); return; } if (isElement(node)) { let exp; const handleDirectives = (node2, context2, component, componentProps, allProps) => { if (warnInvalidDirectives(node2, [":if", ":for"])) return; if (warnInvalidDirectives(node2, [":if", ":teleport"])) return; if (warnInvalidDirectives(node2, [":for", ":teleport"])) return; if (exp = checkAndRemoveAttribute(node2, ":scope")) { const scope = evalGet(context2.scope, exp, node2); if (typeof scope === "object") { Object.assign(context2.scope, scope); } } if (exp = checkAndRemoveAttribute(node2, ":teleport")) { return _teleport(node2, exp, context2); } if (exp = checkAndRemoveAttribute(node2, ":if")) { return _if(node2, exp, context2, component, componentProps, allProps); } if (exp = checkAndRemoveAttribute(node2, ":for")) { return _for(node2, exp, context2, component, componentProps, allProps); } if (exp = checkAndRemoveAttribute(node2, ":show")) { new ShowDirective({ element: node2, context: context2, expression: exp }); } if (exp = checkAndRemoveAttribute(node2, ":ref")) { context2.scope[exp].value = node2; } if (exp = checkAndRemoveAttribute(node2, ":value")) { new ValueDirective({ element: node2, context: context2, expression: exp }); } if (exp = checkAndRemoveAttribute(node2, ":html")) { context2.effect(() => { const result = evalGet(context2.scope, exp, node2); if (result instanceof Element) { node2.replaceChildren(); node2.append(result); } else { node2.innerHTML = result; } }); } if (exp = checkAndRemoveAttribute(node2, ":text")) { context2.effect(() => { node2.textContent = toDisplayString(evalGet(context2.scope, exp, node2)); }); } }; const processAttributes = (node2, component) => { return Array.from(node2.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), node2) : attr.value ? evalGet(context.scope, attr.value, node2) : void 0 })); }; if (isComponent(node, context)) { const component = context.app.getComponent(node.tagName.toLowerCase()); const allProps = processAttributes(node, component); const componentProps = allProps.reduce((acc, { isSpread, isMirror, extractedName, value }) => { if (isSpread) { const spread = evalGet(context.scope, extractedName, node); if (isObject(spread)) Object.assign(acc, spread); } else if (isMirror) { acc[extractedName] = evalGet(context.scope, extractedName, node); } else { acc[extractedName] = value; } return acc; }, {}); const next2 = handleDirectives(node, context, component, componentProps, allProps); if (next2) return next2; const templates = findTemplateNodes(node); return new Block({ element: node, parentContext: context, component, replacementType: "replace", parentComponentBlock: current.componentBlock, templates, componentProps, allProps }).element; } const next = handleDirectives(node, context); if (next) return next; 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; } export { App2 as App, Block, RouterPlugin, createContext, createScopedContext, current, evalGet, evalSet, inject, provide }; //# sourceMappingURL=index.js.map