UNPKG

hono

Version:

Web framework built on Web Standards

615 lines (614 loc) 19.8 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var render_exports = {}; __export(render_exports, { build: () => build, buildDataStack: () => buildDataStack, buildNode: () => buildNode, createPortal: () => createPortal, flushSync: () => flushSync, getNameSpaceContext: () => getNameSpaceContext, render: () => render, renderNode: () => renderNode, update: () => update }); module.exports = __toCommonJS(render_exports); var import_children = require("../children"); var import_constants = require("../constants"); var import_context = require("../context"); var import_hooks = require("../hooks"); var import_utils = require("../utils"); var import_context2 = require("./context"); const HONO_PORTAL_ELEMENT = "_hp"; const eventAliasMap = { Change: "Input", DoubleClick: "DblClick" }; const nameSpaceMap = { svg: "2000/svg", math: "1998/Math/MathML" }; const buildDataStack = []; const refCleanupMap = /* @__PURE__ */ new WeakMap(); let nameSpaceContext = void 0; const getNameSpaceContext = () => nameSpaceContext; const isNodeString = (node) => "t" in node; const eventCache = { onClick: ["click", false] }; const getEventSpec = (key) => { if (!key.startsWith("on")) { return void 0; } if (eventCache[key]) { return eventCache[key]; } const match = key.match(/^on([A-Z][a-zA-Z]+?(?:PointerCapture)?)(Capture)?$/); if (match) { const [, eventName, capture] = match; return eventCache[key] = [(eventAliasMap[eventName] || eventName).toLowerCase(), !!capture]; } return void 0; }; const toAttributeName = (element, key) => nameSpaceContext && element instanceof SVGElement && /[A-Z]/.test(key) && (key in element.style || key.match(/^(?:o|pai|str|u|ve)/)) ? key.replace(/([A-Z])/g, "-$1").toLowerCase() : key; const applyProps = (container, attributes, oldAttributes) => { attributes ||= {}; for (let key in attributes) { const value = attributes[key]; if (key !== "children" && (!oldAttributes || oldAttributes[key] !== value)) { key = (0, import_utils.normalizeIntrinsicElementKey)(key); const eventSpec = getEventSpec(key); if (eventSpec) { if (oldAttributes?.[key] !== value) { if (oldAttributes) { container.removeEventListener(eventSpec[0], oldAttributes[key], eventSpec[1]); } if (value != null) { if (typeof value !== "function") { throw new Error(`Event handler for "${key}" is not a function`); } container.addEventListener(eventSpec[0], value, eventSpec[1]); } } } else if (key === "dangerouslySetInnerHTML" && value) { container.innerHTML = value.__html; } else if (key === "ref") { let cleanup; if (typeof value === "function") { cleanup = value(container) || (() => value(null)); } else if (value && "current" in value) { value.current = container; cleanup = () => value.current = null; } refCleanupMap.set(container, cleanup); } else if (key === "style") { const style = container.style; if (typeof value === "string") { style.cssText = value; } else { style.cssText = ""; if (value != null) { (0, import_utils.styleObjectForEach)(value, style.setProperty.bind(style)); } } } else { if (key === "value") { const nodeName = container.nodeName; if (nodeName === "INPUT" || nodeName === "TEXTAREA" || nodeName === "SELECT") { ; container.value = value === null || value === void 0 || value === false ? null : value; if (nodeName === "TEXTAREA") { container.textContent = value; continue; } else if (nodeName === "SELECT") { if (container.selectedIndex === -1) { ; container.selectedIndex = 0; } continue; } } } else if (key === "checked" && container.nodeName === "INPUT" || key === "selected" && container.nodeName === "OPTION") { ; container[key] = value; } const k = toAttributeName(container, key); if (value === null || value === void 0 || value === false) { container.removeAttribute(k); } else if (value === true) { container.setAttribute(k, ""); } else if (typeof value === "string" || typeof value === "number") { container.setAttribute(k, value); } else { container.setAttribute(k, value.toString()); } } } } if (oldAttributes) { for (let key in oldAttributes) { const value = oldAttributes[key]; if (key !== "children" && !(key in attributes)) { key = (0, import_utils.normalizeIntrinsicElementKey)(key); const eventSpec = getEventSpec(key); if (eventSpec) { container.removeEventListener(eventSpec[0], value, eventSpec[1]); } else if (key === "ref") { refCleanupMap.get(container)?.(); } else { container.removeAttribute(toAttributeName(container, key)); } } } } }; const invokeTag = (context, node) => { node[import_constants.DOM_STASH][0] = 0; buildDataStack.push([context, node]); const func = node.tag[import_constants.DOM_RENDERER] || node.tag; const props = func.defaultProps ? { ...func.defaultProps, ...node.props } : node.props; try { return [func.call(null, props)]; } finally { buildDataStack.pop(); } }; const getNextChildren = (node, container, nextChildren, childrenToRemove, callbacks) => { if (node.vR?.length) { childrenToRemove.push(...node.vR); delete node.vR; } if (typeof node.tag === "function") { node[import_constants.DOM_STASH][1][import_hooks.STASH_EFFECT]?.forEach((data) => callbacks.push(data)); } node.vC.forEach((child) => { if (isNodeString(child)) { nextChildren.push(child); } else { if (typeof child.tag === "function" || child.tag === "") { child.c = container; const currentNextChildrenIndex = nextChildren.length; getNextChildren(child, container, nextChildren, childrenToRemove, callbacks); if (child.s) { for (let i = currentNextChildrenIndex; i < nextChildren.length; i++) { nextChildren[i].s = true; } child.s = false; } } else { nextChildren.push(child); if (child.vR?.length) { childrenToRemove.push(...child.vR); delete child.vR; } } } }); }; const findInsertBefore = (node) => { for (; ; node = node.tag === HONO_PORTAL_ELEMENT || !node.vC || !node.pP ? node.nN : node.vC[0]) { if (!node) { return null; } if (node.tag !== HONO_PORTAL_ELEMENT && node.e) { return node.e; } } }; const removeNode = (node) => { if (!isNodeString(node)) { node[import_constants.DOM_STASH]?.[1][import_hooks.STASH_EFFECT]?.forEach((data) => data[2]?.()); refCleanupMap.get(node.e)?.(); if (node.p === 2) { node.vC?.forEach((n) => n.p = 2); } node.vC?.forEach(removeNode); } if (!node.p) { node.e?.remove(); delete node.e; } if (typeof node.tag === "function") { updateMap.delete(node); fallbackUpdateFnArrayMap.delete(node); delete node[import_constants.DOM_STASH][3]; node.a = true; } }; const apply = (node, container, isNew) => { node.c = container; applyNodeObject(node, container, isNew); }; const findChildNodeIndex = (childNodes, child) => { if (!child) { return; } for (let i = 0, len = childNodes.length; i < len; i++) { if (childNodes[i] === child) { return i; } } return; }; const cancelBuild = Symbol(); const applyNodeObject = (node, container, isNew) => { const next = []; const remove = []; const callbacks = []; getNextChildren(node, container, next, remove, callbacks); remove.forEach(removeNode); const childNodes = isNew ? void 0 : container.childNodes; let offset; let insertBeforeNode = null; if (isNew) { offset = -1; } else if (!childNodes.length) { offset = 0; } else { const offsetByNextNode = findChildNodeIndex(childNodes, findInsertBefore(node.nN)); if (offsetByNextNode !== void 0) { insertBeforeNode = childNodes[offsetByNextNode]; offset = offsetByNextNode; } else { offset = findChildNodeIndex(childNodes, next.find((n) => n.tag !== HONO_PORTAL_ELEMENT && n.e)?.e) ?? -1; } if (offset === -1) { isNew = true; } } for (let i = 0, len = next.length; i < len; i++, offset++) { const child = next[i]; let el; if (child.s && child.e) { el = child.e; child.s = false; } else { const isNewLocal = isNew || !child.e; if (isNodeString(child)) { if (child.e && child.d) { child.e.textContent = child.t; } child.d = false; el = child.e ||= document.createTextNode(child.t); } else { el = child.e ||= child.n ? document.createElementNS(child.n, child.tag) : document.createElement(child.tag); applyProps(el, child.props, child.pP); applyNodeObject(child, el, isNewLocal); } } if (child.tag === HONO_PORTAL_ELEMENT) { offset--; } else if (isNew) { if (!el.parentNode) { container.appendChild(el); } } else if (childNodes[offset] !== el && childNodes[offset - 1] !== el) { if (childNodes[offset + 1] === el) { container.appendChild(childNodes[offset]); } else { container.insertBefore(el, insertBeforeNode || childNodes[offset] || null); } } } if (node.pP) { delete node.pP; } if (callbacks.length) { const useLayoutEffectCbs = []; const useEffectCbs = []; callbacks.forEach(([, useLayoutEffectCb, , useEffectCb, useInsertionEffectCb]) => { if (useLayoutEffectCb) { useLayoutEffectCbs.push(useLayoutEffectCb); } if (useEffectCb) { useEffectCbs.push(useEffectCb); } useInsertionEffectCb?.(); }); useLayoutEffectCbs.forEach((cb) => cb()); if (useEffectCbs.length) { requestAnimationFrame(() => { useEffectCbs.forEach((cb) => cb()); }); } } }; const isSameContext = (oldContexts, newContexts) => !!(oldContexts && oldContexts.length === newContexts.length && oldContexts.every((ctx, i) => ctx[1] === newContexts[i][1])); const fallbackUpdateFnArrayMap = /* @__PURE__ */ new WeakMap(); const build = (context, node, children) => { const buildWithPreviousChildren = !children && node.pC; if (children) { node.pC ||= node.vC; } let foundErrorHandler; try { children ||= typeof node.tag == "function" ? invokeTag(context, node) : (0, import_children.toArray)(node.props.children); if (children[0]?.tag === "" && children[0][import_constants.DOM_ERROR_HANDLER]) { foundErrorHandler = children[0][import_constants.DOM_ERROR_HANDLER]; context[5].push([context, foundErrorHandler, node]); } const oldVChildren = buildWithPreviousChildren ? [...node.pC] : node.vC ? [...node.vC] : void 0; const vChildren = []; let prevNode; for (let i = 0; i < children.length; i++) { if (Array.isArray(children[i])) { children.splice(i, 1, ...children[i].flat()); } let child = buildNode(children[i]); if (child) { if (typeof child.tag === "function" && !child.tag[import_constants.DOM_INTERNAL_TAG]) { if (import_context.globalContexts.length > 0) { child[import_constants.DOM_STASH][2] = import_context.globalContexts.map((c) => [c, c.values.at(-1)]); } if (context[5]?.length) { child[import_constants.DOM_STASH][3] = context[5].at(-1); } } let oldChild; if (oldVChildren && oldVChildren.length) { const i2 = oldVChildren.findIndex( isNodeString(child) ? (c) => isNodeString(c) : child.key !== void 0 ? (c) => c.key === child.key && c.tag === child.tag : (c) => c.tag === child.tag ); if (i2 !== -1) { oldChild = oldVChildren[i2]; oldVChildren.splice(i2, 1); } } if (oldChild) { if (isNodeString(child)) { if (oldChild.t !== child.t) { ; oldChild.t = child.t; oldChild.d = true; } child = oldChild; } else { const pP = oldChild.pP = oldChild.props; oldChild.props = child.props; oldChild.f ||= child.f || node.f; if (typeof child.tag === "function") { const oldContexts = oldChild[import_constants.DOM_STASH][2]; oldChild[import_constants.DOM_STASH][2] = child[import_constants.DOM_STASH][2] || []; oldChild[import_constants.DOM_STASH][3] = child[import_constants.DOM_STASH][3]; if (!oldChild.f && ((oldChild.o || oldChild) === child.o || oldChild.tag[import_constants.DOM_MEMO]?.(pP, oldChild.props)) && isSameContext(oldContexts, oldChild[import_constants.DOM_STASH][2])) { oldChild.s = true; } } child = oldChild; } } else if (!isNodeString(child) && nameSpaceContext) { const ns = (0, import_context.useContext)(nameSpaceContext); if (ns) { child.n = ns; } } if (!isNodeString(child) && !child.s) { build(context, child); delete child.f; } vChildren.push(child); if (prevNode && !prevNode.s && !child.s) { for (let p = prevNode; p && !isNodeString(p); p = p.vC?.at(-1)) { p.nN = child; } } prevNode = child; } } node.vR = buildWithPreviousChildren ? [...node.vC, ...oldVChildren || []] : oldVChildren || []; node.vC = vChildren; if (buildWithPreviousChildren) { delete node.pC; } } catch (e) { node.f = true; if (e === cancelBuild) { if (foundErrorHandler) { return; } else { throw e; } } const [errorHandlerContext, errorHandler, errorHandlerNode] = node[import_constants.DOM_STASH]?.[3] || []; if (errorHandler) { const fallbackUpdateFn = () => update([0, false, context[2]], errorHandlerNode); const fallbackUpdateFnArray = fallbackUpdateFnArrayMap.get(errorHandlerNode) || []; fallbackUpdateFnArray.push(fallbackUpdateFn); fallbackUpdateFnArrayMap.set(errorHandlerNode, fallbackUpdateFnArray); const fallback = errorHandler(e, () => { const fnArray = fallbackUpdateFnArrayMap.get(errorHandlerNode); if (fnArray) { const i = fnArray.indexOf(fallbackUpdateFn); if (i !== -1) { fnArray.splice(i, 1); return fallbackUpdateFn(); } } }); if (fallback) { if (context[0] === 1) { context[1] = true; } else { build(context, errorHandlerNode, [fallback]); if ((errorHandler.length === 1 || context !== errorHandlerContext) && errorHandlerNode.c) { apply(errorHandlerNode, errorHandlerNode.c, false); return; } } throw cancelBuild; } } throw e; } finally { if (foundErrorHandler) { context[5].pop(); } } }; const buildNode = (node) => { if (node === void 0 || node === null || typeof node === "boolean") { return void 0; } else if (typeof node === "string" || typeof node === "number") { return { t: node.toString(), d: true }; } else { if ("vR" in node) { node = { tag: node.tag, props: node.props, key: node.key, f: node.f, type: node.tag, ref: node.props.ref, o: node.o || node }; } if (typeof node.tag === "function") { ; node[import_constants.DOM_STASH] = [0, []]; } else { const ns = nameSpaceMap[node.tag]; if (ns) { nameSpaceContext ||= (0, import_context2.createContext)(""); node.props.children = [ { tag: nameSpaceContext, props: { value: node.n = `http://www.w3.org/${ns}`, children: node.props.children } } ]; } } return node; } }; const replaceContainer = (node, from, to) => { if (node.c === from) { node.c = to; node.vC.forEach((child) => replaceContainer(child, from, to)); } }; const updateSync = (context, node) => { node[import_constants.DOM_STASH][2]?.forEach(([c, v]) => { c.values.push(v); }); try { build(context, node, void 0); } catch { return; } if (node.a) { delete node.a; return; } node[import_constants.DOM_STASH][2]?.forEach(([c]) => { c.values.pop(); }); if (context[0] !== 1 || !context[1]) { apply(node, node.c, false); } }; const updateMap = /* @__PURE__ */ new WeakMap(); const currentUpdateSets = []; const update = async (context, node) => { context[5] ||= []; const existing = updateMap.get(node); if (existing) { existing[0](void 0); } let resolve; const promise = new Promise((r) => resolve = r); updateMap.set(node, [ resolve, () => { if (context[2]) { context[2](context, node, (context2) => { updateSync(context2, node); }).then(() => resolve(node)); } else { updateSync(context, node); resolve(node); } } ]); if (currentUpdateSets.length) { ; currentUpdateSets.at(-1).add(node); } else { await Promise.resolve(); const latest = updateMap.get(node); if (latest) { updateMap.delete(node); latest[1](); } } return promise; }; const renderNode = (node, container) => { const context = []; context[5] = []; context[4] = true; build(context, node, void 0); context[4] = false; const fragment = document.createDocumentFragment(); apply(node, fragment, true); replaceContainer(node, fragment, container); container.replaceChildren(fragment); }; const render = (jsxNode, container) => { renderNode(buildNode({ tag: "", props: { children: jsxNode } }), container); }; const flushSync = (callback) => { const set = /* @__PURE__ */ new Set(); currentUpdateSets.push(set); callback(); set.forEach((node) => { const latest = updateMap.get(node); if (latest) { updateMap.delete(node); latest[1](); } }); currentUpdateSets.pop(); }; const createPortal = (children, container, key) => ({ tag: HONO_PORTAL_ELEMENT, props: { children }, key, e: container, p: 1 }); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { build, buildDataStack, buildNode, createPortal, flushSync, getNameSpaceContext, render, renderNode, update });