Files
SnowLuma/.pnpm-store/v11/files/70/74035a8028444c5c90d6e61aea283d0927a807e1730d90813400a7998e971fc23e8746850b597c3e3d9415e176c434b01fae739f61bfaa688d1a00959b42e1
TanTan 2a69b23b47
Dev Build / cleanup (push) Canceled after 0s
Dev Build / typecheck (push) Canceled after 0s
Dev Build / build (linux-arm64, ubuntu-24.04-arm, linux-arm64) (push) Canceled after 0s
Dev Build / build (linux-x64, ubuntu-latest, linux-x64) (push) Canceled after 0s
Dev Build / build (win-x64, windows-latest, win32-x64) (push) Canceled after 0s
Dev Build / dispatch-dev-docker (push) Canceled after 0s
docs: 仓库链接指向本镜像仓库
2026-11-13 09:27:07 +08:00

252 lines
9.3 KiB
Plaintext

import { escapeToBuffer, raw, resolveCallbackSync, stringBufferToString } from "../utils/html.js";
import { DOM_MEMO, DOM_RENDERER } from "./constants.js";
import { captureRenderContext, createContext, globalContexts, runWithRenderContext, useContext } from "./context.js";
import { domRenderers } from "./intrinsic-element/common.js";
import { components_exports } from "./intrinsic-element/components.js";
import { isValidAttributeName, isValidTagName, normalizeIntrinsicElementKey, styleObjectForEach } from "./utils.js";
//#region src/jsx/base.ts
let nameSpaceContext = void 0;
const getNameSpaceContext = () => nameSpaceContext;
const toSVGAttributeName = (key) => /[A-Z]/.test(key) && key.match(/^(?:al|basel|clip(?:Path|Rule)$|co|do|fill|fl|fo|gl|let|lig|i|marker[EMS]|o|pai|pointe|sh|st[or]|text[^L]|tr|u|ve|w)/) ? key.replace(/([A-Z])/g, "-$1").toLowerCase() : key;
const emptyTags = [
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"keygen",
"link",
"meta",
"param",
"source",
"track",
"wbr"
];
const booleanAttributes = [
"allowfullscreen",
"async",
"autofocus",
"autoplay",
"checked",
"controls",
"default",
"defer",
"disabled",
"download",
"formnovalidate",
"hidden",
"inert",
"ismap",
"itemscope",
"loop",
"multiple",
"muted",
"nomodule",
"novalidate",
"open",
"playsinline",
"readonly",
"required",
"reversed",
"selected"
];
const resolveFunctionComponentResult = (result, suspendedContext) => result.then((resolved) => {
if (typeof resolved !== "string" && !Array.isArray(resolved) && !(resolved instanceof JSXNode)) return resolved;
const children = Array.isArray(resolved) ? resolved : [resolved];
const render = () => {
const buffer = [""];
childrenToStringToBuffer(children, buffer);
return buffer.length === 1 ? raw(buffer[0], buffer.callbacks) : stringBufferToString(buffer, buffer.callbacks);
};
return suspendedContext ? suspendedContext(render) : runWithRenderContext(render);
});
const childrenToStringToBuffer = (children, buffer) => {
for (let i = 0, len = children.length; i < len; i++) {
const child = children[i];
if (typeof child === "string") escapeToBuffer(child, buffer);
else if (typeof child === "boolean" || child === null || child === void 0) continue;
else if (child instanceof JSXNode) child.toStringToBuffer(buffer);
else if (typeof child === "number") buffer[0] += child;
else if (child.isEscaped) {
buffer[0] += child;
const callbacks = child.callbacks;
if (callbacks) {
buffer.callbacks ||= [];
buffer.callbacks.push(...callbacks);
}
} else if (child instanceof Promise) buffer.unshift("", child);
else childrenToStringToBuffer(child, buffer);
}
};
const renderChildren = (children) => runWithRenderContext(() => {
const buffer = [""];
childrenToStringToBuffer(children, buffer);
return buffer.length === 1 ? raw(buffer[0], buffer.callbacks) : stringBufferToString(buffer, buffer.callbacks);
});
const isUntrustedObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof JSXNode) && !(value instanceof Promise) && !value.isEscaped && typeof value.toString === "function";
const renderUntrustedObject = (value) => {
const stringified = value.toString();
const escape = (result) => renderChildren([String(result)]);
return stringified instanceof Promise ? stringified.then(escape) : escape(stringified);
};
var JSXNode = class {
tag;
props;
key;
children;
isEscaped = true;
constructor(tag, props, children) {
if (typeof tag !== "function" && !isValidTagName(tag)) throw new Error(`Invalid JSX tag name: ${tag}`);
this.tag = tag;
this.props = props;
this.children = children;
}
get type() {
return this.tag;
}
get ref() {
return this.props.ref || null;
}
toString() {
const render = () => {
const buffer = [""];
this.toStringToBuffer(buffer);
return buffer.length === 1 ? "callbacks" in buffer ? resolveCallbackSync(raw(buffer[0], buffer.callbacks)).toString() : buffer[0] : stringBufferToString(buffer, buffer.callbacks);
};
return runWithRenderContext(render);
}
toStringToBuffer(buffer) {
const tag = this.tag;
const props = this.props;
let { children } = this;
buffer[0] += `<${tag}`;
const normalizeKey = tag === "svg" || nameSpaceContext && useContext(nameSpaceContext) === "svg" ? (key) => toSVGAttributeName(normalizeIntrinsicElementKey(key)) : (key) => normalizeIntrinsicElementKey(key);
for (let [key, v] of Object.entries(props)) {
key = normalizeKey(key);
if (!isValidAttributeName(key)) continue;
if (key === "children") {} else if (key === "style" && typeof v === "object") {
let styleStr = "";
styleObjectForEach(v, (property, value) => {
if (value != null) styleStr += `${styleStr ? ";" : ""}${property}:${value}`;
});
buffer[0] += " style=\"";
escapeToBuffer(styleStr, buffer);
buffer[0] += "\"";
} else if (typeof v === "string") {
buffer[0] += ` ${key}="`;
escapeToBuffer(v, buffer);
buffer[0] += "\"";
} else if (v === null || v === void 0) {} else if (typeof v === "number" || v.isEscaped) buffer[0] += ` ${key}="${v}"`;
else if (typeof v === "boolean" && booleanAttributes.includes(key)) {
if (v) buffer[0] += ` ${key}=""`;
} else if (key === "dangerouslySetInnerHTML") {
if (children.length > 0) throw new Error("Can only set one of `children` or `props.dangerouslySetInnerHTML`.");
children = [raw(v.__html)];
} else if (v instanceof Promise) {
buffer[0] += ` ${key}="`;
buffer.unshift("\"", v);
} else if (typeof v === "function") {
if (!key.startsWith("on") && key !== "ref") throw new Error(`Invalid prop '${key}' of type 'function' supplied to '${tag}'.`);
} else {
buffer[0] += ` ${key}="`;
escapeToBuffer(v.toString(), buffer);
buffer[0] += "\"";
}
}
if (emptyTags.includes(tag) && children.length === 0) {
buffer[0] += "/>";
return;
}
buffer[0] += ">";
childrenToStringToBuffer(children, buffer);
buffer[0] += `</${tag}>`;
}
};
var JSXFunctionNode = class extends JSXNode {
toStringToBuffer(buffer) {
const { children } = this;
const props = { ...this.props };
if (children.length) props.children = children.length === 1 ? children[0] : children;
const res = this.tag.call(null, props);
if (typeof res === "boolean" || res == null) return;
else if (res instanceof Promise) {
if (globalContexts.length === 0) buffer.unshift("", resolveFunctionComponentResult(res));
else buffer.unshift("", resolveFunctionComponentResult(res, captureRenderContext()));
} else if (res instanceof JSXNode) res.toStringToBuffer(buffer);
else if (Array.isArray(res)) childrenToStringToBuffer(res, buffer);
else if (typeof res === "number" || res.isEscaped) {
buffer[0] += res;
if (res.callbacks) {
buffer.callbacks ||= [];
buffer.callbacks.push(...res.callbacks);
}
} else escapeToBuffer(res, buffer);
}
};
var JSXFragmentNode = class extends JSXNode {
toStringToBuffer(buffer) {
childrenToStringToBuffer(this.children, buffer);
}
};
const jsx = (tag, props, ...children) => {
props ??= {};
if (children.length) props.children = children.length === 1 ? children[0] : children;
const key = props.key;
delete props["key"];
const node = jsxFn(tag, props, children);
node.key = key;
return node;
};
let initDomRenderer = false;
const jsxFn = (tag, props, children) => {
if (!initDomRenderer) {
for (const k in domRenderers) components_exports[k][DOM_RENDERER] = domRenderers[k];
initDomRenderer = true;
}
if (typeof tag === "function") return new JSXFunctionNode(tag, props, children);
else if (components_exports[tag]) return new JSXFunctionNode(components_exports[tag], props, children);
else if (tag === "svg" || tag === "head") {
nameSpaceContext ||= createContext("");
return new JSXNode(tag, props, [new JSXFunctionNode(nameSpaceContext, { value: tag }, children)]);
} else return new JSXNode(tag, props, children);
};
const shallowEqual = (a, b) => {
if (a === b) return true;
const aKeys = Object.keys(a).sort();
const bKeys = Object.keys(b).sort();
if (aKeys.length !== bKeys.length) return false;
for (let i = 0, len = aKeys.length; i < len; i++) if (aKeys[i] === "children" && bKeys[i] === "children" && !a.children?.length && !b.children?.length) continue;
else if (a[aKeys[i]] !== b[aKeys[i]]) return false;
return true;
};
const memo = (component, propsAreEqual = shallowEqual) => {
const wrapper = ((props) => component(props));
wrapper[DOM_MEMO] = propsAreEqual;
wrapper[DOM_RENDERER] = component;
return wrapper;
};
const Fragment = ({ children }) => {
return new JSXFragmentNode("", { children }, Array.isArray(children) ? children : children ? [children] : []);
};
const isValidElement = (element) => {
return !!(element && typeof element === "object" && "tag" in element && "props" in element);
};
const cloneElement = (element, props, ...children) => {
let childrenToClone;
if (children.length > 0) childrenToClone = children;
else {
const c = element.props.children;
childrenToClone = Array.isArray(c) ? c : [c];
}
return jsx(element.tag, {
...element.props,
...props
}, ...childrenToClone);
};
const reactAPICompatVersion = "19.0.0-hono-jsx";
//#endregion
export { Fragment, JSXFragmentNode, JSXNode, booleanAttributes, cloneElement, getNameSpaceContext, isUntrustedObject, isValidElement, jsx, jsxFn, memo, reactAPICompatVersion, renderChildren, renderUntrustedObject, shallowEqual };