@tanstack/redact 0.0.16 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/attributes.d.ts +1 -0
- package/dist/core/attributes.js +19 -0
- package/dist/core/attributes.js.map +7 -0
- package/dist/core/internal.d.ts +2 -1
- package/dist/core/internal.js.map +2 -2
- package/dist/dom/dispatcher.js +3 -1
- package/dist/dom/dispatcher.js.map +2 -2
- package/dist/dom/dom.js +9 -7
- package/dist/dom/dom.js.map +2 -2
- package/dist/dom/features/hydration/full.d.ts +1 -0
- package/dist/dom/features/hydration/full.js +42 -42
- package/dist/dom/features/hydration/full.js.map +2 -2
- package/dist/dom/features/hydration/stub.d.ts +1 -0
- package/dist/dom/features/hydration/stub.js +4 -0
- package/dist/dom/features/hydration/stub.js.map +2 -2
- package/dist/dom/reconcile.js +9 -3
- package/dist/dom/reconcile.js.map +2 -2
- package/dist/dom/root-internal.js +3 -1
- package/dist/dom/root-internal.js.map +2 -2
- package/dist/server/escape.d.ts +1 -2
- package/dist/server/escape.js +3 -13
- package/dist/server/escape.js.map +2 -2
- package/dist/server/walk.d.ts +1 -0
- package/dist/server/walk.js +9 -4
- package/dist/server/walk.js.map +2 -2
- package/package.json +5 -5
- package/src/core/attributes.ts +17 -0
- package/src/core/internal.ts +2 -1
- package/src/dom/dispatcher.ts +3 -1
- package/src/dom/dom.ts +10 -7
- package/src/dom/features/hydration/full.ts +44 -45
- package/src/dom/features/hydration/stub.ts +4 -0
- package/src/dom/reconcile.ts +13 -2
- package/src/dom/root-internal.ts +3 -1
- package/src/server/escape.ts +4 -13
- package/src/server/walk.ts +10 -4
package/dist/server/walk.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/server/walk.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n REACT_ELEMENT_TYPE,\n REACT_LEGACY_ELEMENT_TYPE,\n REACT_FRAGMENT_TYPE,\n type ReactNode,\n type ReactElement,\n} from '../core'\nimport {\n REACT_SUSPENSE_TYPE,\n REACT_PROVIDER_TYPE,\n REACT_CONSUMER_TYPE,\n REACT_FORWARD_REF_TYPE,\n REACT_MEMO_TYPE,\n REACT_LAZY_TYPE,\n REACT_STRICT_MODE_TYPE,\n REACT_PROFILER_TYPE,\n REACT_PORTAL_TYPE,\n} from '../react'\nimport {\n attrToHtml,\n escapeText,\n escapeScript,\n VOID_ELEMENTS,\n RAW_TEXT_ELEMENTS,\n} from './escape'\nimport {\n pushContext,\n popContext,\n snapshotContexts,\n type ContextSnapshot,\n} from './dispatcher'\n\nexport interface SuspendedBoundary {\n id: number\n fallbackHTML: string\n children: ReactNode\n thenable: Promise<any>\n contextSnapshot: ContextSnapshot\n}\n\nexport interface WalkOptions {\n emit: (chunk: string) => void\n onSuspend?: ((boundary: SuspendedBoundary) => void) | undefined\n nextBoundaryId: () => number\n bootstrapped?: boolean | undefined\n isBoundaryResolution?: boolean | undefined\n /**\n * Tracks whether the most recent emission within the *current text flow*\n * ended with a text node. When the next emission is also text, we emit a\n * `<!-- -->` separator so the browser's HTML parser doesn't merge them\n * into a single text node \u2014 required for hydration to line up text\n * boundaries with the React tree. Reset to `false` whenever we enter a\n * new host element (`<tag>` opens a fresh text flow).\n */\n textState?: { lastWasText: boolean }\n}\n\n/**\n * Synchronously walk a React node and emit HTML string pieces to `opts.emit`.\n * Suspended boundaries are emitted as fallbacks with marker IDs; if `opts.onSuspend`\n * is provided, the suspension is recorded for later streaming.\n */\nexport function walk(node: ReactNode, opts: WalkOptions): void {\n // Seed a text state if the caller didn't provide one so the separator logic\n // is active for the whole tree.\n if (!opts.textState) opts = { ...opts, textState: { lastWasText: false } }\n walkNode(node, opts)\n}\n\n// --- <select> selection context ----------------------------------------------\n// Stack of active select values (or `undefined` when the current select has\n// no controlled value). `<option>` walks read the top of stack to decide\n// whether to emit `selected=\"\"`.\nconst selectValueStack: unknown[] = []\nfunction pushSelectContext(value: unknown): void {\n selectValueStack.push(value)\n}\nfunction popSelectContext(): void {\n selectValueStack.pop()\n}\nfunction currentSelectValue(): unknown {\n return selectValueStack.length ? selectValueStack[selectValueStack.length - 1] : undefined\n}\n\nfunction optionChildText(children: unknown): string {\n // `<option>Text</option>` \u2014 if no `value` prop, the option's value is its\n // flat string/number child content. Matches DOM semantics (`option.value`\n // defaults to `textContent` when no attribute is set).\n if (children == null) return ''\n if (typeof children === 'string' || typeof children === 'number') return '' + children\n if (Array.isArray(children)) return children.map(optionChildText).join('')\n return ''\n}\n\nfunction emitText(text: string, opts: WalkOptions): void {\n // Empty string renders no text node and doesn't start/extend a text flow \u2014\n // skip entirely so sibling text isn't separated by a stray `<!-- -->`.\n if (text === '') return\n if (opts.textState?.lastWasText) opts.emit('<!-- -->')\n opts.emit(escapeText(text))\n if (opts.textState) opts.textState.lastWasText = true\n}\n\nfunction walkNode(node: ReactNode, opts: WalkOptions): void {\n if (node == null || node === false || node === true) return\n\n if (typeof node === 'string') {\n emitText(node, opts)\n return\n }\n if (typeof node === 'number') {\n emitText(String(node), opts)\n return\n }\n if (Array.isArray(node)) {\n for (const c of node) walkNode(c, opts)\n return\n }\n if (typeof (node as any)[Symbol.iterator] === 'function') {\n for (const item of node as Iterable<ReactNode>) walkNode(item, opts)\n return\n }\n\n if (typeof node !== 'object') return\n const t = (node as any).$$typeof\n\n // Raw React.lazy in the tree (RSC Flight encodes 'use client' components \u2014\n // CodeBlock, CodeExplorer, etc. \u2014 as bare Lazy objects directly in the\n // tree, not wrapped in REACT_ELEMENT_TYPE). SSR previously dropped these\n // here, so code snippets never made it into the server HTML. The RSC\n // decoder server-side awaits payloads before rendering, so status is\n // 'fulfilled' and `_init()` returns the resolved element synchronously.\n // If still pending (shouldn't happen post-awaitLazyElements), throw the\n // thenable so streaming SSR suspends the current boundary and retries.\n if (t === REACT_LAZY_TYPE) {\n const lazy = node as any\n const resolved = lazy._init(lazy._payload)\n walkNode(resolved, opts)\n return\n }\n\n if (t !== REACT_ELEMENT_TYPE && t !== REACT_LEGACY_ELEMENT_TYPE) return\n\n const el = node as ReactElement\n walkElement(el, opts)\n}\n\nfunction walkElement(el: ReactElement, opts: WalkOptions): void {\n const type = el.type\n const props = el.props ?? {}\n\n if (type === REACT_FRAGMENT_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_PROFILER_TYPE) {\n walkNode(props.children, opts)\n return\n }\n\n if (type === REACT_SUSPENSE_TYPE) {\n walkSuspense(props, opts)\n return\n }\n\n if (typeof type === 'string') {\n walkHost(type, props, opts)\n return\n }\n\n const marker = (type as any)?.$$typeof\n\n if (marker === REACT_PORTAL_TYPE) {\n // Portals don't render to the main HTML output on the server.\n return\n }\n\n if (marker === REACT_PROVIDER_TYPE) {\n const ctx = (type as any)._context\n pushContext(ctx, props.value)\n try {\n walkNode(props.children, opts)\n } finally {\n popContext(ctx)\n }\n return\n }\n\n if (marker === REACT_CONSUMER_TYPE) {\n const ctx = (type as any)._context\n const render = props.children\n if (typeof render === 'function') {\n walkNode(render(ctx._currentValue), opts)\n }\n return\n }\n\n if (marker === REACT_FORWARD_REF_TYPE) {\n const render = (type as any).render\n const ref = (props as any).ref ?? null\n const { ref: _omit, ...rest } = props as any\n const rendered = render(rest, ref)\n walkNode(rendered, opts)\n return\n }\n\n if (marker === REACT_MEMO_TYPE) {\n const inner = (type as any).type\n walkElement({ ...el, type: inner } as ReactElement, opts)\n return\n }\n\n if (marker === REACT_LAZY_TYPE) {\n const { _payload, _init } = type as any\n try {\n const resolved = _init(_payload)\n walkElement({ ...el, type: resolved } as ReactElement, opts)\n } catch (thenable: any) {\n if (isThenable(thenable)) {\n // Suspend this point\n throw thenable\n }\n throw thenable\n }\n return\n }\n\n if (typeof type === 'function') {\n walkComponent(type, props, opts)\n return\n }\n}\n\nfunction walkHost(\n tag: string,\n props: Record<string, any>,\n opts: WalkOptions,\n): void {\n // <textarea value=\"...\"> serializes its value as a TEXT CHILD, not an\n // attribute. `defaultValue` is the fallback when `value` is absent. This\n // matches React and the HTML spec \u2014 `<textarea value=\"x\">` is not valid\n // HTML; the value is the element's textContent.\n const isTextarea = tag === 'textarea'\n const textareaValue = isTextarea\n ? props.value != null\n ? props.value\n : props.defaultValue\n : undefined\n\n // <input defaultValue=\"...\"> should parse with that value \u2014 emit it as a\n // `value` attribute. Similarly `defaultChecked` becomes `checked`. This\n // keeps hydration consistent: the browser parser sees the initial value,\n // and on client commit our setProp seeds `.defaultValue`/`.defaultChecked`\n // without stomping the user-typed value.\n const isInput = tag === 'input'\n const inputValueAttr =\n isInput && props.value == null && props.defaultValue != null\n ? props.defaultValue\n : undefined\n const inputCheckedAttr =\n isInput && props.checked == null && props.defaultChecked != null\n ? props.defaultChecked\n : undefined\n\n // <select value=\"...\"> does NOT become an attribute on `<select>` \u2014 the\n // HTML spec has no such attribute. React resolves the selection by stamping\n // `selected` on the matching `<option>` children during render. Stash the\n // target value(s) on the walk state and the child `<option>` walk reads it.\n const isSelect = tag === 'select'\n if (isSelect) {\n const val = props.value != null ? props.value : props.defaultValue\n pushSelectContext(val)\n }\n const isOption = tag === 'option'\n\n // Prepend the HTML5 doctype to the stream when rendering an <html> root.\n // Without it the browser parses the document in quirks mode, which breaks\n // CSS sizing (documentElement.clientHeight returns the content height, not\n // the viewport) \u2014 and Floating-UI-based libraries (Radix dropdowns etc.)\n // then compute off-screen positions for overlays.\n if (tag === 'html') opts.emit('<!DOCTYPE html>')\n\n opts.emit('<' + tag)\n for (const k in props) {\n if (isTextarea && (k === 'value' || k === 'defaultValue')) continue\n if (isInput && (k === 'defaultValue' || k === 'defaultChecked')) continue\n if (isSelect && (k === 'value' || k === 'defaultValue')) continue\n if (isOption && k === 'selected') continue\n opts.emit(attrToHtml(k, props[k]))\n }\n if (inputValueAttr !== undefined) {\n opts.emit(attrToHtml('value', inputValueAttr))\n }\n if (inputCheckedAttr !== undefined) {\n opts.emit(attrToHtml('checked', inputCheckedAttr))\n }\n if (isOption) {\n const selectVal = currentSelectValue()\n if (selectVal !== undefined) {\n const optionValue =\n props.value != null ? props.value : optionChildText(props.children)\n const matches = Array.isArray(selectVal)\n ? selectVal.some((v) => '' + v === '' + optionValue)\n : '' + selectVal === '' + optionValue\n if (matches) opts.emit(' selected=\"\"')\n } else if (props.selected) {\n opts.emit(' selected=\"\"')\n }\n }\n\n if (VOID_ELEMENTS.has(tag)) {\n opts.emit('/>')\n if (opts.textState) opts.textState.lastWasText = false\n return\n }\n opts.emit('>')\n // Opening a host element starts a fresh text flow context for its children.\n // Children's text separator tracking is independent of the outer context.\n const parentTextState = opts.textState\n const childOpts: WalkOptions = { ...opts, textState: { lastWasText: false } }\n\n if (tag === 'html' && !hasHeadChild(props.children)) {\n opts.emit('<head></head>')\n }\n\n const dangerouslyHtml = props.dangerouslySetInnerHTML?.__html\n\n if (isTextarea && textareaValue != null) {\n opts.emit(escapeText(String(textareaValue)))\n opts.emit(`</${tag}>`)\n if (parentTextState) parentTextState.lastWasText = false\n return\n }\n\n if (RAW_TEXT_ELEMENTS.has(tag)) {\n // script/style: raw-text. React allows either a string/number child or\n // dangerouslySetInnerHTML \u2014 some libs (Start's Scripts) use the latter.\n if (dangerouslyHtml != null) {\n opts.emit(escapeScript(String(dangerouslyHtml)))\n } else {\n const children = props.children\n if (typeof children === 'string' || typeof children === 'number') {\n opts.emit(escapeScript(String(children)))\n } else if (Array.isArray(children)) {\n opts.emit(escapeScript(children.filter((c) => c != null).join('')))\n }\n }\n opts.emit(`</${tag}>`)\n if (parentTextState) parentTextState.lastWasText = false\n return\n }\n\n if (dangerouslyHtml != null) {\n opts.emit(String(dangerouslyHtml))\n } else {\n walkNode(props.children, childOpts)\n }\n opts.emit(`</${tag}>`)\n if (isSelect) popSelectContext()\n // Host element closing resets outer flow \u2014 next sibling text starts fresh.\n if (parentTextState) parentTextState.lastWasText = false\n}\n\nfunction hasHeadChild(children: unknown): boolean {\n if (children == null || typeof children === 'boolean') return false\n if (Array.isArray(children)) return children.some(hasHeadChild)\n if (typeof children !== 'string' && isIterable(children)) {\n for (const child of children as Iterable<unknown>) {\n if (hasHeadChild(child)) return true\n }\n return false\n }\n return isElementOfType(children, 'head')\n}\n\nfunction isElementOfType(value: unknown, type: string): value is ReactElement {\n const marker = (value as ReactElement | null)?.$$typeof as unknown\n return (\n !!value &&\n typeof value === 'object' &&\n (marker === REACT_ELEMENT_TYPE || marker === REACT_LEGACY_ELEMENT_TYPE) &&\n (value as ReactElement).type === type\n )\n}\n\nfunction isIterable(value: unknown): value is Iterable<unknown> {\n return !!value && typeof (value as { [Symbol.iterator]?: unknown })[Symbol.iterator] === 'function'\n}\n\nfunction walkComponent(\n fn: Function,\n props: Record<string, any>,\n opts: WalkOptions,\n): void {\n if ((fn as any).prototype?.isReactComponent) {\n const ctxType = (fn as any).contextType\n const ctxValue = ctxType ? ctxType._currentValue : undefined\n const instance = new (fn as any)(props, ctxValue)\n instance.props = props\n instance.context = ctxValue\n if ((fn as any).getDerivedStateFromProps) {\n const d = (fn as any).getDerivedStateFromProps(props, instance.state)\n if (d) instance.state = { ...instance.state, ...d }\n }\n walkNode(instance.render(), opts)\n return\n }\n const rendered = (fn as any)(props)\n walkNode(rendered, opts)\n}\n\nfunction walkSuspense(\n props: Record<string, any>,\n opts: WalkOptions,\n): void {\n const id = opts.nextBoundaryId()\n // Snapshot contexts BEFORE attempting children, so if a descendant suspends\n // we can replay the same provider stack when re-rendering the boundary.\n const contextSnapshot = snapshotContexts()\n\n // Try to render the children synchronously. If a thenable is thrown,\n // record the boundary and emit the fallback.\n const childParts: string[] = []\n const childEmit = (s: string) => childParts.push(s)\n try {\n walkNode(props.children, {\n emit: childEmit,\n onSuspend: opts.onSuspend,\n nextBoundaryId: opts.nextBoundaryId,\n })\n } catch (thenable: any) {\n if (isThenable(thenable)) {\n const fallbackParts: string[] = []\n try {\n walkNode(props.fallback, {\n emit: (s) => fallbackParts.push(s),\n onSuspend: opts.onSuspend,\n nextBoundaryId: opts.nextBoundaryId,\n })\n } catch {\n // Fallback suspending is unsupported; emit nothing\n }\n emitBoundary(opts, id, fallbackParts.join(''))\n\n if (opts.onSuspend) {\n opts.onSuspend({\n id,\n fallbackHTML: fallbackParts.join(''),\n children: props.children,\n thenable,\n contextSnapshot,\n })\n }\n return\n }\n throw thenable\n }\n\n // Children rendered fully \u2014 emit them wrapped in resolved-boundary markers\n // (`<!--$N-->` / `<!--/$-->`). Without markers, the client hydrator has no\n // way to know this subtree is inside a Suspense, so if the client version\n // of a descendant (e.g. `React.lazy`) suspends it can't pinpoint which DOM\n // range to adopt on resolve \u2014 it creates fresh DOM next to the SSR content,\n // producing visible duplicates (e.g. double navbar logos). Markers let the\n // client treat this as a resolved boundary and hydrate in-place.\n opts.emit(`<!--$${id}-->`)\n opts.emit(childParts.join(''))\n opts.emit(`<!--/$-->`)\n}\n\nfunction emitBoundary(opts: WalkOptions, id: number, fallbackHTML: string): void {\n // Visible div wrapper so the fallback UI shows; B: id lets $RC locate it on\n // reveal. The leading/trailing comments let hydration detect a pending\n // boundary and register a reveal callback.\n opts.emit(`<!--$?${id}--><div id=\"B:${id}\">`)\n opts.emit(fallbackHTML)\n opts.emit(`</div><!--/$-->`)\n}\n\nfunction isThenable(x: any): x is Promise<any> {\n return x != null && typeof x.then === 'function'\n}\n"],
|
|
5
|
-
"mappings": ";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;
|
|
4
|
+
"sourcesContent": ["import {\n REACT_ELEMENT_TYPE,\n REACT_LEGACY_ELEMENT_TYPE,\n REACT_FRAGMENT_TYPE,\n type ReactNode,\n type ReactElement,\n} from '../core'\nimport {\n REACT_SUSPENSE_TYPE,\n REACT_PROVIDER_TYPE,\n REACT_CONSUMER_TYPE,\n REACT_FORWARD_REF_TYPE,\n REACT_MEMO_TYPE,\n REACT_LAZY_TYPE,\n REACT_STRICT_MODE_TYPE,\n REACT_PROFILER_TYPE,\n REACT_PORTAL_TYPE,\n} from '../react'\nimport {\n attrToHtml,\n escapeText,\n escapeScript,\n VOID_ELEMENTS,\n RAW_TEXT_ELEMENTS,\n} from './escape'\nimport {\n pushContext,\n popContext,\n snapshotContexts,\n type ContextSnapshot,\n} from './dispatcher'\n\nexport interface SuspendedBoundary {\n id: number\n fallbackHTML: string\n children: ReactNode\n thenable: Promise<any>\n contextSnapshot: ContextSnapshot\n}\n\nexport interface WalkOptions {\n emit: (chunk: string) => void\n onSuspend?: ((boundary: SuspendedBoundary) => void) | undefined\n nextBoundaryId: () => number\n bootstrapped?: boolean | undefined\n isBoundaryResolution?: boolean | undefined\n isSvg?: boolean | undefined\n /**\n * Tracks whether the most recent emission within the *current text flow*\n * ended with a text node. When the next emission is also text, we emit a\n * `<!-- -->` separator so the browser's HTML parser doesn't merge them\n * into a single text node \u2014 required for hydration to line up text\n * boundaries with the React tree. Reset to `false` whenever we enter a\n * new host element (`<tag>` opens a fresh text flow).\n */\n textState?: { lastWasText: boolean }\n}\n\n/**\n * Synchronously walk a React node and emit HTML string pieces to `opts.emit`.\n * Suspended boundaries are emitted as fallbacks with marker IDs; if `opts.onSuspend`\n * is provided, the suspension is recorded for later streaming.\n */\nexport function walk(node: ReactNode, opts: WalkOptions): void {\n // Seed a text state if the caller didn't provide one so the separator logic\n // is active for the whole tree.\n if (!opts.textState) opts = { ...opts, textState: { lastWasText: false } }\n walkNode(node, opts)\n}\n\n// --- <select> selection context ----------------------------------------------\n// Stack of active select values (or `undefined` when the current select has\n// no controlled value). `<option>` walks read the top of stack to decide\n// whether to emit `selected=\"\"`.\nconst selectValueStack: unknown[] = []\nfunction pushSelectContext(value: unknown): void {\n selectValueStack.push(value)\n}\nfunction popSelectContext(): void {\n selectValueStack.pop()\n}\nfunction currentSelectValue(): unknown {\n return selectValueStack.length ? selectValueStack[selectValueStack.length - 1] : undefined\n}\n\nfunction optionChildText(children: unknown): string {\n // `<option>Text</option>` \u2014 if no `value` prop, the option's value is its\n // flat string/number child content. Matches DOM semantics (`option.value`\n // defaults to `textContent` when no attribute is set).\n if (children == null) return ''\n if (typeof children === 'string' || typeof children === 'number') return '' + children\n if (Array.isArray(children)) return children.map(optionChildText).join('')\n return ''\n}\n\nfunction emitText(text: string, opts: WalkOptions): void {\n // Empty string renders no text node and doesn't start/extend a text flow \u2014\n // skip entirely so sibling text isn't separated by a stray `<!-- -->`.\n if (text === '') return\n if (opts.textState?.lastWasText) opts.emit('<!-- -->')\n opts.emit(escapeText(text))\n if (opts.textState) opts.textState.lastWasText = true\n}\n\nfunction walkNode(node: ReactNode, opts: WalkOptions): void {\n if (node == null || node === false || node === true) return\n\n if (typeof node === 'string') {\n emitText(node, opts)\n return\n }\n if (typeof node === 'number') {\n emitText(String(node), opts)\n return\n }\n if (Array.isArray(node)) {\n for (const c of node) walkNode(c, opts)\n return\n }\n if (typeof (node as any)[Symbol.iterator] === 'function') {\n for (const item of node as Iterable<ReactNode>) walkNode(item, opts)\n return\n }\n\n if (typeof node !== 'object') return\n const t = (node as any).$$typeof\n\n // Raw React.lazy in the tree (RSC Flight encodes 'use client' components \u2014\n // CodeBlock, CodeExplorer, etc. \u2014 as bare Lazy objects directly in the\n // tree, not wrapped in REACT_ELEMENT_TYPE). SSR previously dropped these\n // here, so code snippets never made it into the server HTML. The RSC\n // decoder server-side awaits payloads before rendering, so status is\n // 'fulfilled' and `_init()` returns the resolved element synchronously.\n // If still pending (shouldn't happen post-awaitLazyElements), throw the\n // thenable so streaming SSR suspends the current boundary and retries.\n if (t === REACT_LAZY_TYPE) {\n const lazy = node as any\n const resolved = lazy._init(lazy._payload)\n walkNode(resolved, opts)\n return\n }\n\n if (t !== REACT_ELEMENT_TYPE && t !== REACT_LEGACY_ELEMENT_TYPE) return\n\n const el = node as ReactElement\n walkElement(el, opts)\n}\n\nfunction walkElement(el: ReactElement, opts: WalkOptions): void {\n const type = el.type\n const props = el.props ?? {}\n\n if (type === REACT_FRAGMENT_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_PROFILER_TYPE) {\n walkNode(props.children, opts)\n return\n }\n\n if (type === REACT_SUSPENSE_TYPE) {\n walkSuspense(props, opts)\n return\n }\n\n if (typeof type === 'string') {\n walkHost(type, props, opts)\n return\n }\n\n const marker = (type as any)?.$$typeof\n\n if (marker === REACT_PORTAL_TYPE) {\n // Portals don't render to the main HTML output on the server.\n return\n }\n\n if (marker === REACT_PROVIDER_TYPE) {\n const ctx = (type as any)._context\n pushContext(ctx, props.value)\n try {\n walkNode(props.children, opts)\n } finally {\n popContext(ctx)\n }\n return\n }\n\n if (marker === REACT_CONSUMER_TYPE) {\n const ctx = (type as any)._context\n const render = props.children\n if (typeof render === 'function') {\n walkNode(render(ctx._currentValue), opts)\n }\n return\n }\n\n if (marker === REACT_FORWARD_REF_TYPE) {\n const render = (type as any).render\n const ref = (props as any).ref ?? null\n const { ref: _omit, ...rest } = props as any\n const rendered = render(rest, ref)\n walkNode(rendered, opts)\n return\n }\n\n if (marker === REACT_MEMO_TYPE) {\n const inner = (type as any).type\n walkElement({ ...el, type: inner } as ReactElement, opts)\n return\n }\n\n if (marker === REACT_LAZY_TYPE) {\n const { _payload, _init } = type as any\n try {\n const resolved = _init(_payload)\n walkElement({ ...el, type: resolved } as ReactElement, opts)\n } catch (thenable: any) {\n if (isThenable(thenable)) {\n // Suspend this point\n throw thenable\n }\n throw thenable\n }\n return\n }\n\n if (typeof type === 'function') {\n walkComponent(type, props, opts)\n return\n }\n}\n\nfunction walkHost(\n tag: string,\n props: Record<string, any>,\n opts: WalkOptions,\n): void {\n const isSvg = opts.isSvg || tag === 'svg'\n // <textarea value=\"...\"> serializes its value as a TEXT CHILD, not an\n // attribute. `defaultValue` is the fallback when `value` is absent. This\n // matches React and the HTML spec \u2014 `<textarea value=\"x\">` is not valid\n // HTML; the value is the element's textContent.\n const isTextarea = tag === 'textarea'\n const textareaValue = isTextarea\n ? props.value != null\n ? props.value\n : props.defaultValue\n : undefined\n\n // <input defaultValue=\"...\"> should parse with that value \u2014 emit it as a\n // `value` attribute. Similarly `defaultChecked` becomes `checked`. This\n // keeps hydration consistent: the browser parser sees the initial value,\n // and on client commit our setProp seeds `.defaultValue`/`.defaultChecked`\n // without stomping the user-typed value.\n const isInput = tag === 'input'\n const inputValueAttr =\n isInput && props.value == null && props.defaultValue != null\n ? props.defaultValue\n : undefined\n const inputCheckedAttr =\n isInput && props.checked == null && props.defaultChecked != null\n ? props.defaultChecked\n : undefined\n\n // <select value=\"...\"> does NOT become an attribute on `<select>` \u2014 the\n // HTML spec has no such attribute. React resolves the selection by stamping\n // `selected` on the matching `<option>` children during render. Stash the\n // target value(s) on the walk state and the child `<option>` walk reads it.\n const isSelect = tag === 'select'\n if (isSelect) {\n const val = props.value != null ? props.value : props.defaultValue\n pushSelectContext(val)\n }\n const isOption = tag === 'option'\n\n // Prepend the HTML5 doctype to the stream when rendering an <html> root.\n // Without it the browser parses the document in quirks mode, which breaks\n // CSS sizing (documentElement.clientHeight returns the content height, not\n // the viewport) \u2014 and Floating-UI-based libraries (Radix dropdowns etc.)\n // then compute off-screen positions for overlays.\n if (tag === 'html') opts.emit('<!DOCTYPE html>')\n\n opts.emit('<' + tag)\n for (const k in props) {\n if (isTextarea && (k === 'value' || k === 'defaultValue')) continue\n if (isInput && (k === 'defaultValue' || k === 'defaultChecked')) continue\n if (isSelect && (k === 'value' || k === 'defaultValue')) continue\n if (isOption && k === 'selected') continue\n opts.emit(attrToHtml(k, props[k], isSvg))\n }\n if (inputValueAttr !== undefined) {\n opts.emit(attrToHtml('value', inputValueAttr, isSvg))\n }\n if (inputCheckedAttr !== undefined) {\n opts.emit(attrToHtml('checked', inputCheckedAttr, isSvg))\n }\n if (isOption) {\n const selectVal = currentSelectValue()\n if (selectVal !== undefined) {\n const optionValue =\n props.value != null ? props.value : optionChildText(props.children)\n const matches = Array.isArray(selectVal)\n ? selectVal.some((v) => '' + v === '' + optionValue)\n : '' + selectVal === '' + optionValue\n if (matches) opts.emit(' selected=\"\"')\n } else if (props.selected) {\n opts.emit(' selected=\"\"')\n }\n }\n\n if (VOID_ELEMENTS.has(tag)) {\n opts.emit('/>')\n if (opts.textState) opts.textState.lastWasText = false\n return\n }\n opts.emit('>')\n // Opening a host element starts a fresh text flow context for its children.\n // Children's text separator tracking is independent of the outer context.\n const parentTextState = opts.textState\n const childOpts: WalkOptions = {\n ...opts,\n isSvg: isSvg && tag !== 'foreignObject',\n textState: { lastWasText: false },\n }\n\n if (tag === 'html' && !hasHeadChild(props.children)) {\n opts.emit('<head></head>')\n }\n\n const dangerouslyHtml = props.dangerouslySetInnerHTML?.__html\n\n if (isTextarea && textareaValue != null) {\n opts.emit(escapeText(String(textareaValue)))\n opts.emit(`</${tag}>`)\n if (parentTextState) parentTextState.lastWasText = false\n return\n }\n\n if (RAW_TEXT_ELEMENTS.has(tag)) {\n // script/style: raw-text. React allows either a string/number child or\n // dangerouslySetInnerHTML \u2014 some libs (Start's Scripts) use the latter.\n if (dangerouslyHtml != null) {\n opts.emit(escapeScript(String(dangerouslyHtml)))\n } else {\n const children = props.children\n if (typeof children === 'string' || typeof children === 'number') {\n opts.emit(escapeScript(String(children)))\n } else if (Array.isArray(children)) {\n opts.emit(escapeScript(children.filter((c) => c != null).join('')))\n }\n }\n opts.emit(`</${tag}>`)\n if (parentTextState) parentTextState.lastWasText = false\n return\n }\n\n if (dangerouslyHtml != null) {\n opts.emit(String(dangerouslyHtml))\n } else {\n walkNode(props.children, childOpts)\n }\n opts.emit(`</${tag}>`)\n if (isSelect) popSelectContext()\n // Host element closing resets outer flow \u2014 next sibling text starts fresh.\n if (parentTextState) parentTextState.lastWasText = false\n}\n\nfunction hasHeadChild(children: unknown): boolean {\n if (children == null || typeof children === 'boolean') return false\n if (Array.isArray(children)) return children.some(hasHeadChild)\n if (typeof children !== 'string' && isIterable(children)) {\n for (const child of children as Iterable<unknown>) {\n if (hasHeadChild(child)) return true\n }\n return false\n }\n return isElementOfType(children, 'head')\n}\n\nfunction isElementOfType(value: unknown, type: string): value is ReactElement {\n const marker = (value as ReactElement | null)?.$$typeof as unknown\n return (\n !!value &&\n typeof value === 'object' &&\n (marker === REACT_ELEMENT_TYPE || marker === REACT_LEGACY_ELEMENT_TYPE) &&\n (value as ReactElement).type === type\n )\n}\n\nfunction isIterable(value: unknown): value is Iterable<unknown> {\n return !!value && typeof (value as { [Symbol.iterator]?: unknown })[Symbol.iterator] === 'function'\n}\n\nfunction walkComponent(\n fn: Function,\n props: Record<string, any>,\n opts: WalkOptions,\n): void {\n if ((fn as any).prototype?.isReactComponent) {\n const ctxType = (fn as any).contextType\n const ctxValue = ctxType ? ctxType._currentValue : undefined\n const instance = new (fn as any)(props, ctxValue)\n instance.props = props\n instance.context = ctxValue\n if ((fn as any).getDerivedStateFromProps) {\n const d = (fn as any).getDerivedStateFromProps(props, instance.state)\n if (d) instance.state = { ...instance.state, ...d }\n }\n walkNode(instance.render(), opts)\n return\n }\n const rendered = (fn as any)(props)\n walkNode(rendered, opts)\n}\n\nfunction walkSuspense(\n props: Record<string, any>,\n opts: WalkOptions,\n): void {\n const id = opts.nextBoundaryId()\n // Snapshot contexts BEFORE attempting children, so if a descendant suspends\n // we can replay the same provider stack when re-rendering the boundary.\n const contextSnapshot = snapshotContexts()\n\n // Try to render the children synchronously. If a thenable is thrown,\n // record the boundary and emit the fallback.\n const childParts: string[] = []\n const childEmit = (s: string) => childParts.push(s)\n try {\n walkNode(props.children, {\n emit: childEmit,\n onSuspend: opts.onSuspend,\n nextBoundaryId: opts.nextBoundaryId,\n })\n } catch (thenable: any) {\n if (isThenable(thenable)) {\n const fallbackParts: string[] = []\n try {\n walkNode(props.fallback, {\n emit: (s) => fallbackParts.push(s),\n onSuspend: opts.onSuspend,\n nextBoundaryId: opts.nextBoundaryId,\n })\n } catch {\n // Fallback suspending is unsupported; emit nothing\n }\n emitBoundary(opts, id, fallbackParts.join(''))\n\n if (opts.onSuspend) {\n opts.onSuspend({\n id,\n fallbackHTML: fallbackParts.join(''),\n children: props.children,\n thenable,\n contextSnapshot,\n })\n }\n return\n }\n throw thenable\n }\n\n // Children rendered fully \u2014 emit them wrapped in resolved-boundary markers\n // (`<!--$N-->` / `<!--/$-->`). Without markers, the client hydrator has no\n // way to know this subtree is inside a Suspense, so if the client version\n // of a descendant (e.g. `React.lazy`) suspends it can't pinpoint which DOM\n // range to adopt on resolve \u2014 it creates fresh DOM next to the SSR content,\n // producing visible duplicates (e.g. double navbar logos). Markers let the\n // client treat this as a resolved boundary and hydrate in-place.\n opts.emit(`<!--$${id}-->`)\n opts.emit(childParts.join(''))\n opts.emit(`<!--/$-->`)\n}\n\nfunction emitBoundary(opts: WalkOptions, id: number, fallbackHTML: string): void {\n // Visible div wrapper so the fallback UI shows; B: id lets $RC locate it on\n // reveal. The leading/trailing comments let hydration detect a pending\n // boundary and register a reveal callback.\n opts.emit(`<!--$?${id}--><div id=\"B:${id}\">`)\n opts.emit(fallbackHTML)\n opts.emit(`</div><!--/$-->`)\n}\n\nfunction isThenable(x: any): x is Promise<any> {\n return x != null && typeof x.then === 'function'\n}\n"],
|
|
5
|
+
"mappings": ";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAiCA,SAAS,KAAK,MAAiB,MAAyB;AAG7D,MAAI,CAAC,KAAK,UAAW,QAAO,EAAE,GAAG,MAAM,WAAW,EAAE,aAAa,MAAM,EAAE;AACzE,WAAS,MAAM,IAAI;AACrB;AAMA,IAAM,mBAA8B,CAAC;AACrC,SAAS,kBAAkB,OAAsB;AAC/C,mBAAiB,KAAK,KAAK;AAC7B;AACA,SAAS,mBAAyB;AAChC,mBAAiB,IAAI;AACvB;AACA,SAAS,qBAA8B;AACrC,SAAO,iBAAiB,SAAS,iBAAiB,iBAAiB,SAAS,CAAC,IAAI;AACnF;AAEA,SAAS,gBAAgB,UAA2B;AAIlD,MAAI,YAAY,KAAM,QAAO;AAC7B,MAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAU,QAAO,KAAK;AAC9E,MAAI,MAAM,QAAQ,QAAQ,EAAG,QAAO,SAAS,IAAI,eAAe,EAAE,KAAK,EAAE;AACzE,SAAO;AACT;AAEA,SAAS,SAAS,MAAc,MAAyB;AAGvD,MAAI,SAAS,GAAI;AACjB,MAAI,KAAK,WAAW,YAAa,MAAK,KAAK,UAAU;AACrD,OAAK,KAAK,WAAW,IAAI,CAAC;AAC1B,MAAI,KAAK,UAAW,MAAK,UAAU,cAAc;AACnD;AAEA,SAAS,SAAS,MAAiB,MAAyB;AAC1D,MAAI,QAAQ,QAAQ,SAAS,SAAS,SAAS,KAAM;AAErD,MAAI,OAAO,SAAS,UAAU;AAC5B,aAAS,MAAM,IAAI;AACnB;AAAA,EACF;AACA,MAAI,OAAO,SAAS,UAAU;AAC5B,aAAS,OAAO,IAAI,GAAG,IAAI;AAC3B;AAAA,EACF;AACA,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,eAAW,KAAK,KAAM,UAAS,GAAG,IAAI;AACtC;AAAA,EACF;AACA,MAAI,OAAQ,KAAa,OAAO,QAAQ,MAAM,YAAY;AACxD,eAAW,QAAQ,KAA6B,UAAS,MAAM,IAAI;AACnE;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,SAAU;AAC9B,QAAM,IAAK,KAAa;AAUxB,MAAI,MAAM,iBAAiB;AACzB,UAAM,OAAO;AACb,UAAM,WAAW,KAAK,MAAM,KAAK,QAAQ;AACzC,aAAS,UAAU,IAAI;AACvB;AAAA,EACF;AAEA,MAAI,MAAM,sBAAsB,MAAM,0BAA2B;AAEjE,QAAM,KAAK;AACX,cAAY,IAAI,IAAI;AACtB;AAEA,SAAS,YAAY,IAAkB,MAAyB;AAC9D,QAAM,OAAO,GAAG;AAChB,QAAM,QAAQ,GAAG,SAAS,CAAC;AAE3B,MAAI,SAAS,uBAAuB,SAAS,0BAA0B,SAAS,qBAAqB;AACnG,aAAS,MAAM,UAAU,IAAI;AAC7B;AAAA,EACF;AAEA,MAAI,SAAS,qBAAqB;AAChC,iBAAa,OAAO,IAAI;AACxB;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,UAAU;AAC5B,aAAS,MAAM,OAAO,IAAI;AAC1B;AAAA,EACF;AAEA,QAAM,SAAU,MAAc;AAE9B,MAAI,WAAW,mBAAmB;AAEhC;AAAA,EACF;AAEA,MAAI,WAAW,qBAAqB;AAClC,UAAM,MAAO,KAAa;AAC1B,gBAAY,KAAK,MAAM,KAAK;AAC5B,QAAI;AACF,eAAS,MAAM,UAAU,IAAI;AAAA,IAC/B,UAAE;AACA,iBAAW,GAAG;AAAA,IAChB;AACA;AAAA,EACF;AAEA,MAAI,WAAW,qBAAqB;AAClC,UAAM,MAAO,KAAa;AAC1B,UAAM,SAAS,MAAM;AACrB,QAAI,OAAO,WAAW,YAAY;AAChC,eAAS,OAAO,IAAI,aAAa,GAAG,IAAI;AAAA,IAC1C;AACA;AAAA,EACF;AAEA,MAAI,WAAW,wBAAwB;AACrC,UAAM,SAAU,KAAa;AAC7B,UAAM,MAAO,MAAc,OAAO;AAClC,UAAM,EAAE,KAAK,OAAO,GAAG,KAAK,IAAI;AAChC,UAAM,WAAW,OAAO,MAAM,GAAG;AACjC,aAAS,UAAU,IAAI;AACvB;AAAA,EACF;AAEA,MAAI,WAAW,iBAAiB;AAC9B,UAAM,QAAS,KAAa;AAC5B,gBAAY,EAAE,GAAG,IAAI,MAAM,MAAM,GAAmB,IAAI;AACxD;AAAA,EACF;AAEA,MAAI,WAAW,iBAAiB;AAC9B,UAAM,EAAE,UAAU,MAAM,IAAI;AAC5B,QAAI;AACF,YAAM,WAAW,MAAM,QAAQ;AAC/B,kBAAY,EAAE,GAAG,IAAI,MAAM,SAAS,GAAmB,IAAI;AAAA,IAC7D,SAAS,UAAe;AACtB,UAAI,WAAW,QAAQ,GAAG;AAExB,cAAM;AAAA,MACR;AACA,YAAM;AAAA,IACR;AACA;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,YAAY;AAC9B,kBAAc,MAAM,OAAO,IAAI;AAC/B;AAAA,EACF;AACF;AAEA,SAAS,SACP,KACA,OACA,MACM;AACN,QAAM,QAAQ,KAAK,SAAS,QAAQ;AAKpC,QAAM,aAAa,QAAQ;AAC3B,QAAM,gBAAgB,aAClB,MAAM,SAAS,OACb,MAAM,QACN,MAAM,eACR;AAOJ,QAAM,UAAU,QAAQ;AACxB,QAAM,iBACJ,WAAW,MAAM,SAAS,QAAQ,MAAM,gBAAgB,OACpD,MAAM,eACN;AACN,QAAM,mBACJ,WAAW,MAAM,WAAW,QAAQ,MAAM,kBAAkB,OACxD,MAAM,iBACN;AAMN,QAAM,WAAW,QAAQ;AACzB,MAAI,UAAU;AACZ,UAAM,MAAM,MAAM,SAAS,OAAO,MAAM,QAAQ,MAAM;AACtD,sBAAkB,GAAG;AAAA,EACvB;AACA,QAAM,WAAW,QAAQ;AAOzB,MAAI,QAAQ,OAAQ,MAAK,KAAK,iBAAiB;AAE/C,OAAK,KAAK,MAAM,GAAG;AACnB,aAAW,KAAK,OAAO;AACrB,QAAI,eAAe,MAAM,WAAW,MAAM,gBAAiB;AAC3D,QAAI,YAAY,MAAM,kBAAkB,MAAM,kBAAmB;AACjE,QAAI,aAAa,MAAM,WAAW,MAAM,gBAAiB;AACzD,QAAI,YAAY,MAAM,WAAY;AAClC,SAAK,KAAK,WAAW,GAAG,MAAM,CAAC,GAAG,KAAK,CAAC;AAAA,EAC1C;AACA,MAAI,mBAAmB,QAAW;AAChC,SAAK,KAAK,WAAW,SAAS,gBAAgB,KAAK,CAAC;AAAA,EACtD;AACA,MAAI,qBAAqB,QAAW;AAClC,SAAK,KAAK,WAAW,WAAW,kBAAkB,KAAK,CAAC;AAAA,EAC1D;AACA,MAAI,UAAU;AACZ,UAAM,YAAY,mBAAmB;AACrC,QAAI,cAAc,QAAW;AAC3B,YAAM,cACJ,MAAM,SAAS,OAAO,MAAM,QAAQ,gBAAgB,MAAM,QAAQ;AACpE,YAAM,UAAU,MAAM,QAAQ,SAAS,IACnC,UAAU,KAAK,CAAC,MAAM,KAAK,MAAM,KAAK,WAAW,IACjD,KAAK,cAAc,KAAK;AAC5B,UAAI,QAAS,MAAK,KAAK,cAAc;AAAA,IACvC,WAAW,MAAM,UAAU;AACzB,WAAK,KAAK,cAAc;AAAA,IAC1B;AAAA,EACF;AAEA,MAAI,cAAc,IAAI,GAAG,GAAG;AAC1B,SAAK,KAAK,IAAI;AACd,QAAI,KAAK,UAAW,MAAK,UAAU,cAAc;AACjD;AAAA,EACF;AACA,OAAK,KAAK,GAAG;AAGb,QAAM,kBAAkB,KAAK;AAC7B,QAAM,YAAyB;AAAA,IAC7B,GAAG;AAAA,IACH,OAAO,SAAS,QAAQ;AAAA,IACxB,WAAW,EAAE,aAAa,MAAM;AAAA,EAClC;AAEA,MAAI,QAAQ,UAAU,CAAC,aAAa,MAAM,QAAQ,GAAG;AACnD,SAAK,KAAK,eAAe;AAAA,EAC3B;AAEA,QAAM,kBAAkB,MAAM,yBAAyB;AAEvD,MAAI,cAAc,iBAAiB,MAAM;AACvC,SAAK,KAAK,WAAW,OAAO,aAAa,CAAC,CAAC;AAC3C,SAAK,KAAK,KAAK,GAAG,GAAG;AACrB,QAAI,gBAAiB,iBAAgB,cAAc;AACnD;AAAA,EACF;AAEA,MAAI,kBAAkB,IAAI,GAAG,GAAG;AAG9B,QAAI,mBAAmB,MAAM;AAC3B,WAAK,KAAK,aAAa,OAAO,eAAe,CAAC,CAAC;AAAA,IACjD,OAAO;AACL,YAAM,WAAW,MAAM;AACvB,UAAI,OAAO,aAAa,YAAY,OAAO,aAAa,UAAU;AAChE,aAAK,KAAK,aAAa,OAAO,QAAQ,CAAC,CAAC;AAAA,MAC1C,WAAW,MAAM,QAAQ,QAAQ,GAAG;AAClC,aAAK,KAAK,aAAa,SAAS,OAAO,CAAC,MAAM,KAAK,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAAA,MACpE;AAAA,IACF;AACA,SAAK,KAAK,KAAK,GAAG,GAAG;AACrB,QAAI,gBAAiB,iBAAgB,cAAc;AACnD;AAAA,EACF;AAEA,MAAI,mBAAmB,MAAM;AAC3B,SAAK,KAAK,OAAO,eAAe,CAAC;AAAA,EACnC,OAAO;AACL,aAAS,MAAM,UAAU,SAAS;AAAA,EACpC;AACA,OAAK,KAAK,KAAK,GAAG,GAAG;AACrB,MAAI,SAAU,kBAAiB;AAE/B,MAAI,gBAAiB,iBAAgB,cAAc;AACrD;AAEA,SAAS,aAAa,UAA4B;AAChD,MAAI,YAAY,QAAQ,OAAO,aAAa,UAAW,QAAO;AAC9D,MAAI,MAAM,QAAQ,QAAQ,EAAG,QAAO,SAAS,KAAK,YAAY;AAC9D,MAAI,OAAO,aAAa,YAAY,WAAW,QAAQ,GAAG;AACxD,eAAW,SAAS,UAA+B;AACjD,UAAI,aAAa,KAAK,EAAG,QAAO;AAAA,IAClC;AACA,WAAO;AAAA,EACT;AACA,SAAO,gBAAgB,UAAU,MAAM;AACzC;AAEA,SAAS,gBAAgB,OAAgB,MAAqC;AAC5E,QAAM,SAAU,OAA+B;AAC/C,SACE,CAAC,CAAC,SACF,OAAO,UAAU,aAChB,WAAW,sBAAsB,WAAW,8BAC5C,MAAuB,SAAS;AAErC;AAEA,SAAS,WAAW,OAA4C;AAC9D,SAAO,CAAC,CAAC,SAAS,OAAQ,MAA0C,OAAO,QAAQ,MAAM;AAC3F;AAEA,SAAS,cACP,IACA,OACA,MACM;AACN,MAAK,GAAW,WAAW,kBAAkB;AAC3C,UAAM,UAAW,GAAW;AAC5B,UAAM,WAAW,UAAU,QAAQ,gBAAgB;AACnD,UAAM,WAAW,IAAK,GAAW,OAAO,QAAQ;AAChD,aAAS,QAAQ;AACjB,aAAS,UAAU;AACnB,QAAK,GAAW,0BAA0B;AACxC,YAAM,IAAK,GAAW,yBAAyB,OAAO,SAAS,KAAK;AACpE,UAAI,EAAG,UAAS,QAAQ,EAAE,GAAG,SAAS,OAAO,GAAG,EAAE;AAAA,IACpD;AACA,aAAS,SAAS,OAAO,GAAG,IAAI;AAChC;AAAA,EACF;AACA,QAAM,WAAY,GAAW,KAAK;AAClC,WAAS,UAAU,IAAI;AACzB;AAEA,SAAS,aACP,OACA,MACM;AACN,QAAM,KAAK,KAAK,eAAe;AAG/B,QAAM,kBAAkB,iBAAiB;AAIzC,QAAM,aAAuB,CAAC;AAC9B,QAAM,YAAY,CAAC,MAAc,WAAW,KAAK,CAAC;AAClD,MAAI;AACF,aAAS,MAAM,UAAU;AAAA,MACvB,MAAM;AAAA,MACN,WAAW,KAAK;AAAA,MAChB,gBAAgB,KAAK;AAAA,IACvB,CAAC;AAAA,EACH,SAAS,UAAe;AACtB,QAAI,WAAW,QAAQ,GAAG;AACxB,YAAM,gBAA0B,CAAC;AACjC,UAAI;AACF,iBAAS,MAAM,UAAU;AAAA,UACvB,MAAM,CAAC,MAAM,cAAc,KAAK,CAAC;AAAA,UACjC,WAAW,KAAK;AAAA,UAChB,gBAAgB,KAAK;AAAA,QACvB,CAAC;AAAA,MACH,QAAQ;AAAA,MAER;AACA,mBAAa,MAAM,IAAI,cAAc,KAAK,EAAE,CAAC;AAE7C,UAAI,KAAK,WAAW;AAClB,aAAK,UAAU;AAAA,UACb;AAAA,UACA,cAAc,cAAc,KAAK,EAAE;AAAA,UACnC,UAAU,MAAM;AAAA,UAChB;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AACA;AAAA,IACF;AACA,UAAM;AAAA,EACR;AASA,OAAK,KAAK,QAAQ,EAAE,KAAK;AACzB,OAAK,KAAK,WAAW,KAAK,EAAE,CAAC;AAC7B,OAAK,KAAK,WAAW;AACvB;AAEA,SAAS,aAAa,MAAmB,IAAY,cAA4B;AAI/E,OAAK,KAAK,SAAS,EAAE,iBAAiB,EAAE,IAAI;AAC5C,OAAK,KAAK,YAAY;AACtB,OAAK,KAAK,iBAAiB;AAC7B;AAEA,SAAS,WAAW,GAA2B;AAC7C,SAAO,KAAK,QAAQ,OAAO,EAAE,SAAS;AACxC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/redact",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"description": "React, redacted. A minimal React-API-compatible drop-in replacement.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/react/index.js",
|
|
@@ -78,10 +78,10 @@
|
|
|
78
78
|
"optional": true
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
|
-
"publishConfig": {
|
|
82
|
-
"access": "public"
|
|
83
|
-
},
|
|
84
81
|
"scripts": {
|
|
85
82
|
"build": "echo done-by-root-build"
|
|
83
|
+
},
|
|
84
|
+
"publishConfig": {
|
|
85
|
+
"access": "public"
|
|
86
86
|
}
|
|
87
|
-
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function attributeName(name: string, isSvg = false): string {
|
|
2
|
+
if (isSvg && name !== 'viewBox' && SVG_KEBAB_PREFIX.test(name)) {
|
|
3
|
+
return name.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase())
|
|
4
|
+
}
|
|
5
|
+
if (name === 'className') return 'class'
|
|
6
|
+
if (name === 'htmlFor') return 'for'
|
|
7
|
+
if (name === 'httpEquiv') return 'http-equiv'
|
|
8
|
+
if (name === 'acceptCharset') return 'accept-charset'
|
|
9
|
+
if (name === 'crossOrigin') return 'crossorigin'
|
|
10
|
+
if (name === 'noModule') return 'nomodule'
|
|
11
|
+
if (name === 'viewBox') return 'viewBox'
|
|
12
|
+
return name.startsWith('aria-') || name.startsWith('data-')
|
|
13
|
+
? name
|
|
14
|
+
: name.toLowerCase()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const SVG_KEBAB_PREFIX = /^(?:clip|fill|stroke)/
|
package/src/core/internal.ts
CHANGED
package/src/dom/dispatcher.ts
CHANGED
|
@@ -209,7 +209,9 @@ function makeDispatcherImpl() {
|
|
|
209
209
|
if (hook.s === undefined) {
|
|
210
210
|
const fiber = getCurrentFiber()
|
|
211
211
|
const root = findRootFromFiber(fiber)
|
|
212
|
-
|
|
212
|
+
const prefix = root?.i ?? (root?.h ? ':R' : ':r')
|
|
213
|
+
const id = root?.h ? root.ic++ : idCounter++
|
|
214
|
+
hook.s = prefix + id.toString(36)
|
|
213
215
|
}
|
|
214
216
|
return hook.s as string
|
|
215
217
|
},
|
package/src/dom/dom.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { attributeName } from '../core/attributes'
|
|
2
|
+
|
|
1
3
|
const SVG_NS = 'http://www.w3.org/2000/svg'
|
|
2
4
|
|
|
3
5
|
const BOOLEAN_ATTRS = new Set([
|
|
@@ -60,6 +62,7 @@ export function setProp(
|
|
|
60
62
|
isSvg: boolean,
|
|
61
63
|
): void {
|
|
62
64
|
if (name === 'children' || name === 'key' || name === 'ref') return
|
|
65
|
+
const attr = attributeName(name, isSvg)
|
|
63
66
|
|
|
64
67
|
// defaultValue / defaultChecked are IDL-property-only — they seed the
|
|
65
68
|
// initial value/checked of a form control on first mount and must NOT be
|
|
@@ -123,8 +126,8 @@ export function setProp(
|
|
|
123
126
|
}
|
|
124
127
|
|
|
125
128
|
if (BOOLEAN_ATTRS.has(name.toLowerCase())) {
|
|
126
|
-
if (next) el.setAttribute(
|
|
127
|
-
else el.removeAttribute(
|
|
129
|
+
if (next) el.setAttribute(attr, '')
|
|
130
|
+
else el.removeAttribute(attr)
|
|
128
131
|
return
|
|
129
132
|
}
|
|
130
133
|
|
|
@@ -134,20 +137,20 @@ export function setProp(
|
|
|
134
137
|
if (name.length > 5 && (name.charCodeAt(0) === 97 /* a */ || name.charCodeAt(0) === 100 /* d */)) {
|
|
135
138
|
if (name.startsWith('aria-') || name.startsWith('data-')) {
|
|
136
139
|
if (next == null) {
|
|
137
|
-
el.removeAttribute(
|
|
140
|
+
el.removeAttribute(attr)
|
|
138
141
|
} else {
|
|
139
|
-
el.setAttribute(
|
|
142
|
+
el.setAttribute(attr, '' + next)
|
|
140
143
|
}
|
|
141
144
|
return
|
|
142
145
|
}
|
|
143
146
|
}
|
|
144
147
|
|
|
145
148
|
if (next == null || next === false) {
|
|
146
|
-
el.removeAttribute(
|
|
149
|
+
el.removeAttribute(attr)
|
|
147
150
|
} else if (next === true) {
|
|
148
|
-
el.setAttribute(
|
|
151
|
+
el.setAttribute(attr, '')
|
|
149
152
|
} else {
|
|
150
|
-
el.setAttribute(
|
|
153
|
+
el.setAttribute(attr, '' + next)
|
|
151
154
|
}
|
|
152
155
|
}
|
|
153
156
|
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type ReactElement,
|
|
7
7
|
type ReactNode,
|
|
8
8
|
} from '../../../core'
|
|
9
|
+
import { attributeName } from '../../../core/attributes'
|
|
9
10
|
import { createHostNode, setProp } from '../../dom'
|
|
10
11
|
import { drainReplayQueue } from '../../event-replay'
|
|
11
12
|
import { discardPendingWork, findRoot, flushSyncWork, renderRoot } from '../../reconcile'
|
|
@@ -169,8 +170,6 @@ export function hydrateRootImpl(
|
|
|
169
170
|
|
|
170
171
|
const normalizedInitialChildren =
|
|
171
172
|
isDocument ? normalizeDocumentChildren(initialChildren) : initialChildren
|
|
172
|
-
let documentBodyFallback = false
|
|
173
|
-
|
|
174
173
|
let hydrationError: unknown = null
|
|
175
174
|
beginHydration(root)
|
|
176
175
|
try {
|
|
@@ -182,34 +181,7 @@ export function hydrateRootImpl(
|
|
|
182
181
|
}
|
|
183
182
|
endHydration(root)
|
|
184
183
|
|
|
185
|
-
if (hydrationError)
|
|
186
|
-
if (!isHydrationBailout(hydrationError)) {
|
|
187
|
-
throw hydrationError
|
|
188
|
-
}
|
|
189
|
-
let recoveryContainer: Element | Document = target
|
|
190
|
-
let recoveryChildren = normalizedInitialChildren
|
|
191
|
-
const hostRecovery = getRecoverableHostChildren(hydrationError)
|
|
192
|
-
if (hostRecovery) {
|
|
193
|
-
recoveryContainer = hostRecovery[0]
|
|
194
|
-
recoveryChildren = hostRecovery[1]
|
|
195
|
-
} else if (body) {
|
|
196
|
-
const bodyChildren = getRecoverableDocumentBodyChildren(hydrationError)
|
|
197
|
-
if (bodyChildren != null) {
|
|
198
|
-
documentBodyFallback = true
|
|
199
|
-
recoveryContainer = body
|
|
200
|
-
recoveryChildren = bodyChildren
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
resetAfterHydrationFailure(root, recoveryContainer)
|
|
204
|
-
try {
|
|
205
|
-
flushSyncWork(() => {
|
|
206
|
-
renderRoot(root, recoveryChildren)
|
|
207
|
-
})
|
|
208
|
-
} catch (clientError) {
|
|
209
|
-
resetAfterHydrationFailure(root, recoveryContainer)
|
|
210
|
-
throw clientError
|
|
211
|
-
}
|
|
212
|
-
}
|
|
184
|
+
if (hydrationError && !recoverHydration(root, hydrationError)) throw hydrationError
|
|
213
185
|
drainReplayQueue()
|
|
214
186
|
|
|
215
187
|
return {
|
|
@@ -218,7 +190,7 @@ export function hydrateRootImpl(
|
|
|
218
190
|
const normalized = isDocument ? normalizeDocumentChildren(children) : children
|
|
219
191
|
renderRoot(
|
|
220
192
|
root,
|
|
221
|
-
|
|
193
|
+
root.c === body ? getStaticDocumentBodyChildren(normalized) ?? normalized : normalized,
|
|
222
194
|
)
|
|
223
195
|
})
|
|
224
196
|
},
|
|
@@ -233,7 +205,7 @@ export function hydrateRootImpl(
|
|
|
233
205
|
// Head elements that we match against server DOM by attribute signature.
|
|
234
206
|
const HEAD_KEY_ATTRS: Record<string, ReadonlyArray<string>> = {
|
|
235
207
|
link: ['rel', 'href', 'sizes', 'type'],
|
|
236
|
-
meta: ['name', 'property', '
|
|
208
|
+
meta: ['name', 'property', 'charSet', 'httpEquiv'],
|
|
237
209
|
script: ['src', 'type'],
|
|
238
210
|
}
|
|
239
211
|
|
|
@@ -249,21 +221,17 @@ function headAttrsMatch(
|
|
|
249
221
|
): boolean {
|
|
250
222
|
if (CLAIMED.has(el)) return false
|
|
251
223
|
if (!keys) return true
|
|
252
|
-
let matched = false
|
|
253
224
|
for (const k of keys) {
|
|
254
|
-
const propVal = props[k]
|
|
255
|
-
const elVal = el.getAttribute(k)
|
|
256
|
-
// If neither defines it, skip this key; if one defines it, they must match.
|
|
225
|
+
const propVal = props[k]
|
|
226
|
+
const elVal = el.getAttribute(attributeName(k))
|
|
257
227
|
if (propVal == null && elVal == null) continue
|
|
258
|
-
|
|
259
|
-
if (propVal == null || elVal == null) continue // tolerate missing on either side
|
|
260
|
-
if (String(propVal) !== elVal) return false
|
|
228
|
+
if (propVal == null || elVal == null || String(propVal) !== elVal) return false
|
|
261
229
|
}
|
|
262
|
-
|
|
263
|
-
return matched
|
|
230
|
+
return true
|
|
264
231
|
}
|
|
265
232
|
|
|
266
233
|
export function beginHydration(root: FiberRoot): void {
|
|
234
|
+
root.ic = 0
|
|
267
235
|
root.h = true
|
|
268
236
|
hydrationCursors.set(root.r, new HydrationCursor(root.c))
|
|
269
237
|
}
|
|
@@ -510,6 +478,33 @@ function isSafeHostRecoveryElement(fiber: Fiber): boolean {
|
|
|
510
478
|
return tag !== 'html' && tag !== 'head' && tag !== 'body'
|
|
511
479
|
}
|
|
512
480
|
|
|
481
|
+
export function recoverHydration(root: FiberRoot, error: unknown): boolean {
|
|
482
|
+
if (!isHydrationBailout(error)) return false
|
|
483
|
+
|
|
484
|
+
let container = root.c as Element | Document
|
|
485
|
+
let children = root.r.pp?.children ?? null
|
|
486
|
+
const hostRecovery = getRecoverableHostChildren(error)
|
|
487
|
+
if (hostRecovery) {
|
|
488
|
+
container = hostRecovery[0]
|
|
489
|
+
children = hostRecovery[1]
|
|
490
|
+
} else if (container.nodeType === 9) {
|
|
491
|
+
const bodyChildren = getRecoverableDocumentBodyChildren(error)
|
|
492
|
+
if (bodyChildren != null) {
|
|
493
|
+
container = (container as Document).body
|
|
494
|
+
children = bodyChildren
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
resetAfterHydrationFailure(root, container)
|
|
499
|
+
try {
|
|
500
|
+
flushSyncWork(() => renderRoot(root, children))
|
|
501
|
+
} catch (clientError) {
|
|
502
|
+
resetAfterHydrationFailure(root, container)
|
|
503
|
+
throw clientError
|
|
504
|
+
}
|
|
505
|
+
return true
|
|
506
|
+
}
|
|
507
|
+
|
|
513
508
|
function resetAfterHydrationFailure(
|
|
514
509
|
root: FiberRoot,
|
|
515
510
|
container: Element | Document,
|
|
@@ -679,9 +674,13 @@ function validateHydrationProps(
|
|
|
679
674
|
) continue
|
|
680
675
|
|
|
681
676
|
if (k === 'dangerouslySetInnerHTML') {
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
677
|
+
let html = '' + (value?.__html ?? '')
|
|
678
|
+
if (tag !== 'script' && tag !== 'style') {
|
|
679
|
+
const probe = document.createElement('div')
|
|
680
|
+
probe.innerHTML = html
|
|
681
|
+
html = probe.innerHTML
|
|
682
|
+
}
|
|
683
|
+
if ((el as HTMLElement).innerHTML !== html) {
|
|
685
684
|
if (process.env.NODE_ENV !== 'production') {
|
|
686
685
|
failHydration(
|
|
687
686
|
fiber,
|
|
@@ -746,7 +745,7 @@ function validateHydrationProps(
|
|
|
746
745
|
}
|
|
747
746
|
|
|
748
747
|
const stringifiedBoolean = k.startsWith('aria-') || k.startsWith('data-')
|
|
749
|
-
const attr = k
|
|
748
|
+
const attr = attributeName(k, isSvg)
|
|
750
749
|
|
|
751
750
|
let expectedValue: string | null
|
|
752
751
|
if (value == null || (value === false && !stringifiedBoolean)) {
|
|
@@ -74,6 +74,10 @@ export function isHydrationBailout(_error: unknown): _error is HydrationBailoutE
|
|
|
74
74
|
return false
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
export function recoverHydration(_root: FiberRoot, _error: unknown): boolean {
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
80
|
+
|
|
77
81
|
export function abortHydration(cause: unknown, fiber: Fiber | null = null): never {
|
|
78
82
|
const error = (cause instanceof Error ? cause : new Error('Hydration mismatch.')) as HydrationBailoutError
|
|
79
83
|
;(error as any).f = fiber
|
package/src/dom/reconcile.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
clearHydrationCursor,
|
|
28
28
|
findHostParent as findHydrationHost,
|
|
29
29
|
abortHydration,
|
|
30
|
+
recoverHydration,
|
|
30
31
|
} from './features/hydration'
|
|
31
32
|
|
|
32
33
|
// ---------------------------------------------------------------------------
|
|
@@ -117,7 +118,12 @@ function flushPending(): void {
|
|
|
117
118
|
root.p.clear()
|
|
118
119
|
pending.sort((a, b) => fiberDepth(a) - fiberDepth(b))
|
|
119
120
|
for (const fiber of pending) {
|
|
120
|
-
|
|
121
|
+
try {
|
|
122
|
+
rerenderFiber(fiber, root)
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (!recoverHydration(root, error)) throw error
|
|
125
|
+
break
|
|
126
|
+
}
|
|
121
127
|
}
|
|
122
128
|
runEffects(root)
|
|
123
129
|
}
|
|
@@ -815,7 +821,12 @@ function renderHost(fiber: Fiber, domParent: Node, anchor: Node | null): void {
|
|
|
815
821
|
const hasOpaqueHydrationChildren =
|
|
816
822
|
props.dangerouslySetInnerHTML != null ||
|
|
817
823
|
(parentTag === 'textarea' && (props.value != null || props.defaultValue != null))
|
|
818
|
-
if (
|
|
824
|
+
if (
|
|
825
|
+
parentTag !== 'head' &&
|
|
826
|
+
parentTag !== 'html' &&
|
|
827
|
+
parentTag !== 'body' &&
|
|
828
|
+
!hasOpaqueHydrationChildren
|
|
829
|
+
) {
|
|
819
830
|
const cursor = getHydrationCursor(fiber)
|
|
820
831
|
if (cursor) {
|
|
821
832
|
if (cursor.has()) {
|
package/src/dom/root-internal.ts
CHANGED
|
@@ -20,7 +20,8 @@ export function createFiberRoot(
|
|
|
20
20
|
re: options.onRecoverableError,
|
|
21
21
|
ce: options.onCaughtError,
|
|
22
22
|
ue: options.onUncaughtError,
|
|
23
|
-
i: options.identifierPrefix
|
|
23
|
+
i: options.identifierPrefix,
|
|
24
|
+
ic: 0,
|
|
24
25
|
h: false,
|
|
25
26
|
}
|
|
26
27
|
rootFiber.root = root
|
|
@@ -34,6 +35,7 @@ export function attachRootFiber(
|
|
|
34
35
|
): void {
|
|
35
36
|
const rootFiber = createFiber(FiberTag.Root, null, null)
|
|
36
37
|
root.c = container as any
|
|
38
|
+
root.ic = 0
|
|
37
39
|
rootFiber.root = root
|
|
38
40
|
rootFiber.sn = container
|
|
39
41
|
root.r = rootFiber
|
package/src/server/escape.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { attributeName } from '../core/attributes'
|
|
2
|
+
|
|
1
3
|
const ATTR_MAP: Record<string, string> = {
|
|
2
4
|
'&': '&',
|
|
3
5
|
'"': '"',
|
|
@@ -48,17 +50,6 @@ export const VOID_ELEMENTS = new Set([
|
|
|
48
50
|
|
|
49
51
|
export const RAW_TEXT_ELEMENTS = new Set(['script', 'style'])
|
|
50
52
|
|
|
51
|
-
// Map JSX prop names to HTML attribute names where they differ
|
|
52
|
-
export const ATTR_ALIASES: Record<string, string> = {
|
|
53
|
-
className: 'class',
|
|
54
|
-
htmlFor: 'for',
|
|
55
|
-
httpEquiv: 'http-equiv',
|
|
56
|
-
acceptCharset: 'accept-charset',
|
|
57
|
-
crossOrigin: 'crossorigin',
|
|
58
|
-
viewBox: 'viewBox', // SVG keeps camelCase
|
|
59
|
-
noModule: 'nomodule',
|
|
60
|
-
}
|
|
61
|
-
|
|
62
53
|
const BOOLEAN_ATTRS = new Set([
|
|
63
54
|
'allowfullscreen',
|
|
64
55
|
'async',
|
|
@@ -87,7 +78,7 @@ const BOOLEAN_ATTRS = new Set([
|
|
|
87
78
|
'selected',
|
|
88
79
|
])
|
|
89
80
|
|
|
90
|
-
export function attrToHtml(name: string, value: unknown): string {
|
|
81
|
+
export function attrToHtml(name: string, value: unknown, isSvg = false): string {
|
|
91
82
|
if (
|
|
92
83
|
name === 'children' ||
|
|
93
84
|
name === 'key' ||
|
|
@@ -103,7 +94,7 @@ export function attrToHtml(name: string, value: unknown): string {
|
|
|
103
94
|
if (name[0] === 'o' && name[1] === 'n' && typeof value === 'function') return ''
|
|
104
95
|
if (value == null) return ''
|
|
105
96
|
|
|
106
|
-
const htmlName =
|
|
97
|
+
const htmlName = attributeName(name, isSvg)
|
|
107
98
|
|
|
108
99
|
// aria-* and data-* stringify booleans to `"true"`/`"false"` rather than
|
|
109
100
|
// using boolean-attribute presence semantics — matches React and the ARIA
|
package/src/server/walk.ts
CHANGED
|
@@ -44,6 +44,7 @@ export interface WalkOptions {
|
|
|
44
44
|
nextBoundaryId: () => number
|
|
45
45
|
bootstrapped?: boolean | undefined
|
|
46
46
|
isBoundaryResolution?: boolean | undefined
|
|
47
|
+
isSvg?: boolean | undefined
|
|
47
48
|
/**
|
|
48
49
|
* Tracks whether the most recent emission within the *current text flow*
|
|
49
50
|
* ended with a text node. When the next emission is also text, we emit a
|
|
@@ -232,6 +233,7 @@ function walkHost(
|
|
|
232
233
|
props: Record<string, any>,
|
|
233
234
|
opts: WalkOptions,
|
|
234
235
|
): void {
|
|
236
|
+
const isSvg = opts.isSvg || tag === 'svg'
|
|
235
237
|
// <textarea value="..."> serializes its value as a TEXT CHILD, not an
|
|
236
238
|
// attribute. `defaultValue` is the fallback when `value` is absent. This
|
|
237
239
|
// matches React and the HTML spec — `<textarea value="x">` is not valid
|
|
@@ -282,13 +284,13 @@ function walkHost(
|
|
|
282
284
|
if (isInput && (k === 'defaultValue' || k === 'defaultChecked')) continue
|
|
283
285
|
if (isSelect && (k === 'value' || k === 'defaultValue')) continue
|
|
284
286
|
if (isOption && k === 'selected') continue
|
|
285
|
-
opts.emit(attrToHtml(k, props[k]))
|
|
287
|
+
opts.emit(attrToHtml(k, props[k], isSvg))
|
|
286
288
|
}
|
|
287
289
|
if (inputValueAttr !== undefined) {
|
|
288
|
-
opts.emit(attrToHtml('value', inputValueAttr))
|
|
290
|
+
opts.emit(attrToHtml('value', inputValueAttr, isSvg))
|
|
289
291
|
}
|
|
290
292
|
if (inputCheckedAttr !== undefined) {
|
|
291
|
-
opts.emit(attrToHtml('checked', inputCheckedAttr))
|
|
293
|
+
opts.emit(attrToHtml('checked', inputCheckedAttr, isSvg))
|
|
292
294
|
}
|
|
293
295
|
if (isOption) {
|
|
294
296
|
const selectVal = currentSelectValue()
|
|
@@ -313,7 +315,11 @@ function walkHost(
|
|
|
313
315
|
// Opening a host element starts a fresh text flow context for its children.
|
|
314
316
|
// Children's text separator tracking is independent of the outer context.
|
|
315
317
|
const parentTextState = opts.textState
|
|
316
|
-
const childOpts: WalkOptions = {
|
|
318
|
+
const childOpts: WalkOptions = {
|
|
319
|
+
...opts,
|
|
320
|
+
isSvg: isSvg && tag !== 'foreignObject',
|
|
321
|
+
textState: { lastWasText: false },
|
|
322
|
+
}
|
|
317
323
|
|
|
318
324
|
if (tag === 'html' && !hasHeadChild(props.children)) {
|
|
319
325
|
opts.emit('<head></head>')
|