@askrjs/askr 0.0.34 → 0.0.36
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/bench/components/benchmark-row.js.map +1 -1
- package/dist/bench/components/benchmark-table.js.map +1 -1
- package/dist/benchmark.js +2 -2
- package/dist/benchmark.js.map +1 -1
- package/dist/bin/askr-ssg.js.map +1 -1
- package/dist/boot/index.d.ts.map +1 -1
- package/dist/boot/index.js +9 -29
- package/dist/boot/index.js.map +1 -1
- package/dist/common/control.js.map +1 -1
- package/dist/common/env.js.map +1 -1
- package/dist/common/router.d.ts +8 -1
- package/dist/common/router.d.ts.map +1 -1
- package/dist/common/ssr-errors.js.map +1 -1
- package/dist/common/vnode.js.map +1 -1
- package/dist/components/error-boundary.js.map +1 -1
- package/dist/components/link.js.map +1 -1
- package/dist/control/case.js.map +1 -1
- package/dist/control/for.js.map +1 -1
- package/dist/control/shared.js.map +1 -1
- package/dist/control/show.js.map +1 -1
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +165 -23
- package/dist/data/index.js.map +1 -1
- package/dist/dev/invariant.js.map +1 -1
- package/dist/dev/logger.js.map +1 -1
- package/dist/foundations/icon/icon.js.map +1 -1
- package/dist/foundations/interactions/dismissable.js.map +1 -1
- package/dist/foundations/interactions/focusable.js.map +1 -1
- package/dist/foundations/interactions/hoverable.js.map +1 -1
- package/dist/foundations/interactions/interaction-policy.js.map +1 -1
- package/dist/foundations/interactions/pressable.js.map +1 -1
- package/dist/foundations/interactions/roving-focus.js.map +1 -1
- package/dist/foundations/state/controllable.js.map +1 -1
- package/dist/foundations/structures/collection.js.map +1 -1
- package/dist/foundations/structures/layer.js.map +1 -1
- package/dist/foundations/structures/layout.js.map +1 -1
- package/dist/foundations/structures/portal.js.map +1 -1
- package/dist/foundations/structures/presence.js.map +1 -1
- package/dist/foundations/structures/slot.js.map +1 -1
- package/dist/foundations/utilities/aria.js.map +1 -1
- package/dist/foundations/utilities/compose-handlers.js.map +1 -1
- package/dist/foundations/utilities/compose-ref.js.map +1 -1
- package/dist/foundations/utilities/merge-props.js.map +1 -1
- package/dist/foundations/utilities/use-id.js.map +1 -1
- package/dist/fx/fx.js.map +1 -1
- package/dist/fx/timing.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/jsx/utils.js.map +1 -1
- package/dist/jsx-dev-runtime.js.map +1 -1
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/renderer/children.js.map +1 -1
- package/dist/renderer/cleanup.js.map +1 -1
- package/dist/renderer/dom.js +128 -53
- package/dist/renderer/dom.js.map +1 -1
- package/dist/renderer/evaluate.js +7 -0
- package/dist/renderer/evaluate.js.map +1 -1
- package/dist/renderer/fastpath.js.map +1 -1
- package/dist/renderer/index.js.map +1 -1
- package/dist/renderer/keyed.js +1 -1
- package/dist/renderer/keyed.js.map +1 -1
- package/dist/renderer/reconcile.js.map +1 -1
- package/dist/renderer/utils.js.map +1 -1
- package/dist/router/index.d.ts +3 -3
- package/dist/router/index.js +2 -2
- package/dist/router/match.js.map +1 -1
- package/dist/router/navigate.d.ts.map +1 -1
- package/dist/router/navigate.js +8 -3
- package/dist/router/navigate.js.map +1 -1
- package/dist/router/policy.js.map +1 -1
- package/dist/router/route-context.js.map +1 -1
- package/dist/router/route.d.ts +11 -2
- package/dist/router/route.d.ts.map +1 -1
- package/dist/router/route.js +268 -109
- package/dist/router/route.js.map +1 -1
- package/dist/runtime/child-scope.d.ts +1 -0
- package/dist/runtime/child-scope.d.ts.map +1 -1
- package/dist/runtime/child-scope.js +2 -0
- package/dist/runtime/child-scope.js.map +1 -1
- package/dist/runtime/component.js +1 -1
- package/dist/runtime/component.js.map +1 -1
- package/dist/runtime/context.js.map +1 -1
- package/dist/runtime/control.js.map +1 -1
- package/dist/runtime/derive.d.ts +2 -1
- package/dist/runtime/derive.d.ts.map +1 -1
- package/dist/runtime/derive.js +15 -10
- package/dist/runtime/derive.js.map +1 -1
- package/dist/runtime/dev-namespace.js.map +1 -1
- package/dist/runtime/effect.d.ts.map +1 -1
- package/dist/runtime/effect.js +34 -9
- package/dist/runtime/effect.js.map +1 -1
- package/dist/runtime/events.js.map +1 -1
- package/dist/runtime/execution-model.js.map +1 -1
- package/dist/runtime/fastlane.js.map +1 -1
- package/dist/runtime/for-bench.d.ts +1 -1
- package/dist/runtime/for-bench.d.ts.map +1 -1
- package/dist/runtime/for-bench.js +15 -14
- package/dist/runtime/for-bench.js.map +1 -1
- package/dist/runtime/for.d.ts +1 -0
- package/dist/runtime/for.d.ts.map +1 -1
- package/dist/runtime/for.js +63 -16
- package/dist/runtime/for.js.map +1 -1
- package/dist/runtime/operations.d.ts.map +1 -1
- package/dist/runtime/operations.js +9 -7
- package/dist/runtime/operations.js.map +1 -1
- package/dist/runtime/perf-metrics.js +8 -2
- package/dist/runtime/perf-metrics.js.map +1 -1
- package/dist/runtime/readable.d.ts +1 -1
- package/dist/runtime/readable.d.ts.map +1 -1
- package/dist/runtime/readable.js +2 -1
- package/dist/runtime/readable.js.map +1 -1
- package/dist/runtime/resource-cell.js +3 -2
- package/dist/runtime/resource-cell.js.map +1 -1
- package/dist/runtime/scheduler.d.ts +0 -1
- package/dist/runtime/scheduler.d.ts.map +1 -1
- package/dist/runtime/scheduler.js +6 -19
- package/dist/runtime/scheduler.js.map +1 -1
- package/dist/runtime/selector.d.ts.map +1 -1
- package/dist/runtime/selector.js +6 -4
- package/dist/runtime/selector.js.map +1 -1
- package/dist/runtime/snapshot-source.d.ts +10 -0
- package/dist/runtime/snapshot-source.d.ts.map +1 -0
- package/dist/runtime/snapshot-source.js +17 -0
- package/dist/runtime/snapshot-source.js.map +1 -0
- package/dist/runtime/ssr-bridge.js.map +1 -1
- package/dist/runtime/state.d.ts.map +1 -1
- package/dist/runtime/state.js +1 -5
- package/dist/runtime/state.js.map +1 -1
- package/dist/ssg/batch-render.js.map +1 -1
- package/dist/ssg/create-static-gen.js.map +1 -1
- package/dist/ssg/generate-metadata.js.map +1 -1
- package/dist/ssg/incremental-manifest.js.map +1 -1
- package/dist/ssg/resolve-ssg-data.js.map +1 -1
- package/dist/ssg/route-utils.js.map +1 -1
- package/dist/ssg/write-static-files.js.map +1 -1
- package/dist/ssr/attrs.js.map +1 -1
- package/dist/ssr/context.d.ts +1 -0
- package/dist/ssr/context.d.ts.map +1 -1
- package/dist/ssr/context.js +3 -0
- package/dist/ssr/context.js.map +1 -1
- package/dist/ssr/escape.js +56 -16
- package/dist/ssr/escape.js.map +1 -1
- package/dist/ssr/index.d.ts.map +1 -1
- package/dist/ssr/index.js +15 -3
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/render-keys.js.map +1 -1
- package/dist/ssr/sink.js.map +1 -1
- package/dist/ssr/stream-render.js +14 -2
- package/dist/ssr/stream-render.js.map +1 -1
- package/package.json +8 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"children.js","names":[],"sources":["../../src/renderer/children.ts"],"sourcesContent":["import { setDevValue, incDevCounter } from '../runtime/dev-namespace';\nimport { getRuntimeEnv } from './env';\nimport { keyedElements } from './keyed';\nimport { teardownNodeSubtree } from './cleanup';\nimport { createDOMNode, updateElementFromVnode } from './dom';\nimport { _isDOMElement, type DOMElement, type VNode } from './types';\nimport {\n hasNonTrivialProps,\n logFastPathDebug,\n now,\n recordDOMReplace,\n recordFastPathStats,\n tagNamesEqualIgnoreCase,\n} from './utils';\n\nfunction upperCommonTagName(tag: string): string | null {\n switch (tag) {\n case 'div':\n return 'DIV';\n case 'span':\n return 'SPAN';\n case 'p':\n return 'P';\n case 'a':\n return 'A';\n case 'button':\n return 'BUTTON';\n case 'input':\n return 'INPUT';\n case 'ul':\n return 'UL';\n case 'ol':\n return 'OL';\n case 'li':\n return 'LI';\n default:\n return null;\n }\n}\n\nfunction tagsEqualIgnoreCase(\n elementTagName: string,\n vnodeType: string\n): boolean {\n if (elementTagName === vnodeType) return true;\n\n const upperCommon = upperCommonTagName(vnodeType);\n if (upperCommon !== null && elementTagName === upperCommon) return true;\n\n return tagNamesEqualIgnoreCase(elementTagName, vnodeType);\n}\n\nexport function performBulkPositionalKeyedTextUpdate(\n parent: Element,\n keyedVnodes: Array<{ key: string | number; vnode: VNode }>\n) {\n const total = keyedVnodes.length;\n let reused = 0;\n let updatedKeys = 0;\n const start = now();\n const env = getRuntimeEnv();\n const debugFastPath =\n env.ASKR_FASTPATH_DEBUG === '1' || env.ASKR_FASTPATH_DEBUG === 'true';\n\n for (let index = 0; index < total; index += 1) {\n const { key, vnode } = keyedVnodes[index];\n const child = parent.children[index] as Element | undefined;\n\n if (\n child &&\n _isDOMElement(vnode) &&\n typeof (vnode as DOMElement).type === 'string'\n ) {\n const vnodeType = (vnode as DOMElement).type as string;\n\n if (tagsEqualIgnoreCase(child.tagName, vnodeType)) {\n const children =\n (vnode as DOMElement).children ||\n (vnode as DOMElement).props?.children;\n\n if (debugFastPath) {\n logFastPathDebug('positional idx', index, {\n chTag: child.tagName,\n vnodeType,\n chChildNodes: child.childNodes.length,\n childrenType: Array.isArray(children) ? 'array' : typeof children,\n });\n }\n\n updateTextContent(child, children, vnode as DOMElement);\n setDataKey(child, key, () => (updatedKeys += 1));\n reused += 1;\n continue;\n }\n\n if (debugFastPath) {\n logFastPathDebug('positional tag mismatch', index, {\n chTag: child.tagName,\n vnodeType,\n });\n }\n } else if (debugFastPath) {\n logFastPathDebug('positional missing or invalid', index, { ch: !!child });\n }\n\n replaceNodeAtPosition(parent, index, vnode);\n }\n\n const elapsed = now() - start;\n updateKeyedElementsMap(parent, keyedVnodes);\n\n const stats = { n: total, reused, updatedKeys, t: elapsed } as const;\n recordFastPathStats(stats, 'bulkKeyedPositionalHits');\n\n return stats;\n}\n\nfunction updateTextContent(\n el: Element,\n children: unknown,\n vnode: DOMElement\n): void {\n if (typeof children === 'string' || typeof children === 'number') {\n setTextNodeData(el, String(children));\n if (vnode.props && hasNonTrivialProps(vnode.props)) {\n updateElementFromVnode(el, vnode, false);\n }\n return;\n }\n\n if (\n Array.isArray(children) &&\n children.length === 1 &&\n (typeof children[0] === 'string' || typeof children[0] === 'number')\n ) {\n setTextNodeData(el, String(children[0]));\n if (vnode.props && hasNonTrivialProps(vnode.props)) {\n updateElementFromVnode(el, vnode, false);\n }\n return;\n }\n\n if (!tryUpdateTwoChildTextPattern(el, vnode)) {\n updateElementFromVnode(el, vnode);\n }\n}\n\nfunction tryUpdateTwoChildTextPattern(\n parentEl: Element,\n vnode: DOMElement\n): boolean {\n const vnodeChildren = vnode.props?.children ?? vnode.children;\n if (!Array.isArray(vnodeChildren) || vnodeChildren.length !== 2) return false;\n\n const firstChild = vnodeChildren[0];\n const secondChild = vnodeChildren[1];\n if (!_isDOMElement(firstChild) || !_isDOMElement(secondChild)) return false;\n if (\n typeof firstChild.type !== 'string' ||\n typeof secondChild.type !== 'string'\n ) {\n return false;\n }\n\n const firstElement = parentEl.children[0] as Element | undefined;\n const secondElement = parentEl.children[1] as Element | undefined;\n if (!firstElement || !secondElement) return false;\n if (parentEl.children.length !== 2 || parentEl.childNodes.length !== 2) {\n return false;\n }\n\n if (!tagsEqualIgnoreCase(firstElement.tagName, firstChild.type)) return false;\n if (!tagsEqualIgnoreCase(secondElement.tagName, secondChild.type))\n return false;\n\n const firstText = (firstChild.children ||\n firstChild.props?.children) as unknown;\n const secondText = (secondChild.children ||\n secondChild.props?.children) as unknown;\n\n if (typeof firstText === 'string' || typeof firstText === 'number') {\n setTextNodeData(firstElement, String(firstText));\n } else if (\n Array.isArray(firstText) &&\n firstText.length === 1 &&\n (typeof firstText[0] === 'string' || typeof firstText[0] === 'number')\n ) {\n setTextNodeData(firstElement, String(firstText[0]));\n } else {\n return false;\n }\n\n if (typeof secondText === 'string' || typeof secondText === 'number') {\n setTextNodeData(secondElement, String(secondText));\n } else if (\n Array.isArray(secondText) &&\n secondText.length === 1 &&\n (typeof secondText[0] === 'string' || typeof secondText[0] === 'number')\n ) {\n setTextNodeData(secondElement, String(secondText[0]));\n } else {\n return false;\n }\n\n return true;\n}\n\nfunction setTextNodeData(el: Element, text: string): void {\n if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {\n const textNode = el.firstChild as Text;\n if (textNode.data !== text) textNode.data = text;\n } else {\n el.textContent = text;\n }\n}\n\nfunction setDataKey(\n el: Element,\n key: string | number,\n onSet: () => void\n): void {\n try {\n const next = String(key);\n if (el.getAttribute('data-key') === next) return;\n el.setAttribute('data-key', next);\n onSet();\n } catch {\n // Ignore errors setting data-key\n }\n}\n\nfunction replaceNodeAtPosition(\n parent: Element,\n index: number,\n vnode: VNode\n): void {\n const dom = createDOMNode(vnode);\n if (dom) {\n const existing = parent.children[index];\n if (existing) {\n teardownNodeSubtree(existing);\n parent.replaceChild(dom, existing);\n } else {\n parent.appendChild(dom);\n }\n }\n}\n\nfunction updateKeyedElementsMap(\n parent: Element,\n keyedVnodes: Array<{ key: string | number; vnode: VNode }>\n): void {\n try {\n const existing = keyedElements.get(parent);\n const newKeyMap = existing\n ? (existing.clear(), existing)\n : new Map<string | number, Element>();\n\n for (let index = 0; index < keyedVnodes.length; index += 1) {\n const key = keyedVnodes[index].key;\n const child = parent.children[index] as Element | undefined;\n if (child) newKeyMap.set(key, child);\n }\n\n keyedElements.set(parent, newKeyMap);\n } catch {\n // Ignore errors updating key map\n }\n}\n\nexport function performBulkTextReplace(parent: Element, newChildren: VNode[]) {\n const start = now();\n const existing = Array.from(parent.childNodes);\n const finalNodes: Node[] = [];\n let reused = 0;\n let created = 0;\n\n for (let index = 0; index < newChildren.length; index += 1) {\n const result = processChildNode(\n newChildren[index],\n existing[index],\n finalNodes\n );\n if (result === 'reused') reused += 1;\n else if (result === 'created') created += 1;\n }\n\n const tBuild = now() - start;\n const tCommit = commitBulkReplace(parent, finalNodes);\n\n keyedElements.delete(parent);\n\n const stats = {\n n: newChildren.length,\n reused,\n created,\n tBuild,\n tCommit,\n } as const;\n recordBulkTextStats(stats);\n\n return stats;\n}\n\nfunction processChildNode(\n vnode: VNode,\n existingNode: ChildNode | undefined,\n finalNodes: Node[]\n): 'reused' | 'created' | 'skipped' {\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n return processTextVnode(String(vnode), existingNode, finalNodes);\n }\n\n if (typeof vnode === 'object' && vnode !== null && 'type' in vnode) {\n return processElementVnode(vnode, existingNode, finalNodes);\n }\n\n return 'skipped';\n}\n\nfunction processTextVnode(\n text: string,\n existingNode: ChildNode | undefined,\n finalNodes: Node[]\n): 'reused' | 'created' {\n if (existingNode && existingNode.nodeType === 3) {\n (existingNode as Text).data = text;\n finalNodes.push(existingNode);\n return 'reused';\n }\n\n finalNodes.push(document.createTextNode(text));\n return 'created';\n}\n\nfunction processElementVnode(\n vnode: VNode,\n existingNode: ChildNode | undefined,\n finalNodes: Node[]\n): 'reused' | 'created' | 'skipped' {\n const vnodeObj = vnode as unknown as { type?: unknown };\n\n if (typeof vnodeObj.type === 'string') {\n const tag = vnodeObj.type;\n if (\n existingNode &&\n existingNode.nodeType === 1 &&\n tagsEqualIgnoreCase((existingNode as Element).tagName, tag)\n ) {\n updateElementFromVnode(existingNode as Element, vnode);\n finalNodes.push(existingNode);\n return 'reused';\n }\n }\n\n const dom = createDOMNode(vnode);\n if (dom) {\n finalNodes.push(dom);\n return 'created';\n }\n\n return 'skipped';\n}\n\nfunction commitBulkReplace(parent: Element, nodes: Node[]): number {\n const startedAt = Date.now();\n const fragment = document.createDocumentFragment();\n\n for (let index = 0; index < nodes.length; index += 1) {\n fragment.appendChild(nodes[index]);\n }\n\n try {\n for (let node = parent.firstChild; node; ) {\n const next = node.nextSibling;\n teardownNodeSubtree(node);\n node = next;\n }\n } catch {\n // SLOW PATH: cleanup failure\n }\n\n recordDOMReplace('bulk-text-replace');\n parent.replaceChildren(fragment);\n return Date.now() - startedAt;\n}\n\nfunction recordBulkTextStats(stats: {\n n: number;\n reused: number;\n created: number;\n tBuild: number;\n tCommit: number;\n}): void {\n try {\n setDevValue('__LAST_BULK_TEXT_FASTPATH_STATS', stats);\n setDevValue('__LAST_FASTPATH_STATS', stats);\n setDevValue('__LAST_FASTPATH_COMMIT_COUNT', 1);\n incDevCounter('bulkTextFastpathHits');\n } catch {\n // Ignore stats errors\n }\n}\n\nexport function isBulkTextFastPathEligible(\n parent: Element,\n newChildren: VNode[]\n) {\n const env = getRuntimeEnv();\n const threshold = Number(env.ASKR_BULK_TEXT_THRESHOLD) || 1024;\n const requiredFraction = 0.8;\n\n const total = Array.isArray(newChildren) ? newChildren.length : 0;\n\n if (total < threshold) {\n recordBulkDiag({\n phase: 'bulk-unkeyed-eligible',\n reason: 'too-small',\n total,\n threshold,\n });\n return false;\n }\n\n const result = countSimpleChildren(newChildren);\n if (result.componentFound !== undefined) {\n recordBulkDiag({\n phase: 'bulk-unkeyed-eligible',\n reason: 'component-child',\n index: result.componentFound,\n });\n return false;\n }\n\n const fraction = result.simple / total;\n const eligible =\n fraction >= requiredFraction && parent.childNodes.length >= total;\n\n recordBulkDiag({\n phase: 'bulk-unkeyed-eligible',\n total,\n simple: result.simple,\n fraction,\n requiredFraction,\n eligible,\n });\n\n return eligible;\n}\n\nfunction countSimpleChildren(children: VNode[]): {\n simple: number;\n componentFound?: number;\n} {\n let simple = 0;\n\n for (let index = 0; index < children.length; index += 1) {\n const child = children[index];\n\n if (typeof child === 'string' || typeof child === 'number') {\n simple += 1;\n continue;\n }\n\n if (typeof child === 'object' && child !== null && 'type' in child) {\n const dom = child as DOMElement;\n\n if (typeof dom.type === 'function') {\n return { simple, componentFound: index };\n }\n\n if (typeof dom.type === 'string' && isSimpleElement(dom)) {\n simple += 1;\n }\n }\n }\n\n return { simple };\n}\n\nfunction isSimpleElement(dom: DOMElement): boolean {\n const children = dom.props?.children ?? dom.children;\n\n if (children === null || children === undefined) return true;\n\n if (typeof children === 'string' || typeof children === 'number') {\n return true;\n }\n\n if (\n Array.isArray(children) &&\n children.length === 1 &&\n (typeof children[0] === 'string' || typeof children[0] === 'number')\n ) {\n return true;\n }\n\n return false;\n}\n\nfunction recordBulkDiag(data: Record<string, unknown>): void {\n const env = getRuntimeEnv();\n if (env.NODE_ENV !== 'production' || env.ASKR_FASTPATH_DEBUG === '1') {\n try {\n setDevValue('__BULK_DIAG', data);\n } catch {\n // Ignore\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"children.js","names":[],"sources":["../../src/renderer/children.ts"],"sourcesContent":["import { setDevValue, incDevCounter } from '../runtime/dev-namespace';\nimport { getRuntimeEnv } from './env';\nimport { keyedElements } from './keyed';\nimport { teardownNodeSubtree } from './cleanup';\nimport { createDOMNode, updateElementFromVnode } from './dom';\nimport { _isDOMElement, type DOMElement, type VNode } from './types';\nimport {\n hasNonTrivialProps,\n logFastPathDebug,\n now,\n recordDOMReplace,\n recordFastPathStats,\n tagNamesEqualIgnoreCase,\n} from './utils';\n\nfunction upperCommonTagName(tag: string): string | null {\n switch (tag) {\n case 'div':\n return 'DIV';\n case 'span':\n return 'SPAN';\n case 'p':\n return 'P';\n case 'a':\n return 'A';\n case 'button':\n return 'BUTTON';\n case 'input':\n return 'INPUT';\n case 'ul':\n return 'UL';\n case 'ol':\n return 'OL';\n case 'li':\n return 'LI';\n default:\n return null;\n }\n}\n\nfunction tagsEqualIgnoreCase(\n elementTagName: string,\n vnodeType: string\n): boolean {\n if (elementTagName === vnodeType) return true;\n\n const upperCommon = upperCommonTagName(vnodeType);\n if (upperCommon !== null && elementTagName === upperCommon) return true;\n\n return tagNamesEqualIgnoreCase(elementTagName, vnodeType);\n}\n\nexport function performBulkPositionalKeyedTextUpdate(\n parent: Element,\n keyedVnodes: Array<{ key: string | number; vnode: VNode }>\n) {\n const total = keyedVnodes.length;\n let reused = 0;\n let updatedKeys = 0;\n const start = now();\n const env = getRuntimeEnv();\n const debugFastPath =\n env.ASKR_FASTPATH_DEBUG === '1' || env.ASKR_FASTPATH_DEBUG === 'true';\n\n for (let index = 0; index < total; index += 1) {\n const { key, vnode } = keyedVnodes[index];\n const child = parent.children[index] as Element | undefined;\n\n if (\n child &&\n _isDOMElement(vnode) &&\n typeof (vnode as DOMElement).type === 'string'\n ) {\n const vnodeType = (vnode as DOMElement).type as string;\n\n if (tagsEqualIgnoreCase(child.tagName, vnodeType)) {\n const children =\n (vnode as DOMElement).children ||\n (vnode as DOMElement).props?.children;\n\n if (debugFastPath) {\n logFastPathDebug('positional idx', index, {\n chTag: child.tagName,\n vnodeType,\n chChildNodes: child.childNodes.length,\n childrenType: Array.isArray(children) ? 'array' : typeof children,\n });\n }\n\n updateTextContent(child, children, vnode as DOMElement);\n setDataKey(child, key, () => (updatedKeys += 1));\n reused += 1;\n continue;\n }\n\n if (debugFastPath) {\n logFastPathDebug('positional tag mismatch', index, {\n chTag: child.tagName,\n vnodeType,\n });\n }\n } else if (debugFastPath) {\n logFastPathDebug('positional missing or invalid', index, { ch: !!child });\n }\n\n replaceNodeAtPosition(parent, index, vnode);\n }\n\n const elapsed = now() - start;\n updateKeyedElementsMap(parent, keyedVnodes);\n\n const stats = { n: total, reused, updatedKeys, t: elapsed } as const;\n recordFastPathStats(stats, 'bulkKeyedPositionalHits');\n\n return stats;\n}\n\nfunction updateTextContent(\n el: Element,\n children: unknown,\n vnode: DOMElement\n): void {\n if (typeof children === 'string' || typeof children === 'number') {\n setTextNodeData(el, String(children));\n if (vnode.props && hasNonTrivialProps(vnode.props)) {\n updateElementFromVnode(el, vnode, false);\n }\n return;\n }\n\n if (\n Array.isArray(children) &&\n children.length === 1 &&\n (typeof children[0] === 'string' || typeof children[0] === 'number')\n ) {\n setTextNodeData(el, String(children[0]));\n if (vnode.props && hasNonTrivialProps(vnode.props)) {\n updateElementFromVnode(el, vnode, false);\n }\n return;\n }\n\n if (!tryUpdateTwoChildTextPattern(el, vnode)) {\n updateElementFromVnode(el, vnode);\n }\n}\n\nfunction tryUpdateTwoChildTextPattern(\n parentEl: Element,\n vnode: DOMElement\n): boolean {\n const vnodeChildren = vnode.props?.children ?? vnode.children;\n if (!Array.isArray(vnodeChildren) || vnodeChildren.length !== 2) return false;\n\n const firstChild = vnodeChildren[0];\n const secondChild = vnodeChildren[1];\n if (!_isDOMElement(firstChild) || !_isDOMElement(secondChild)) return false;\n if (\n typeof firstChild.type !== 'string' ||\n typeof secondChild.type !== 'string'\n ) {\n return false;\n }\n\n const firstElement = parentEl.children[0] as Element | undefined;\n const secondElement = parentEl.children[1] as Element | undefined;\n if (!firstElement || !secondElement) return false;\n if (parentEl.children.length !== 2 || parentEl.childNodes.length !== 2) {\n return false;\n }\n\n if (!tagsEqualIgnoreCase(firstElement.tagName, firstChild.type)) return false;\n if (!tagsEqualIgnoreCase(secondElement.tagName, secondChild.type))\n return false;\n\n const firstText = (firstChild.children ||\n firstChild.props?.children) as unknown;\n const secondText = (secondChild.children ||\n secondChild.props?.children) as unknown;\n\n if (typeof firstText === 'string' || typeof firstText === 'number') {\n setTextNodeData(firstElement, String(firstText));\n } else if (\n Array.isArray(firstText) &&\n firstText.length === 1 &&\n (typeof firstText[0] === 'string' || typeof firstText[0] === 'number')\n ) {\n setTextNodeData(firstElement, String(firstText[0]));\n } else {\n return false;\n }\n\n if (typeof secondText === 'string' || typeof secondText === 'number') {\n setTextNodeData(secondElement, String(secondText));\n } else if (\n Array.isArray(secondText) &&\n secondText.length === 1 &&\n (typeof secondText[0] === 'string' || typeof secondText[0] === 'number')\n ) {\n setTextNodeData(secondElement, String(secondText[0]));\n } else {\n return false;\n }\n\n return true;\n}\n\nfunction setTextNodeData(el: Element, text: string): void {\n if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {\n const textNode = el.firstChild as Text;\n if (textNode.data !== text) textNode.data = text;\n } else {\n el.textContent = text;\n }\n}\n\nfunction setDataKey(\n el: Element,\n key: string | number,\n onSet: () => void\n): void {\n try {\n const next = String(key);\n if (el.getAttribute('data-key') === next) return;\n el.setAttribute('data-key', next);\n onSet();\n } catch {\n // Ignore errors setting data-key\n }\n}\n\nfunction replaceNodeAtPosition(\n parent: Element,\n index: number,\n vnode: VNode\n): void {\n const dom = createDOMNode(vnode);\n if (dom) {\n const existing = parent.children[index];\n if (existing) {\n teardownNodeSubtree(existing);\n parent.replaceChild(dom, existing);\n } else {\n parent.appendChild(dom);\n }\n }\n}\n\nfunction updateKeyedElementsMap(\n parent: Element,\n keyedVnodes: Array<{ key: string | number; vnode: VNode }>\n): void {\n try {\n const existing = keyedElements.get(parent);\n const newKeyMap = existing\n ? (existing.clear(), existing)\n : new Map<string | number, Element>();\n\n for (let index = 0; index < keyedVnodes.length; index += 1) {\n const key = keyedVnodes[index].key;\n const child = parent.children[index] as Element | undefined;\n if (child) newKeyMap.set(key, child);\n }\n\n keyedElements.set(parent, newKeyMap);\n } catch {\n // Ignore errors updating key map\n }\n}\n\nexport function performBulkTextReplace(parent: Element, newChildren: VNode[]) {\n const start = now();\n const existing = Array.from(parent.childNodes);\n const finalNodes: Node[] = [];\n let reused = 0;\n let created = 0;\n\n for (let index = 0; index < newChildren.length; index += 1) {\n const result = processChildNode(\n newChildren[index],\n existing[index],\n finalNodes\n );\n if (result === 'reused') reused += 1;\n else if (result === 'created') created += 1;\n }\n\n const tBuild = now() - start;\n const tCommit = commitBulkReplace(parent, finalNodes);\n\n keyedElements.delete(parent);\n\n const stats = {\n n: newChildren.length,\n reused,\n created,\n tBuild,\n tCommit,\n } as const;\n recordBulkTextStats(stats);\n\n return stats;\n}\n\nfunction processChildNode(\n vnode: VNode,\n existingNode: ChildNode | undefined,\n finalNodes: Node[]\n): 'reused' | 'created' | 'skipped' {\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n return processTextVnode(String(vnode), existingNode, finalNodes);\n }\n\n if (typeof vnode === 'object' && vnode !== null && 'type' in vnode) {\n return processElementVnode(vnode, existingNode, finalNodes);\n }\n\n return 'skipped';\n}\n\nfunction processTextVnode(\n text: string,\n existingNode: ChildNode | undefined,\n finalNodes: Node[]\n): 'reused' | 'created' {\n if (existingNode && existingNode.nodeType === 3) {\n (existingNode as Text).data = text;\n finalNodes.push(existingNode);\n return 'reused';\n }\n\n finalNodes.push(document.createTextNode(text));\n return 'created';\n}\n\nfunction processElementVnode(\n vnode: VNode,\n existingNode: ChildNode | undefined,\n finalNodes: Node[]\n): 'reused' | 'created' | 'skipped' {\n const vnodeObj = vnode as unknown as { type?: unknown };\n\n if (typeof vnodeObj.type === 'string') {\n const tag = vnodeObj.type;\n if (\n existingNode &&\n existingNode.nodeType === 1 &&\n tagsEqualIgnoreCase((existingNode as Element).tagName, tag)\n ) {\n updateElementFromVnode(existingNode as Element, vnode);\n finalNodes.push(existingNode);\n return 'reused';\n }\n }\n\n const dom = createDOMNode(vnode);\n if (dom) {\n finalNodes.push(dom);\n return 'created';\n }\n\n return 'skipped';\n}\n\nfunction commitBulkReplace(parent: Element, nodes: Node[]): number {\n const startedAt = Date.now();\n const fragment = document.createDocumentFragment();\n\n for (let index = 0; index < nodes.length; index += 1) {\n fragment.appendChild(nodes[index]);\n }\n\n try {\n for (let node = parent.firstChild; node; ) {\n const next = node.nextSibling;\n teardownNodeSubtree(node);\n node = next;\n }\n } catch {\n // SLOW PATH: cleanup failure\n }\n\n recordDOMReplace('bulk-text-replace');\n parent.replaceChildren(fragment);\n return Date.now() - startedAt;\n}\n\nfunction recordBulkTextStats(stats: {\n n: number;\n reused: number;\n created: number;\n tBuild: number;\n tCommit: number;\n}): void {\n try {\n setDevValue('__LAST_BULK_TEXT_FASTPATH_STATS', stats);\n setDevValue('__LAST_FASTPATH_STATS', stats);\n setDevValue('__LAST_FASTPATH_COMMIT_COUNT', 1);\n incDevCounter('bulkTextFastpathHits');\n } catch {\n // Ignore stats errors\n }\n}\n\nexport function isBulkTextFastPathEligible(\n parent: Element,\n newChildren: VNode[]\n) {\n const env = getRuntimeEnv();\n const threshold = Number(env.ASKR_BULK_TEXT_THRESHOLD) || 1024;\n const requiredFraction = 0.8;\n\n const total = Array.isArray(newChildren) ? newChildren.length : 0;\n\n if (total < threshold) {\n recordBulkDiag({\n phase: 'bulk-unkeyed-eligible',\n reason: 'too-small',\n total,\n threshold,\n });\n return false;\n }\n\n const result = countSimpleChildren(newChildren);\n if (result.componentFound !== undefined) {\n recordBulkDiag({\n phase: 'bulk-unkeyed-eligible',\n reason: 'component-child',\n index: result.componentFound,\n });\n return false;\n }\n\n const fraction = result.simple / total;\n const eligible =\n fraction >= requiredFraction && parent.childNodes.length >= total;\n\n recordBulkDiag({\n phase: 'bulk-unkeyed-eligible',\n total,\n simple: result.simple,\n fraction,\n requiredFraction,\n eligible,\n });\n\n return eligible;\n}\n\nfunction countSimpleChildren(children: VNode[]): {\n simple: number;\n componentFound?: number;\n} {\n let simple = 0;\n\n for (let index = 0; index < children.length; index += 1) {\n const child = children[index];\n\n if (typeof child === 'string' || typeof child === 'number') {\n simple += 1;\n continue;\n }\n\n if (typeof child === 'object' && child !== null && 'type' in child) {\n const dom = child as DOMElement;\n\n if (typeof dom.type === 'function') {\n return { simple, componentFound: index };\n }\n\n if (typeof dom.type === 'string' && isSimpleElement(dom)) {\n simple += 1;\n }\n }\n }\n\n return { simple };\n}\n\nfunction isSimpleElement(dom: DOMElement): boolean {\n const children = dom.props?.children ?? dom.children;\n\n if (children === null || children === undefined) return true;\n\n if (typeof children === 'string' || typeof children === 'number') {\n return true;\n }\n\n if (\n Array.isArray(children) &&\n children.length === 1 &&\n (typeof children[0] === 'string' || typeof children[0] === 'number')\n ) {\n return true;\n }\n\n return false;\n}\n\nfunction recordBulkDiag(data: Record<string, unknown>): void {\n const env = getRuntimeEnv();\n if (env.NODE_ENV !== 'production' || env.ASKR_FASTPATH_DEBUG === '1') {\n try {\n setDevValue('__BULK_DIAG', data);\n } catch {\n // Ignore\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup.js","names":[],"sources":["../../src/renderer/cleanup.ts"],"sourcesContent":["import { cleanupComponent } from '../runtime/component';\nimport type { ComponentInstance } from '../runtime/component';\nimport { logger } from '../dev/logger';\nimport { incDevCounter } from '../runtime/dev-namespace';\nimport {\n clearDelegatedHandlersForElement,\n removeDelegatedListener,\n} from '../runtime/events';\n\ntype InstanceHost = Node & { __ASKR_INSTANCE?: unknown };\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Instance Cleanup Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction cleanupSingleInstance(\n node: InstanceHost,\n errors: unknown[] | null,\n strict: boolean\n): void {\n const inst = node.__ASKR_INSTANCE;\n if (!inst) return;\n\n try {\n cleanupComponent(inst as ComponentInstance);\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] cleanupComponent failed:', err);\n }\n\n try {\n delete node.__ASKR_INSTANCE;\n } catch (e) {\n if (strict) errors!.push(e);\n }\n}\n\nfunction teardownSingleElement(\n element: Element,\n errors: unknown[] | null,\n strict: boolean\n): void {\n try {\n removeElementListeners(element);\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] removeElementListeners failed:', err);\n }\n\n try {\n removeElementReactiveProps(element);\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] removeElementReactiveProps failed:', err);\n }\n\n try {\n cleanupSingleInstance(element as InstanceHost, errors, strict);\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] cleanupSingleInstance failed:', err);\n }\n}\n\n// Walk descendant elements with minimal allocations.\n// HOT PATH: used during subtree teardown (replace/unmount).\nfunction forEachDescendantElement(root: Element, visit: (el: Element) => void) {\n // Prefer TreeWalker when available; it avoids allocating a NodeList.\n try {\n const doc = root.ownerDocument;\n const createTreeWalker = doc?.createTreeWalker;\n if (typeof createTreeWalker === 'function') {\n // NodeFilter.SHOW_ELEMENT === 1\n const walker = createTreeWalker.call(doc, root, 1);\n let n = walker.firstChild();\n while (n) {\n visit(n as Element);\n n = walker.nextNode();\n }\n return;\n }\n } catch {\n // SLOW PATH: TreeWalker unavailable\n }\n\n // Fallback: querySelectorAll\n const descendants = root.querySelectorAll('*');\n for (let i = 0; i < descendants.length; i++) {\n visit(descendants[i]);\n }\n}\n\nfunction forEachElementInSubtree(root: Element, visit: (el: Element) => void) {\n visit(root);\n forEachDescendantElement(root, visit);\n}\n\n// Track listeners so we can remove them on cleanup\nexport interface ListenerMapEntry {\n handler: EventListener;\n original: EventListener;\n options?: boolean | AddEventListenerOptions;\n isDelegated?: boolean;\n updateHandler?: (nextHandler: EventListener) => void;\n}\nexport const elementListeners = new WeakMap<\n Element,\n Map<string, ListenerMapEntry>\n>();\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Public API\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Clean up component instance attached to a DOM node\n * Accepts an optional `opts.strict` flag to surface errors instead of swallowing them.\n */\nexport function cleanupInstanceIfPresent(\n node: Node | null,\n opts?: { strict?: boolean }\n): void {\n if (!node) return;\n\n const strict = opts?.strict ?? false;\n const errors: unknown[] | null = strict ? [] : null;\n\n // Clean up the node itself\n try {\n cleanupSingleInstance(node as InstanceHost, errors, strict);\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] cleanupInstanceIfPresent failed:', err);\n }\n\n if (node instanceof Element) {\n // Clean up any nested instances on descendants\n try {\n forEachDescendantElement(node, (d) => {\n try {\n cleanupSingleInstance(d as InstanceHost, errors, strict);\n } catch (err) {\n if (strict) errors!.push(err);\n else\n logger.warn(\n '[Askr] cleanupInstanceIfPresent descendant cleanup failed:',\n err\n );\n }\n });\n } catch (err) {\n if (strict) errors!.push(err);\n else\n logger.warn(\n '[Askr] cleanupInstanceIfPresent descendant query failed:',\n err\n );\n }\n }\n\n if (errors && errors.length > 0) {\n throw new AggregateError(errors, 'cleanupInstanceIfPresent failed');\n }\n}\n\n// Public helper to clean up any component instances under a node. Used by\n// runtime commit logic to ensure component instances are torn down when their\n// host nodes are removed during an update.\nexport function cleanupInstancesUnder(\n node: Node | null,\n opts?: { strict?: boolean }\n): void {\n cleanupInstanceIfPresent(node, opts);\n}\n\nexport function teardownNodeSubtree(\n node: Node | null,\n opts?: { strict?: boolean }\n): void {\n if (!node) return;\n\n const strict = opts?.strict ?? false;\n const errors: unknown[] | null = strict ? [] : null;\n\n if (!(node instanceof Element)) {\n cleanupSingleInstance(node as InstanceHost, errors, strict);\n if (errors && errors.length > 0) {\n throw new AggregateError(errors, 'teardownNodeSubtree failed');\n }\n return;\n }\n\n try {\n forEachElementInSubtree(node, (element) => {\n teardownSingleElement(element, errors, strict);\n });\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] teardownNodeSubtree failed:', err);\n }\n\n if (errors && errors.length > 0) {\n throw new AggregateError(errors, 'teardownNodeSubtree failed');\n }\n}\n\n// Track reactive props cleanup functions and their function references\nexport interface ReactivePropCleanupEntry {\n cleanup: () => void;\n fnRef: unknown;\n updateFn?: (nextValue: unknown) => void;\n}\n\nexport const REACTIVE_CHILDREN_KEY = '__askr_reactive_children__';\n\nexport const elementReactivePropsCleanup = new WeakMap<\n Element,\n Map<string, ReactivePropCleanupEntry>\n>();\n\nexport function removeElementReactiveProps(element: Element): void {\n const cleanupMap = elementReactivePropsCleanup.get(element);\n if (cleanupMap) {\n for (const entry of cleanupMap.values()) {\n try {\n entry.cleanup();\n } catch (err) {\n logger.warn('[Askr] reactive prop cleanup failed:', err);\n }\n }\n elementReactivePropsCleanup.delete(element);\n }\n}\n\nexport function removeElementListeners(element: Element): void {\n const map = elementListeners.get(element);\n if (map) {\n for (const [eventName, entry] of map) {\n incDevCounter('listenerRemoves');\n if (entry.isDelegated) {\n removeDelegatedListener(element, eventName);\n } else {\n if (entry.options !== undefined)\n element.removeEventListener(eventName, entry.handler, entry.options);\n else element.removeEventListener(eventName, entry.handler);\n }\n }\n elementListeners.delete(element);\n }\n\n clearDelegatedHandlersForElement(element);\n}\n\nexport function removeAllListeners(root: Element | null): void {\n if (!root) return;\n\n forEachElementInSubtree(root, (el) => {\n removeElementListeners(el);\n removeElementReactiveProps(el);\n });\n}\n"],"mappings":";;;;;AAeA,SAAS,sBACP,MACA,QACA,QACM;CACN,MAAM,OAAO,KAAK;AAClB,KAAI,CAAC,KAAM;AAEX,KAAI;AACF,mBAAiB,KAA0B;UACpC,KAAK;AACZ,MAAI,OAAQ,QAAQ,KAAK,IAAI;MACxB,QAAO,KAAK,mCAAmC,IAAI;;AAG1D,KAAI;AACF,SAAO,KAAK;UACL,GAAG;AACV,MAAI,OAAQ,QAAQ,KAAK,EAAE;;;AAI/B,SAAS,sBACP,SACA,QACA,QACM;AACN,KAAI;AACF,yBAAuB,QAAQ;UACxB,KAAK;AACZ,MAAI,OAAQ,QAAQ,KAAK,IAAI;MACxB,QAAO,KAAK,yCAAyC,IAAI;;AAGhE,KAAI;AACF,6BAA2B,QAAQ;UAC5B,KAAK;AACZ,MAAI,OAAQ,QAAQ,KAAK,IAAI;MACxB,QAAO,KAAK,6CAA6C,IAAI;;AAGpE,KAAI;AACF,wBAAsB,SAAyB,QAAQ,OAAO;UACvD,KAAK;AACZ,MAAI,OAAQ,QAAQ,KAAK,IAAI;MACxB,QAAO,KAAK,wCAAwC,IAAI;;;AAMjE,SAAS,yBAAyB,MAAe,OAA8B;AAE7E,KAAI;EACF,MAAM,MAAM,KAAK;EACjB,MAAM,mBAAmB,KAAK;AAC9B,MAAI,OAAO,qBAAqB,YAAY;GAE1C,MAAM,SAAS,iBAAiB,KAAK,KAAK,MAAM,EAAE;GAClD,IAAI,IAAI,OAAO,YAAY;AAC3B,UAAO,GAAG;AACR,UAAM,EAAa;AACnB,QAAI,OAAO,UAAU;;AAEvB;;SAEI;CAKR,MAAM,cAAc,KAAK,iBAAiB,IAAI;AAC9C,MAAK,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,IACtC,OAAM,YAAY,GAAG;;AAIzB,SAAS,wBAAwB,MAAe,OAA8B;AAC5E,OAAM,KAAK;AACX,0BAAyB,MAAM,MAAM;;AAWvC,MAAa,mCAAmB,IAAI,SAGjC;;;;;AAUH,SAAgB,yBACd,MACA,MACM;AACN,KAAI,CAAC,KAAM;CAEX,MAAM,SAAS,MAAM,UAAU;CAC/B,MAAM,SAA2B,SAAS,EAAE,GAAG;AAG/C,KAAI;AACF,wBAAsB,MAAsB,QAAQ,OAAO;UACpD,KAAK;AACZ,MAAI,OAAQ,QAAQ,KAAK,IAAI;MACxB,QAAO,KAAK,2CAA2C,IAAI;;AAGlE,KAAI,gBAAgB,QAElB,KAAI;AACF,2BAAyB,OAAO,MAAM;AACpC,OAAI;AACF,0BAAsB,GAAmB,QAAQ,OAAO;YACjD,KAAK;AACZ,QAAI,OAAQ,QAAQ,KAAK,IAAI;QAE3B,QAAO,KACL,8DACA,IACD;;IAEL;UACK,KAAK;AACZ,MAAI,OAAQ,QAAQ,KAAK,IAAI;MAE3B,QAAO,KACL,4DACA,IACD;;AAIP,KAAI,UAAU,OAAO,SAAS,EAC5B,OAAM,IAAI,eAAe,QAAQ,kCAAkC;;AAOvE,SAAgB,sBACd,MACA,MACM;AACN,0BAAyB,MAAM,KAAK;;AAGtC,SAAgB,oBACd,MACA,MACM;AACN,KAAI,CAAC,KAAM;CAEX,MAAM,SAAS,MAAM,UAAU;CAC/B,MAAM,SAA2B,SAAS,EAAE,GAAG;AAE/C,KAAI,EAAE,gBAAgB,UAAU;AAC9B,wBAAsB,MAAsB,QAAQ,OAAO;AAC3D,MAAI,UAAU,OAAO,SAAS,EAC5B,OAAM,IAAI,eAAe,QAAQ,6BAA6B;AAEhE;;AAGF,KAAI;AACF,0BAAwB,OAAO,YAAY;AACzC,yBAAsB,SAAS,QAAQ,OAAO;IAC9C;UACK,KAAK;AACZ,MAAI,OAAQ,QAAQ,KAAK,IAAI;MACxB,QAAO,KAAK,sCAAsC,IAAI;;AAG7D,KAAI,UAAU,OAAO,SAAS,EAC5B,OAAM,IAAI,eAAe,QAAQ,6BAA6B;;AAWlE,MAAa,wBAAwB;AAErC,MAAa,8CAA8B,IAAI,SAG5C;AAEH,SAAgB,2BAA2B,SAAwB;CACjE,MAAM,aAAa,4BAA4B,IAAI,QAAQ;AAC3D,KAAI,YAAY;AACd,OAAK,MAAM,SAAS,WAAW,QAAQ,CACrC,KAAI;AACF,SAAM,SAAS;WACR,KAAK;AACZ,UAAO,KAAK,wCAAwC,IAAI;;AAG5D,8BAA4B,OAAO,QAAQ;;;AAI/C,SAAgB,uBAAuB,SAAwB;CAC7D,MAAM,MAAM,iBAAiB,IAAI,QAAQ;AACzC,KAAI,KAAK;AACP,OAAK,MAAM,CAAC,WAAW,UAAU,KAAK;AACpC,iBAAc,kBAAkB;AAChC,OAAI,MAAM,YACR,yBAAwB,SAAS,UAAU;YAEvC,MAAM,YAAY,OACpB,SAAQ,oBAAoB,WAAW,MAAM,SAAS,MAAM,QAAQ;OACjE,SAAQ,oBAAoB,WAAW,MAAM,QAAQ;;AAG9D,mBAAiB,OAAO,QAAQ;;AAGlC,kCAAiC,QAAQ;;AAG3C,SAAgB,mBAAmB,MAA4B;AAC7D,KAAI,CAAC,KAAM;AAEX,yBAAwB,OAAO,OAAO;AACpC,yBAAuB,GAAG;AAC1B,6BAA2B,GAAG;GAC9B"}
|
|
1
|
+
{"version":3,"file":"cleanup.js","names":[],"sources":["../../src/renderer/cleanup.ts"],"sourcesContent":["import { cleanupComponent } from '../runtime/component';\nimport type { ComponentInstance } from '../runtime/component';\nimport { logger } from '../dev/logger';\nimport { incDevCounter } from '../runtime/dev-namespace';\nimport {\n clearDelegatedHandlersForElement,\n removeDelegatedListener,\n} from '../runtime/events';\n\ntype InstanceHost = Node & { __ASKR_INSTANCE?: unknown };\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Instance Cleanup Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction cleanupSingleInstance(\n node: InstanceHost,\n errors: unknown[] | null,\n strict: boolean\n): void {\n const inst = node.__ASKR_INSTANCE;\n if (!inst) return;\n\n try {\n cleanupComponent(inst as ComponentInstance);\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] cleanupComponent failed:', err);\n }\n\n try {\n delete node.__ASKR_INSTANCE;\n } catch (e) {\n if (strict) errors!.push(e);\n }\n}\n\nfunction teardownSingleElement(\n element: Element,\n errors: unknown[] | null,\n strict: boolean\n): void {\n try {\n removeElementListeners(element);\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] removeElementListeners failed:', err);\n }\n\n try {\n removeElementReactiveProps(element);\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] removeElementReactiveProps failed:', err);\n }\n\n try {\n cleanupSingleInstance(element as InstanceHost, errors, strict);\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] cleanupSingleInstance failed:', err);\n }\n}\n\n// Walk descendant elements with minimal allocations.\n// HOT PATH: used during subtree teardown (replace/unmount).\nfunction forEachDescendantElement(root: Element, visit: (el: Element) => void) {\n // Prefer TreeWalker when available; it avoids allocating a NodeList.\n try {\n const doc = root.ownerDocument;\n const createTreeWalker = doc?.createTreeWalker;\n if (typeof createTreeWalker === 'function') {\n // NodeFilter.SHOW_ELEMENT === 1\n const walker = createTreeWalker.call(doc, root, 1);\n let n = walker.firstChild();\n while (n) {\n visit(n as Element);\n n = walker.nextNode();\n }\n return;\n }\n } catch {\n // SLOW PATH: TreeWalker unavailable\n }\n\n // Fallback: querySelectorAll\n const descendants = root.querySelectorAll('*');\n for (let i = 0; i < descendants.length; i++) {\n visit(descendants[i]);\n }\n}\n\nfunction forEachElementInSubtree(root: Element, visit: (el: Element) => void) {\n visit(root);\n forEachDescendantElement(root, visit);\n}\n\n// Track listeners so we can remove them on cleanup\nexport interface ListenerMapEntry {\n handler: EventListener;\n original: EventListener;\n options?: boolean | AddEventListenerOptions;\n isDelegated?: boolean;\n updateHandler?: (nextHandler: EventListener) => void;\n}\nexport const elementListeners = new WeakMap<\n Element,\n Map<string, ListenerMapEntry>\n>();\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Public API\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Clean up component instance attached to a DOM node\n * Accepts an optional `opts.strict` flag to surface errors instead of swallowing them.\n */\nexport function cleanupInstanceIfPresent(\n node: Node | null,\n opts?: { strict?: boolean }\n): void {\n if (!node) return;\n\n const strict = opts?.strict ?? false;\n const errors: unknown[] | null = strict ? [] : null;\n\n // Clean up the node itself\n try {\n cleanupSingleInstance(node as InstanceHost, errors, strict);\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] cleanupInstanceIfPresent failed:', err);\n }\n\n if (node instanceof Element) {\n // Clean up any nested instances on descendants\n try {\n forEachDescendantElement(node, (d) => {\n try {\n cleanupSingleInstance(d as InstanceHost, errors, strict);\n } catch (err) {\n if (strict) errors!.push(err);\n else\n logger.warn(\n '[Askr] cleanupInstanceIfPresent descendant cleanup failed:',\n err\n );\n }\n });\n } catch (err) {\n if (strict) errors!.push(err);\n else\n logger.warn(\n '[Askr] cleanupInstanceIfPresent descendant query failed:',\n err\n );\n }\n }\n\n if (errors && errors.length > 0) {\n throw new AggregateError(errors, 'cleanupInstanceIfPresent failed');\n }\n}\n\n// Public helper to clean up any component instances under a node. Used by\n// runtime commit logic to ensure component instances are torn down when their\n// host nodes are removed during an update.\nexport function cleanupInstancesUnder(\n node: Node | null,\n opts?: { strict?: boolean }\n): void {\n cleanupInstanceIfPresent(node, opts);\n}\n\nexport function teardownNodeSubtree(\n node: Node | null,\n opts?: { strict?: boolean }\n): void {\n if (!node) return;\n\n const strict = opts?.strict ?? false;\n const errors: unknown[] | null = strict ? [] : null;\n\n if (!(node instanceof Element)) {\n cleanupSingleInstance(node as InstanceHost, errors, strict);\n if (errors && errors.length > 0) {\n throw new AggregateError(errors, 'teardownNodeSubtree failed');\n }\n return;\n }\n\n try {\n forEachElementInSubtree(node, (element) => {\n teardownSingleElement(element, errors, strict);\n });\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] teardownNodeSubtree failed:', err);\n }\n\n if (errors && errors.length > 0) {\n throw new AggregateError(errors, 'teardownNodeSubtree failed');\n }\n}\n\n// Track reactive props cleanup functions and their function references\nexport interface ReactivePropCleanupEntry {\n cleanup: () => void;\n fnRef: unknown;\n updateFn?: (nextValue: unknown) => void;\n}\n\nexport const REACTIVE_CHILDREN_KEY = '__askr_reactive_children__';\n\nexport const elementReactivePropsCleanup = new WeakMap<\n Element,\n Map<string, ReactivePropCleanupEntry>\n>();\n\nexport function removeElementReactiveProps(element: Element): void {\n const cleanupMap = elementReactivePropsCleanup.get(element);\n if (cleanupMap) {\n for (const entry of cleanupMap.values()) {\n try {\n entry.cleanup();\n } catch (err) {\n logger.warn('[Askr] reactive prop cleanup failed:', err);\n }\n }\n elementReactivePropsCleanup.delete(element);\n }\n}\n\nexport function removeElementListeners(element: Element): void {\n const map = elementListeners.get(element);\n if (map) {\n for (const [eventName, entry] of map) {\n incDevCounter('listenerRemoves');\n if (entry.isDelegated) {\n removeDelegatedListener(element, eventName);\n } else {\n if (entry.options !== undefined)\n element.removeEventListener(eventName, entry.handler, entry.options);\n else element.removeEventListener(eventName, entry.handler);\n }\n }\n elementListeners.delete(element);\n }\n\n clearDelegatedHandlersForElement(element);\n}\n\nexport function removeAllListeners(root: Element | null): void {\n if (!root) return;\n\n forEachElementInSubtree(root, (el) => {\n removeElementListeners(el);\n removeElementReactiveProps(el);\n });\n}\n"],"mappings":";;;;;AAeA,SAAS,sBACP,MACA,QACA,QACM;CACN,MAAM,OAAO,KAAK;CAClB,IAAI,CAAC,MAAM;CAEX,IAAI;EACF,iBAAiB,KAA0B;UACpC,KAAK;EACZ,IAAI,QAAQ,OAAQ,KAAK,IAAI;OACxB,OAAO,KAAK,mCAAmC,IAAI;;CAG1D,IAAI;EACF,OAAO,KAAK;UACL,GAAG;EACV,IAAI,QAAQ,OAAQ,KAAK,EAAE;;;AAI/B,SAAS,sBACP,SACA,QACA,QACM;CACN,IAAI;EACF,uBAAuB,QAAQ;UACxB,KAAK;EACZ,IAAI,QAAQ,OAAQ,KAAK,IAAI;OACxB,OAAO,KAAK,yCAAyC,IAAI;;CAGhE,IAAI;EACF,2BAA2B,QAAQ;UAC5B,KAAK;EACZ,IAAI,QAAQ,OAAQ,KAAK,IAAI;OACxB,OAAO,KAAK,6CAA6C,IAAI;;CAGpE,IAAI;EACF,sBAAsB,SAAyB,QAAQ,OAAO;UACvD,KAAK;EACZ,IAAI,QAAQ,OAAQ,KAAK,IAAI;OACxB,OAAO,KAAK,wCAAwC,IAAI;;;AAMjE,SAAS,yBAAyB,MAAe,OAA8B;CAE7E,IAAI;EACF,MAAM,MAAM,KAAK;EACjB,MAAM,mBAAmB,KAAK;EAC9B,IAAI,OAAO,qBAAqB,YAAY;GAE1C,MAAM,SAAS,iBAAiB,KAAK,KAAK,MAAM,EAAE;GAClD,IAAI,IAAI,OAAO,YAAY;GAC3B,OAAO,GAAG;IACR,MAAM,EAAa;IACnB,IAAI,OAAO,UAAU;;GAEvB;;SAEI;CAKR,MAAM,cAAc,KAAK,iBAAiB,IAAI;CAC9C,KAAK,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KACtC,MAAM,YAAY,GAAG;;AAIzB,SAAS,wBAAwB,MAAe,OAA8B;CAC5E,MAAM,KAAK;CACX,yBAAyB,MAAM,MAAM;;AAWvC,MAAa,mCAAmB,IAAI,SAGjC;;;;;AAUH,SAAgB,yBACd,MACA,MACM;CACN,IAAI,CAAC,MAAM;CAEX,MAAM,SAAS,MAAM,UAAU;CAC/B,MAAM,SAA2B,SAAS,EAAE,GAAG;CAG/C,IAAI;EACF,sBAAsB,MAAsB,QAAQ,OAAO;UACpD,KAAK;EACZ,IAAI,QAAQ,OAAQ,KAAK,IAAI;OACxB,OAAO,KAAK,2CAA2C,IAAI;;CAGlE,IAAI,gBAAgB,SAElB,IAAI;EACF,yBAAyB,OAAO,MAAM;GACpC,IAAI;IACF,sBAAsB,GAAmB,QAAQ,OAAO;YACjD,KAAK;IACZ,IAAI,QAAQ,OAAQ,KAAK,IAAI;SAE3B,OAAO,KACL,8DACA,IACD;;IAEL;UACK,KAAK;EACZ,IAAI,QAAQ,OAAQ,KAAK,IAAI;OAE3B,OAAO,KACL,4DACA,IACD;;CAIP,IAAI,UAAU,OAAO,SAAS,GAC5B,MAAM,IAAI,eAAe,QAAQ,kCAAkC;;AAOvE,SAAgB,sBACd,MACA,MACM;CACN,yBAAyB,MAAM,KAAK;;AAGtC,SAAgB,oBACd,MACA,MACM;CACN,IAAI,CAAC,MAAM;CAEX,MAAM,SAAS,MAAM,UAAU;CAC/B,MAAM,SAA2B,SAAS,EAAE,GAAG;CAE/C,IAAI,EAAE,gBAAgB,UAAU;EAC9B,sBAAsB,MAAsB,QAAQ,OAAO;EAC3D,IAAI,UAAU,OAAO,SAAS,GAC5B,MAAM,IAAI,eAAe,QAAQ,6BAA6B;EAEhE;;CAGF,IAAI;EACF,wBAAwB,OAAO,YAAY;GACzC,sBAAsB,SAAS,QAAQ,OAAO;IAC9C;UACK,KAAK;EACZ,IAAI,QAAQ,OAAQ,KAAK,IAAI;OACxB,OAAO,KAAK,sCAAsC,IAAI;;CAG7D,IAAI,UAAU,OAAO,SAAS,GAC5B,MAAM,IAAI,eAAe,QAAQ,6BAA6B;;AAWlE,MAAa,wBAAwB;AAErC,MAAa,8CAA8B,IAAI,SAG5C;AAEH,SAAgB,2BAA2B,SAAwB;CACjE,MAAM,aAAa,4BAA4B,IAAI,QAAQ;CAC3D,IAAI,YAAY;EACd,KAAK,MAAM,SAAS,WAAW,QAAQ,EACrC,IAAI;GACF,MAAM,SAAS;WACR,KAAK;GACZ,OAAO,KAAK,wCAAwC,IAAI;;EAG5D,4BAA4B,OAAO,QAAQ;;;AAI/C,SAAgB,uBAAuB,SAAwB;CAC7D,MAAM,MAAM,iBAAiB,IAAI,QAAQ;CACzC,IAAI,KAAK;EACP,KAAK,MAAM,CAAC,WAAW,UAAU,KAAK;GACpC,cAAc,kBAAkB;GAChC,IAAI,MAAM,aACR,wBAAwB,SAAS,UAAU;QAE3C,IAAI,MAAM,YAAY,QACpB,QAAQ,oBAAoB,WAAW,MAAM,SAAS,MAAM,QAAQ;QACjE,QAAQ,oBAAoB,WAAW,MAAM,QAAQ;;EAG9D,iBAAiB,OAAO,QAAQ;;CAGlC,iCAAiC,QAAQ;;AAG3C,SAAgB,mBAAmB,MAA4B;CAC7D,IAAI,CAAC,MAAM;CAEX,wBAAwB,OAAO,OAAO;EACpC,uBAAuB,GAAG;EAC1B,2BAA2B,GAAG;GAC9B"}
|
package/dist/renderer/dom.js
CHANGED
|
@@ -1254,10 +1254,48 @@ function normalizeComponentChildren(result) {
|
|
|
1254
1254
|
}
|
|
1255
1255
|
return [result];
|
|
1256
1256
|
}
|
|
1257
|
-
function syncComponentElement(currentDom, node, type, props, parentNamespace) {
|
|
1257
|
+
function syncComponentElement(currentDom, node, type, props, parentNamespace, forceChildrenUpdate = false) {
|
|
1258
1258
|
const existingHost = currentDom instanceof Element ? currentDom : null;
|
|
1259
1259
|
const existingInstance = existingHost ? findHostInstanceByType(existingHost, type) : null;
|
|
1260
|
-
if (!existingHost
|
|
1260
|
+
if (!existingHost) return null;
|
|
1261
|
+
if (!existingInstance || existingInstance.fn !== type) {
|
|
1262
|
+
const snapshot = getVNodeContextFrame(node) || getCurrentContextFrame() || null;
|
|
1263
|
+
const hydrationInstance = createComponentInstance(nextComponentInstanceId(), type, props || {}, existingHost);
|
|
1264
|
+
setVNodeComponentInstance(node, hydrationInstance);
|
|
1265
|
+
if (snapshot) hydrationInstance.ownerFrame = snapshot;
|
|
1266
|
+
const result = withContext(snapshot, () => renderComponentInline(hydrationInstance));
|
|
1267
|
+
if (result instanceof Promise) throw new Error("Async components are not supported. Components must return synchronously.");
|
|
1268
|
+
const scopedResult = markVNodeTreeWithContextFrame(result, snapshot ?? null);
|
|
1269
|
+
if (scopedResult && typeof scopedResult === "object" && "type" in scopedResult && typeof scopedResult.type === "string" && tagNamesEqualIgnoreCase(existingHost.tagName, scopedResult.type)) {
|
|
1270
|
+
withContext(snapshot, () => {
|
|
1271
|
+
updateElementFromVnode(existingHost, inheritComponentKey(scopedResult, node), true, forceChildrenUpdate || hydrationInstance.mounted === false);
|
|
1272
|
+
materializeKey(existingHost, node, props);
|
|
1273
|
+
});
|
|
1274
|
+
mountInstanceInline(hydrationInstance, existingHost);
|
|
1275
|
+
itemInstanceHydrationComplete(existingHost);
|
|
1276
|
+
warnUnusedStateReads(hydrationInstance);
|
|
1277
|
+
return existingHost;
|
|
1278
|
+
}
|
|
1279
|
+
const resolvedResult = resolveNestedComponentResult(scopedResult, snapshot ?? null);
|
|
1280
|
+
if (_isDOMElement(resolvedResult) && typeof resolvedResult.type === "string" && tagNamesEqualIgnoreCase(existingHost.tagName, resolvedResult.type)) {
|
|
1281
|
+
withContext(snapshot, () => {
|
|
1282
|
+
updateElementFromVnode(existingHost, inheritComponentKey(resolvedResult, node), true, forceChildrenUpdate || hydrationInstance.mounted === false);
|
|
1283
|
+
materializeKey(existingHost, node, props);
|
|
1284
|
+
});
|
|
1285
|
+
mountInstanceInline(hydrationInstance, existingHost);
|
|
1286
|
+
itemInstanceHydrationComplete(existingHost);
|
|
1287
|
+
warnUnusedStateReads(hydrationInstance);
|
|
1288
|
+
return existingHost;
|
|
1289
|
+
}
|
|
1290
|
+
const nextDom = materializeComponentResultNode(hydrationInstance, scopedResult, parentNamespace);
|
|
1291
|
+
if (nextDom instanceof Element) materializeKey(nextDom, node, props);
|
|
1292
|
+
if (nextDom !== existingHost && existingHost.parentNode) {
|
|
1293
|
+
existingHost.parentNode.replaceChild(nextDom, existingHost);
|
|
1294
|
+
cleanupDetachedComponentHost(existingHost, hydrationInstance);
|
|
1295
|
+
}
|
|
1296
|
+
warnUnusedStateReads(hydrationInstance);
|
|
1297
|
+
return nextDom;
|
|
1298
|
+
}
|
|
1261
1299
|
const snapshot = getVNodeContextFrame(node) || getCurrentContextFrame() || existingInstance.ownerFrame || null;
|
|
1262
1300
|
existingInstance.props = props || {};
|
|
1263
1301
|
if (snapshot) existingInstance.ownerFrame = snapshot;
|
|
@@ -1271,7 +1309,7 @@ function syncComponentElement(currentDom, node, type, props, parentNamespace) {
|
|
|
1271
1309
|
}
|
|
1272
1310
|
if (scopedResult && typeof scopedResult === "object" && "type" in scopedResult && typeof scopedResult.type === "string" && tagNamesEqualIgnoreCase(existingHost.tagName, scopedResult.type)) {
|
|
1273
1311
|
withContext(snapshot, () => {
|
|
1274
|
-
updateElementFromVnode(existingHost, inheritComponentKey(scopedResult, node), true);
|
|
1312
|
+
updateElementFromVnode(existingHost, inheritComponentKey(scopedResult, node), true, forceChildrenUpdate || existingInstance.mounted === false);
|
|
1275
1313
|
materializeKey(existingHost, node, props);
|
|
1276
1314
|
});
|
|
1277
1315
|
warnUnusedStateReads(existingInstance);
|
|
@@ -1280,13 +1318,14 @@ function syncComponentElement(currentDom, node, type, props, parentNamespace) {
|
|
|
1280
1318
|
const resolvedResult = resolveNestedComponentResult(scopedResult, snapshot ?? null);
|
|
1281
1319
|
if (_isDOMElement(resolvedResult) && typeof resolvedResult.type === "string" && tagNamesEqualIgnoreCase(existingHost.tagName, resolvedResult.type)) {
|
|
1282
1320
|
withContext(snapshot, () => {
|
|
1283
|
-
updateElementFromVnode(existingHost, inheritComponentKey(resolvedResult, node), true);
|
|
1321
|
+
updateElementFromVnode(existingHost, inheritComponentKey(resolvedResult, node), true, forceChildrenUpdate || existingInstance.mounted === false);
|
|
1284
1322
|
materializeKey(existingHost, node, props);
|
|
1285
1323
|
});
|
|
1286
1324
|
warnUnusedStateReads(existingInstance);
|
|
1287
1325
|
return existingHost;
|
|
1288
1326
|
}
|
|
1289
1327
|
const nextDom = materializeComponentResultNode(existingInstance, scopedResult, parentNamespace);
|
|
1328
|
+
if (nextDom instanceof Element) materializeKey(nextDom, node, props);
|
|
1290
1329
|
if (nextDom !== existingHost && existingHost.parentNode) {
|
|
1291
1330
|
existingHost.parentNode.replaceChild(nextDom, existingHost);
|
|
1292
1331
|
cleanupDetachedComponentHost(existingHost, existingInstance);
|
|
@@ -1294,6 +1333,13 @@ function syncComponentElement(currentDom, node, type, props, parentNamespace) {
|
|
|
1294
1333
|
warnUnusedStateReads(existingInstance);
|
|
1295
1334
|
return nextDom;
|
|
1296
1335
|
}
|
|
1336
|
+
function itemInstanceHydrationComplete(host) {
|
|
1337
|
+
const instance = host.__ASKR_INSTANCE;
|
|
1338
|
+
if (instance) {
|
|
1339
|
+
const scope = instance.scope;
|
|
1340
|
+
if (scope) scope.hydrationPending = false;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1297
1343
|
function createComponentElement(node, type, props, parentNamespace) {
|
|
1298
1344
|
const snapshot = getVNodeContextFrame(node) || getCurrentContextFrame();
|
|
1299
1345
|
const componentFn = type;
|
|
@@ -1511,7 +1557,6 @@ function createErrorBoundaryElement(node, props, parentNamespace) {
|
|
|
1511
1557
|
function syncForItemDom(parent, scope, vnode) {
|
|
1512
1558
|
let dom = scope.dom ?? null;
|
|
1513
1559
|
const parentNamespace = parent.namespaceURI === SVG_NAMESPACE ? SVG_NAMESPACE : void 0;
|
|
1514
|
-
if (dom && !scope.needsDomUpdate) return dom;
|
|
1515
1560
|
if (_isDOMElement(vnode) && typeof vnode.type === "function") {
|
|
1516
1561
|
const syncedComponentDom = syncComponentElement(dom, vnode, vnode.type, vnode.props ?? {}, parentNamespace);
|
|
1517
1562
|
if (syncedComponentDom) {
|
|
@@ -1593,12 +1638,14 @@ function resolveStableIntrinsicPatchVNode(dom, vnode) {
|
|
|
1593
1638
|
return null;
|
|
1594
1639
|
}
|
|
1595
1640
|
function tryPatchStableForDirtyItem(scope) {
|
|
1641
|
+
incDevCounter("stableForPatchAttempt");
|
|
1596
1642
|
if (!(scope.dom instanceof Element) || scope.vnode === void 0) return false;
|
|
1597
1643
|
const nextIntrinsic = resolveStableIntrinsicPatchVNode(scope.dom, scope.vnode);
|
|
1598
1644
|
if (!nextIntrinsic) return false;
|
|
1599
1645
|
const didPatch = patchStableIntrinsicElement(scope.dom, nextIntrinsic);
|
|
1600
1646
|
const existingInstance = scope.dom.__ASKR_INSTANCE;
|
|
1601
1647
|
if (didPatch && existingInstance) warnUnusedStateReads(existingInstance);
|
|
1648
|
+
if (didPatch) incDevCounter("stableForPatchHit");
|
|
1602
1649
|
return didPatch;
|
|
1603
1650
|
}
|
|
1604
1651
|
function removeForBoundaryNodes(parent, removedNodes) {
|
|
@@ -1704,8 +1751,9 @@ function commitForBoundaryChildren(parent, controlState, childrenVNodes) {
|
|
|
1704
1751
|
const itemInstance = forState.items.get(itemKey);
|
|
1705
1752
|
if (!itemInstance || itemInstance.scope.dom) continue;
|
|
1706
1753
|
const existingDom = domKeyMap.get(itemKey);
|
|
1707
|
-
if (!existingDom
|
|
1754
|
+
if (!existingDom) continue;
|
|
1708
1755
|
itemInstance.scope.dom = existingDom;
|
|
1756
|
+
itemInstance.scope.needsDomUpdate = true;
|
|
1709
1757
|
}
|
|
1710
1758
|
};
|
|
1711
1759
|
if (forState.orderedKeys.length === 0) {
|
|
@@ -1727,10 +1775,7 @@ function commitForBoundaryChildren(parent, controlState, childrenVNodes) {
|
|
|
1727
1775
|
const pendingDirtyIndices = forState.pendingDirtyIndices;
|
|
1728
1776
|
if (pendingDirtyIndices && pendingDirtyIndices.length > 0) return pendingDirtyIndices;
|
|
1729
1777
|
const dirtyIndices = [];
|
|
1730
|
-
for (let index = 0; index < forState.orderedKeys.length; index += 1)
|
|
1731
|
-
const itemKey = forState.orderedKeys[index];
|
|
1732
|
-
if (forState.items.get(itemKey)?.scope.needsDomUpdate) dirtyIndices.push(index);
|
|
1733
|
-
}
|
|
1778
|
+
for (let index = 0; index < forState.orderedKeys.length; index += 1) if (forState.orderedItems[index]?.scope.needsDomUpdate) dirtyIndices.push(index);
|
|
1734
1779
|
return dirtyIndices;
|
|
1735
1780
|
};
|
|
1736
1781
|
let dirtyIndicesCache = null;
|
|
@@ -1739,12 +1784,15 @@ function commitForBoundaryChildren(parent, controlState, childrenVNodes) {
|
|
|
1739
1784
|
dirtyIndicesCache = getDirtyForIndices();
|
|
1740
1785
|
return dirtyIndicesCache;
|
|
1741
1786
|
};
|
|
1787
|
+
let boundaryChildrenExact = false;
|
|
1742
1788
|
const commitDirtyNoReorder = (dirtyIndices) => {
|
|
1743
|
-
if (dirtyIndices.length === 0)
|
|
1789
|
+
if (dirtyIndices.length === 0) {
|
|
1790
|
+
boundaryChildrenExact = true;
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1744
1793
|
for (let dirtyIndex = 0; dirtyIndex < dirtyIndices.length; dirtyIndex++) {
|
|
1745
1794
|
const i = dirtyIndices[dirtyIndex];
|
|
1746
|
-
const
|
|
1747
|
-
const itemInstance = forState.items.get(itemKey);
|
|
1795
|
+
const itemInstance = forState.orderedItems[i];
|
|
1748
1796
|
if (!itemInstance) continue;
|
|
1749
1797
|
if (tryPatchStableForDirtyItem(itemInstance.scope)) continue;
|
|
1750
1798
|
const dom = syncForItemDom(parent, itemInstance.scope, childrenVNodes[i]);
|
|
@@ -1755,11 +1803,11 @@ function commitForBoundaryChildren(parent, controlState, childrenVNodes) {
|
|
|
1755
1803
|
parent.insertBefore(dom, anchor);
|
|
1756
1804
|
}
|
|
1757
1805
|
}
|
|
1806
|
+
boundaryChildrenExact = true;
|
|
1758
1807
|
};
|
|
1759
1808
|
const commitPositional = () => {
|
|
1760
1809
|
for (let i = 0; i < forState.orderedKeys.length; i++) {
|
|
1761
|
-
const
|
|
1762
|
-
const itemInstance = forState.items.get(itemKey);
|
|
1810
|
+
const itemInstance = forState.orderedItems[i];
|
|
1763
1811
|
if (!itemInstance) continue;
|
|
1764
1812
|
const dom = syncForItemDom(parent, itemInstance.scope, childrenVNodes[i]);
|
|
1765
1813
|
if (!dom) continue;
|
|
@@ -1769,14 +1817,34 @@ function commitForBoundaryChildren(parent, controlState, childrenVNodes) {
|
|
|
1769
1817
|
parent.insertBefore(dom, anchor);
|
|
1770
1818
|
}
|
|
1771
1819
|
}
|
|
1820
|
+
boundaryChildrenExact = true;
|
|
1772
1821
|
};
|
|
1773
1822
|
const commitAppend = () => {
|
|
1823
|
+
if (!forState._hasResolvedItemDom && forState.lastRemovedNodes.length === 0 && parent.childNodes.length === forState.orderedKeys.length) {
|
|
1824
|
+
let exactOrder = true;
|
|
1825
|
+
let currentNode = parent.firstChild;
|
|
1826
|
+
for (let i = 0; i < forState.orderedKeys.length; i++) {
|
|
1827
|
+
const itemInstance = forState.orderedItems[i];
|
|
1828
|
+
if (!itemInstance) {
|
|
1829
|
+
exactOrder = false;
|
|
1830
|
+
currentNode = currentNode?.nextSibling ?? null;
|
|
1831
|
+
continue;
|
|
1832
|
+
}
|
|
1833
|
+
if (currentNode !== itemInstance.scope.dom) exactOrder = false;
|
|
1834
|
+
const dom = syncForItemDom(parent, itemInstance.scope, childrenVNodes[i]);
|
|
1835
|
+
if (!dom || dom.parentNode !== parent || dom !== currentNode) exactOrder = false;
|
|
1836
|
+
currentNode = currentNode?.nextSibling ?? null;
|
|
1837
|
+
}
|
|
1838
|
+
if (exactOrder) {
|
|
1839
|
+
boundaryChildrenExact = true;
|
|
1840
|
+
return;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1774
1843
|
withBenchMetricScope("coldCreate", () => {
|
|
1775
1844
|
const fragment = parent.ownerDocument.createDocumentFragment();
|
|
1776
1845
|
let hasPendingAppend = false;
|
|
1777
1846
|
for (let i = 0; i < forState.orderedKeys.length; i++) {
|
|
1778
|
-
const
|
|
1779
|
-
const itemInstance = forState.items.get(itemKey);
|
|
1847
|
+
const itemInstance = forState.orderedItems[i];
|
|
1780
1848
|
if (!itemInstance) continue;
|
|
1781
1849
|
if (itemInstance.scope.dom?.parentNode === parent && !itemInstance.scope.needsDomUpdate) continue;
|
|
1782
1850
|
const dom = syncForItemDom(parent, itemInstance.scope, childrenVNodes[i]);
|
|
@@ -1789,6 +1857,7 @@ function commitForBoundaryChildren(parent, controlState, childrenVNodes) {
|
|
|
1789
1857
|
}
|
|
1790
1858
|
if (hasPendingAppend) parent.appendChild(fragment);
|
|
1791
1859
|
});
|
|
1860
|
+
boundaryChildrenExact = true;
|
|
1792
1861
|
};
|
|
1793
1862
|
const commitSwap = () => {
|
|
1794
1863
|
const swapIndices = forState.pendingSwapIndices;
|
|
@@ -1814,30 +1883,34 @@ function commitForBoundaryChildren(parent, controlState, childrenVNodes) {
|
|
|
1814
1883
|
commitReorder();
|
|
1815
1884
|
return;
|
|
1816
1885
|
}
|
|
1817
|
-
if ((firstDom.compareDocumentPosition(secondDom) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0)
|
|
1886
|
+
if ((firstDom.compareDocumentPosition(secondDom) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0) {
|
|
1887
|
+
boundaryChildrenExact = true;
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1818
1890
|
const firstNextSibling = firstDom.nextSibling;
|
|
1819
1891
|
recordBenchEvent("domMove");
|
|
1820
1892
|
parent.insertBefore(firstDom, secondDom);
|
|
1821
1893
|
recordBenchEvent("domMove");
|
|
1822
1894
|
parent.insertBefore(secondDom, firstNextSibling);
|
|
1895
|
+
boundaryChildrenExact = true;
|
|
1823
1896
|
};
|
|
1824
1897
|
const commitReorder = () => {
|
|
1825
|
-
const
|
|
1826
|
-
const count =
|
|
1898
|
+
const items = forState.orderedItems;
|
|
1899
|
+
const count = items.length;
|
|
1827
1900
|
if (forState.pendingMoveOnly && forState.lastRemovedNodes.length === 0) {
|
|
1828
|
-
const
|
|
1901
|
+
const nodes = [];
|
|
1829
1902
|
for (let i = 0; i < count; i++) {
|
|
1830
|
-
const
|
|
1831
|
-
const dom = forState.items.get(itemKey)?.scope.dom;
|
|
1903
|
+
const dom = items[i]?.scope.dom;
|
|
1832
1904
|
if (!dom) return;
|
|
1833
1905
|
recordBenchEvent(dom.parentNode === parent ? "domMove" : "domInsert");
|
|
1834
|
-
|
|
1906
|
+
nodes.push(dom);
|
|
1835
1907
|
}
|
|
1836
|
-
parent.replaceChildren(
|
|
1908
|
+
parent.replaceChildren(...nodes);
|
|
1909
|
+
boundaryChildrenExact = true;
|
|
1837
1910
|
return;
|
|
1838
1911
|
}
|
|
1839
1912
|
let hasExistingChild = false;
|
|
1840
|
-
for (let i = 0; i < count; i++) if (
|
|
1913
|
+
for (let i = 0; i < count; i++) if (items[i]?.scope.dom?.parentNode === parent) {
|
|
1841
1914
|
hasExistingChild = true;
|
|
1842
1915
|
break;
|
|
1843
1916
|
}
|
|
@@ -1845,8 +1918,7 @@ function commitForBoundaryChildren(parent, controlState, childrenVNodes) {
|
|
|
1845
1918
|
withBenchMetricScope("coldCreate", () => {
|
|
1846
1919
|
const frag = parent.ownerDocument.createDocumentFragment();
|
|
1847
1920
|
for (let i = 0; i < count; i++) {
|
|
1848
|
-
const
|
|
1849
|
-
const itemInstance = forState.items.get(itemKey);
|
|
1921
|
+
const itemInstance = items[i];
|
|
1850
1922
|
if (!itemInstance) continue;
|
|
1851
1923
|
const dom = syncForItemDom(parent, itemInstance.scope, childrenVNodes[i]);
|
|
1852
1924
|
if (dom) {
|
|
@@ -1857,13 +1929,13 @@ function commitForBoundaryChildren(parent, controlState, childrenVNodes) {
|
|
|
1857
1929
|
recordBenchCounter("replaceChildrenCommits");
|
|
1858
1930
|
parent.replaceChildren(frag);
|
|
1859
1931
|
});
|
|
1932
|
+
boundaryChildrenExact = true;
|
|
1860
1933
|
return;
|
|
1861
1934
|
}
|
|
1862
1935
|
if (forState.lastRemovedNodes.length === 0) {
|
|
1863
1936
|
const frag = parent.ownerDocument.createDocumentFragment();
|
|
1864
1937
|
for (let i = 0; i < count; i++) {
|
|
1865
|
-
const
|
|
1866
|
-
const itemInstance = forState.items.get(itemKey);
|
|
1938
|
+
const itemInstance = items[i];
|
|
1867
1939
|
if (!itemInstance) continue;
|
|
1868
1940
|
const dom = syncForItemDom(parent, itemInstance.scope, childrenVNodes[i]);
|
|
1869
1941
|
if (!dom) continue;
|
|
@@ -1871,11 +1943,11 @@ function commitForBoundaryChildren(parent, controlState, childrenVNodes) {
|
|
|
1871
1943
|
frag.appendChild(dom);
|
|
1872
1944
|
}
|
|
1873
1945
|
parent.replaceChildren(frag);
|
|
1946
|
+
boundaryChildrenExact = true;
|
|
1874
1947
|
return;
|
|
1875
1948
|
}
|
|
1876
1949
|
for (let i = 0; i < count; i++) {
|
|
1877
|
-
const
|
|
1878
|
-
const itemInstance = forState.items.get(itemKey);
|
|
1950
|
+
const itemInstance = items[i];
|
|
1879
1951
|
if (!itemInstance) continue;
|
|
1880
1952
|
const dom = syncForItemDom(parent, itemInstance.scope, childrenVNodes[i]);
|
|
1881
1953
|
if (!dom) continue;
|
|
@@ -1885,18 +1957,22 @@ function commitForBoundaryChildren(parent, controlState, childrenVNodes) {
|
|
|
1885
1957
|
parent.insertBefore(dom, anchor);
|
|
1886
1958
|
}
|
|
1887
1959
|
}
|
|
1960
|
+
boundaryChildrenExact = true;
|
|
1888
1961
|
};
|
|
1889
1962
|
const isCurrentForDomOrder = () => {
|
|
1890
1963
|
for (let index = 0; index < forState.orderedKeys.length; index += 1) {
|
|
1891
|
-
const
|
|
1892
|
-
const itemInstance = forState.items.get(itemKey);
|
|
1964
|
+
const itemInstance = forState.orderedItems[index];
|
|
1893
1965
|
if (!itemInstance?.scope.dom) return false;
|
|
1894
1966
|
if (parent.childNodes[index] !== itemInstance.scope.dom) return false;
|
|
1895
1967
|
}
|
|
1968
|
+
boundaryChildrenExact = true;
|
|
1896
1969
|
return true;
|
|
1897
1970
|
};
|
|
1898
1971
|
if (forState.lastCommitStrategy === "NO_REORDER" && ensureDirtyIndices().length > 0 && forState.pendingDirtyIndices === null && forState.pendingSwapIndices === null && !forState.pendingMoveOnly && forState.lastRemovedNodes.length === 0 && parent.childNodes.length === forState.orderedKeys.length) {
|
|
1899
|
-
if (
|
|
1972
|
+
if (getRuntimeEnv().NODE_ENV === "production") {
|
|
1973
|
+
commitDirtyNoReorder(ensureDirtyIndices());
|
|
1974
|
+
syncKeyedMapFromForState(parent, forState, "NO_REORDER", []);
|
|
1975
|
+
} else if (isCurrentForDomOrder()) {
|
|
1900
1976
|
commitDirtyNoReorder(ensureDirtyIndices());
|
|
1901
1977
|
syncKeyedMapFromForState(parent, forState, "NO_REORDER", []);
|
|
1902
1978
|
} else {
|
|
@@ -1929,10 +2005,9 @@ function commitForBoundaryChildren(parent, controlState, childrenVNodes) {
|
|
|
1929
2005
|
const syncExactForBoundaryChildren = () => {
|
|
1930
2006
|
const expectedNodes = [];
|
|
1931
2007
|
for (let i = 0; i < forState.orderedKeys.length; i++) {
|
|
1932
|
-
const
|
|
1933
|
-
const itemInstance = forState.items.get(itemKey);
|
|
2008
|
+
const itemInstance = forState.orderedItems[i];
|
|
1934
2009
|
if (!itemInstance) continue;
|
|
1935
|
-
const dom = itemInstance.scope.dom
|
|
2010
|
+
const dom = itemInstance.scope.dom && !itemInstance.scope.needsDomUpdate ? itemInstance.scope.dom : syncForItemDom(parent, itemInstance.scope, childrenVNodes[i]);
|
|
1936
2011
|
if (dom) expectedNodes.push(dom);
|
|
1937
2012
|
}
|
|
1938
2013
|
const currentNodes = Array.from(parent.childNodes);
|
|
@@ -1948,7 +2023,7 @@ function commitForBoundaryChildren(parent, controlState, childrenVNodes) {
|
|
|
1948
2023
|
}
|
|
1949
2024
|
parent.replaceChildren(...expectedNodes);
|
|
1950
2025
|
};
|
|
1951
|
-
syncExactForBoundaryChildren();
|
|
2026
|
+
if (!boundaryChildrenExact) syncExactForBoundaryChildren();
|
|
1952
2027
|
forState._hasResolvedItemDom = true;
|
|
1953
2028
|
recordBenchTiming("domCommit", performance.now() - domCommitStart);
|
|
1954
2029
|
clearForDomUpdateState(forState);
|
|
@@ -1956,7 +2031,7 @@ function commitForBoundaryChildren(parent, controlState, childrenVNodes) {
|
|
|
1956
2031
|
/**
|
|
1957
2032
|
* Update an existing element's attributes and children from vnode
|
|
1958
2033
|
*/
|
|
1959
|
-
function updateElementFromVnode(el, vnode, updateChildren = true) {
|
|
2034
|
+
function updateElementFromVnode(el, vnode, updateChildren = true, forceChildrenUpdate = false) {
|
|
1960
2035
|
if (!_isDOMElement(vnode)) return;
|
|
1961
2036
|
const props = vnode.props || {};
|
|
1962
2037
|
const domVNode = vnode;
|
|
@@ -1968,11 +2043,11 @@ function updateElementFromVnode(el, vnode, updateChildren = true) {
|
|
|
1968
2043
|
const existingListeners = elementListeners.get(el);
|
|
1969
2044
|
const existingReactiveProps = elementReactivePropsCleanup.get(el);
|
|
1970
2045
|
if ((!existingListeners || existingListeners.size === 0) && (!existingReactiveProps || existingReactiveProps.size === 0)) {
|
|
1971
|
-
if (hasMatchingStaticProps(el, props, vnode.type)) {
|
|
2046
|
+
if (!forceChildrenUpdate && hasMatchingStaticProps(el, props, vnode.type)) {
|
|
1972
2047
|
if (updateChildren) {
|
|
1973
2048
|
const children = props.children ?? vnode.children;
|
|
1974
|
-
if (canReuseStaticSubtree(el, domVNode)) return;
|
|
1975
|
-
updateElementChildren(el, children);
|
|
2049
|
+
if (!forceChildrenUpdate && canReuseStaticSubtree(el, domVNode)) return;
|
|
2050
|
+
updateElementChildren(el, children, forceChildrenUpdate);
|
|
1976
2051
|
}
|
|
1977
2052
|
return;
|
|
1978
2053
|
}
|
|
@@ -2122,10 +2197,10 @@ function updateElementFromVnode(el, vnode, updateChildren = true) {
|
|
|
2122
2197
|
if (updateChildren) {
|
|
2123
2198
|
const children = props.children ?? vnode.children;
|
|
2124
2199
|
if (usesReactiveChildren) return;
|
|
2125
|
-
updateElementChildren(el, children);
|
|
2200
|
+
updateElementChildren(el, children, forceChildrenUpdate);
|
|
2126
2201
|
}
|
|
2127
2202
|
}
|
|
2128
|
-
function updateElementChildren(el, children) {
|
|
2203
|
+
function updateElementChildren(el, children, forceUpdate = false) {
|
|
2129
2204
|
const directControlBoundary = getDirectControlBoundaryVNode(children);
|
|
2130
2205
|
if (directControlBoundary) {
|
|
2131
2206
|
const controlState = getControlBoundaryState(directControlBoundary);
|
|
@@ -2145,7 +2220,7 @@ function updateElementChildren(el, children) {
|
|
|
2145
2220
|
return;
|
|
2146
2221
|
}
|
|
2147
2222
|
if (!Array.isArray(children) && isFragmentVNode(children)) {
|
|
2148
|
-
updateUnkeyedChildren(el, normalizeComponentChildren(children));
|
|
2223
|
+
updateUnkeyedChildren(el, normalizeComponentChildren(children), forceUpdate);
|
|
2149
2224
|
return;
|
|
2150
2225
|
}
|
|
2151
2226
|
if (!Array.isArray(children) && (typeof children === "string" || typeof children === "number")) {
|
|
@@ -2178,11 +2253,11 @@ function updateElementChildren(el, children) {
|
|
|
2178
2253
|
keyedElements.delete(el);
|
|
2179
2254
|
return;
|
|
2180
2255
|
}
|
|
2181
|
-
updateUnkeyedChildren(el, children);
|
|
2256
|
+
updateUnkeyedChildren(el, children, forceUpdate);
|
|
2182
2257
|
return;
|
|
2183
2258
|
}
|
|
2184
2259
|
if (_isDOMElement(children)) {
|
|
2185
|
-
updateUnkeyedChildren(el, [children]);
|
|
2260
|
+
updateUnkeyedChildren(el, [children], forceUpdate);
|
|
2186
2261
|
return;
|
|
2187
2262
|
}
|
|
2188
2263
|
for (let n = el.firstChild; n;) {
|
|
@@ -2317,11 +2392,11 @@ function canReuseStaticSubtree(el, vnode) {
|
|
|
2317
2392
|
}
|
|
2318
2393
|
return true;
|
|
2319
2394
|
}
|
|
2320
|
-
function updateUnkeyedChildren(parent, newChildren) {
|
|
2395
|
+
function updateUnkeyedChildren(parent, newChildren, forceUpdate = false) {
|
|
2321
2396
|
const parentNamespace = parent.namespaceURI === SVG_NAMESPACE ? SVG_NAMESPACE : void 0;
|
|
2322
2397
|
const trySyncComponentChild = (currentDom, next) => {
|
|
2323
2398
|
if (typeof next.type !== "function") return null;
|
|
2324
|
-
return syncComponentElement(currentDom, next, next.type, (next.props ?? {}) || {}, parentNamespace);
|
|
2399
|
+
return syncComponentElement(currentDom, next, next.type, (next.props ?? {}) || {}, parentNamespace, forceUpdate);
|
|
2325
2400
|
};
|
|
2326
2401
|
const hasText = newChildren.some((c) => typeof c === "string" || typeof c === "number");
|
|
2327
2402
|
const hasElements = newChildren.some((c) => _isDOMElement(c));
|
|
@@ -2334,7 +2409,7 @@ function updateUnkeyedChildren(parent, newChildren) {
|
|
|
2334
2409
|
const next = newChildren[i];
|
|
2335
2410
|
const current = c[i];
|
|
2336
2411
|
if (!current || next === void 0) continue;
|
|
2337
|
-
if (_isDOMElement(next) && typeof next.type === "string") if (tagsEqualIgnoreCase(current.tagName, next.type)) updateElementFromVnode(current, next);
|
|
2412
|
+
if (_isDOMElement(next) && typeof next.type === "string") if (tagsEqualIgnoreCase(current.tagName, next.type)) updateElementFromVnode(current, next, true, forceUpdate);
|
|
2338
2413
|
else {
|
|
2339
2414
|
const dom = createDOMNode(next, parentNamespace);
|
|
2340
2415
|
if (dom) {
|
|
@@ -2388,7 +2463,7 @@ function updateUnkeyedChildren(parent, newChildren) {
|
|
|
2388
2463
|
}
|
|
2389
2464
|
else if (_isDOMElement(next)) if (currentNode.nodeType === 1) {
|
|
2390
2465
|
const currentEl = currentNode;
|
|
2391
|
-
if (typeof next.type === "string") if (tagsEqualIgnoreCase(currentEl.tagName, next.type)) updateElementFromVnode(currentEl, next);
|
|
2466
|
+
if (typeof next.type === "string") if (tagsEqualIgnoreCase(currentEl.tagName, next.type)) updateElementFromVnode(currentEl, next, true, forceUpdate);
|
|
2392
2467
|
else {
|
|
2393
2468
|
const dom = createDOMNode(next, parentNamespace);
|
|
2394
2469
|
if (dom) {
|
|
@@ -2450,7 +2525,7 @@ function updateUnkeyedChildren(parent, newChildren) {
|
|
|
2450
2525
|
const textNode = document.createTextNode(String(next));
|
|
2451
2526
|
teardownNodeSubtree(current);
|
|
2452
2527
|
parent.replaceChild(textNode, current);
|
|
2453
|
-
} else if (_isDOMElement(next)) if (typeof next.type === "string") if (tagsEqualIgnoreCase(current.tagName, next.type)) updateElementFromVnode(current, next);
|
|
2528
|
+
} else if (_isDOMElement(next)) if (typeof next.type === "string") if (tagsEqualIgnoreCase(current.tagName, next.type)) updateElementFromVnode(current, next, true, forceUpdate);
|
|
2454
2529
|
else {
|
|
2455
2530
|
const dom = createDOMNode(next, parentNamespace);
|
|
2456
2531
|
if (dom) {
|