| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- // src/jsx/base.ts
- import { raw } from "../helper/html/index.js";
- import { escapeToBuffer, resolveCallbackSync, stringBufferToString } from "../utils/html.js";
- import { DOM_RENDERER, DOM_MEMO } from "./constants.js";
- import { createContext, globalContexts, useContext } from "./context.js";
- import { domRenderers } from "./intrinsic-element/common.js";
- import * as intrinsicElementTags from "./intrinsic-element/components.js";
- import { normalizeIntrinsicElementKey, styleObjectForEach } from "./utils.js";
- var nameSpaceContext = void 0;
- var getNameSpaceContext = () => nameSpaceContext;
- var toSVGAttributeName = (key) => /[A-Z]/.test(key) && // Presentation attributes are findable in style object. "clip-path", "font-size", "stroke-width", etc.
- // Or other un-deprecated kebab-case attributes. "overline-position", "paint-order", "strikethrough-position", etc.
- 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;
- var emptyTags = [
- "area",
- "base",
- "br",
- "col",
- "embed",
- "hr",
- "img",
- "input",
- "keygen",
- "link",
- "meta",
- "param",
- "source",
- "track",
- "wbr"
- ];
- var 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"
- ];
- var 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" || child.isEscaped) {
- ;
- buffer[0] += child;
- } else if (child instanceof Promise) {
- buffer.unshift("", child);
- } else {
- childrenToStringToBuffer(child, buffer);
- }
- }
- };
- var JSXNode = class {
- tag;
- props;
- key;
- children;
- isEscaped = true;
- localContexts;
- constructor(tag, props, children) {
- this.tag = tag;
- this.props = props;
- this.children = children;
- }
- get type() {
- return this.tag;
- }
- // Added for compatibility with libraries that rely on React's internal structure
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- get ref() {
- return this.props.ref || null;
- }
- toString() {
- const buffer = [""];
- this.localContexts?.forEach(([context, value]) => {
- context.values.push(value);
- });
- try {
- this.toStringToBuffer(buffer);
- } finally {
- this.localContexts?.forEach(([context]) => {
- context.values.pop();
- });
- }
- return buffer.length === 1 ? "callbacks" in buffer ? resolveCallbackSync(raw(buffer[0], buffer.callbacks)).toString() : buffer[0] : stringBufferToString(buffer, buffer.callbacks);
- }
- toStringToBuffer(buffer) {
- const tag = this.tag;
- const props = this.props;
- let { children } = this;
- buffer[0] += `<${tag}`;
- const normalizeKey = nameSpaceContext && useContext(nameSpaceContext) === "svg" ? (key) => toSVGAttributeName(normalizeIntrinsicElementKey(key)) : (key) => normalizeIntrinsicElementKey(key);
- for (let [key, v] of Object.entries(props)) {
- key = normalizeKey(key);
- 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("", res);
- } else {
- const currentContexts = globalContexts.map((c) => [c, c.values.at(-1)]);
- buffer.unshift(
- "",
- res.then((childRes) => {
- if (childRes instanceof JSXNode) {
- childRes.localContexts = currentContexts;
- }
- return childRes;
- })
- );
- }
- } else if (res instanceof JSXNode) {
- res.toStringToBuffer(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);
- }
- };
- var 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;
- };
- var initDomRenderer = false;
- var jsxFn = (tag, props, children) => {
- if (!initDomRenderer) {
- for (const k in domRenderers) {
- ;
- intrinsicElementTags[k][DOM_RENDERER] = domRenderers[k];
- }
- initDomRenderer = true;
- }
- if (typeof tag === "function") {
- return new JSXFunctionNode(tag, props, children);
- } else if (intrinsicElementTags[tag]) {
- return new JSXFunctionNode(
- intrinsicElementTags[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);
- }
- };
- var 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;
- };
- var memo = (component, propsAreEqual = shallowEqual) => {
- let computed = null;
- let prevProps = void 0;
- const wrapper = ((props) => {
- if (prevProps && !propsAreEqual(prevProps, props)) {
- computed = null;
- }
- prevProps = props;
- return computed ||= component(props);
- });
- wrapper[DOM_MEMO] = propsAreEqual;
- wrapper[DOM_RENDERER] = component;
- return wrapper;
- };
- var Fragment = ({
- children
- }) => {
- return new JSXFragmentNode(
- "",
- {
- children
- },
- Array.isArray(children) ? children : children ? [children] : []
- );
- };
- var isValidElement = (element) => {
- return !!(element && typeof element === "object" && "tag" in element && "props" in element);
- };
- var 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
- );
- };
- var reactAPICompatVersion = "19.0.0-hono-jsx";
- export {
- Fragment,
- JSXFragmentNode,
- JSXNode,
- booleanAttributes,
- cloneElement,
- getNameSpaceContext,
- isValidElement,
- jsx,
- jsxFn,
- memo,
- reactAPICompatVersion,
- shallowEqual
- };
|