@askrjs/askr 0.0.45 → 0.0.47
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/benchmark.js +2 -2
- package/dist/renderer/cleanup.js +33 -1
- package/dist/renderer/cleanup.js.map +1 -1
- package/dist/renderer/dom.js +43 -21
- package/dist/renderer/dom.js.map +1 -1
- package/dist/renderer/reconcile.js +34 -19
- package/dist/renderer/reconcile.js.map +1 -1
- package/package.json +3 -3
package/dist/benchmark.js
CHANGED
|
@@ -10,8 +10,8 @@ import { BenchmarkTable } from "./bench/components/benchmark-table.js";
|
|
|
10
10
|
installRendererBridge();
|
|
11
11
|
const benchmarkMetadata = {
|
|
12
12
|
packageName: "@askrjs/askr",
|
|
13
|
-
packageVersion: "0.0.
|
|
14
|
-
buildLabel: "0.0.
|
|
13
|
+
packageVersion: "0.0.47",
|
|
14
|
+
buildLabel: "0.0.47-local"
|
|
15
15
|
};
|
|
16
16
|
function getBenchmarkMetadata() {
|
|
17
17
|
return { ...benchmarkMetadata };
|
package/dist/renderer/cleanup.js
CHANGED
|
@@ -3,6 +3,31 @@ import { incDevCounter } from "../runtime/dev-namespace.js";
|
|
|
3
3
|
import { cleanupComponent } from "../runtime/component.js";
|
|
4
4
|
import { clearDelegatedHandlersForElement, removeDelegatedListener } from "../runtime/events.js";
|
|
5
5
|
//#region src/renderer/cleanup.ts
|
|
6
|
+
const elementRefs = /* @__PURE__ */ new WeakMap();
|
|
7
|
+
function applyRefValue(ref, value) {
|
|
8
|
+
const resolvedRef = ref;
|
|
9
|
+
if (!resolvedRef) return;
|
|
10
|
+
if (typeof resolvedRef === "function") {
|
|
11
|
+
resolvedRef(value);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (Object.isExtensible(resolvedRef)) resolvedRef.current = value;
|
|
15
|
+
}
|
|
16
|
+
function updateElementRef(element, ref) {
|
|
17
|
+
const previousRef = elementRefs.get(element);
|
|
18
|
+
if (previousRef === ref) return;
|
|
19
|
+
if (previousRef) applyRefValue(previousRef, null);
|
|
20
|
+
if (ref) {
|
|
21
|
+
applyRefValue(ref, element);
|
|
22
|
+
elementRefs.set(element, ref);
|
|
23
|
+
} else elementRefs.delete(element);
|
|
24
|
+
}
|
|
25
|
+
function removeElementRef(element) {
|
|
26
|
+
const ref = elementRefs.get(element);
|
|
27
|
+
if (!ref) return;
|
|
28
|
+
applyRefValue(ref, null);
|
|
29
|
+
elementRefs.delete(element);
|
|
30
|
+
}
|
|
6
31
|
function cleanupSingleInstance(node, errors, strict) {
|
|
7
32
|
const instances = /* @__PURE__ */ new Set();
|
|
8
33
|
if (Array.isArray(node.__ASKR_INSTANCES)) {
|
|
@@ -23,6 +48,12 @@ function cleanupSingleInstance(node, errors, strict) {
|
|
|
23
48
|
}
|
|
24
49
|
}
|
|
25
50
|
function teardownSingleElement(element, errors, strict) {
|
|
51
|
+
try {
|
|
52
|
+
removeElementRef(element);
|
|
53
|
+
} catch (err) {
|
|
54
|
+
if (strict) errors.push(err);
|
|
55
|
+
else logger.warn("[Askr] removeElementRef failed:", err);
|
|
56
|
+
}
|
|
26
57
|
try {
|
|
27
58
|
removeElementListeners(element);
|
|
28
59
|
} catch (err) {
|
|
@@ -167,11 +198,12 @@ function removeElementListeners(element) {
|
|
|
167
198
|
function removeAllListeners(root) {
|
|
168
199
|
if (!root) return;
|
|
169
200
|
forEachElementInSubtree(root, (el) => {
|
|
201
|
+
removeElementRef(el);
|
|
170
202
|
removeElementListeners(el);
|
|
171
203
|
removeElementReactiveProps(el);
|
|
172
204
|
});
|
|
173
205
|
}
|
|
174
206
|
//#endregion
|
|
175
|
-
export { REACTIVE_CHILDREN_KEY, cleanupInstanceIfPresent, cleanupInstancesUnder, elementListeners, elementReactivePropsCleanup, removeAllListeners, removeElementListeners, removeElementReactiveProps, teardownNodeSubtree };
|
|
207
|
+
export { REACTIVE_CHILDREN_KEY, cleanupInstanceIfPresent, cleanupInstancesUnder, elementListeners, elementReactivePropsCleanup, elementRefs, removeAllListeners, removeElementListeners, removeElementReactiveProps, removeElementRef, teardownNodeSubtree, updateElementRef };
|
|
176
208
|
|
|
177
209
|
//# sourceMappingURL=cleanup.js.map
|
|
@@ -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 & {\n __ASKR_INSTANCE?: unknown;\n __ASKR_INSTANCES?: unknown[];\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Instance Cleanup Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction cleanupSingleInstance(\n node: InstanceHost,\n errors: unknown[] | null,\n strict: boolean\n): void {\n const instances = new Set<ComponentInstance>();\n\n if (Array.isArray(node.__ASKR_INSTANCES)) {\n for (const instance of node.__ASKR_INSTANCES) {\n if (instance) {\n instances.add(instance as ComponentInstance);\n }\n }\n }\n\n if (node.__ASKR_INSTANCE) {\n instances.add(node.__ASKR_INSTANCE as ComponentInstance);\n }\n\n for (const instance of instances) {\n try {\n cleanupComponent(instance);\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] cleanupComponent failed:', err);\n }\n }\n\n try {\n delete node.__ASKR_INSTANCE;\n delete node.__ASKR_INSTANCES;\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 forEachDescendantNode(root: Node, visit: (node: Node) => void) {\n try {\n const doc = root.ownerDocument;\n const createTreeWalker = doc?.createTreeWalker;\n if (typeof createTreeWalker === 'function') {\n // NodeFilter.SHOW_ALL === 0xffffffff\n const walker = createTreeWalker.call(doc, root, 0xffffffff);\n let node = walker.nextNode();\n while (node) {\n visit(node);\n node = walker.nextNode();\n }\n return;\n }\n } catch {\n // SLOW PATH: TreeWalker unavailable\n }\n\n const stack = Array.from(root.childNodes).reverse();\n while (stack.length > 0) {\n const node = stack.pop()!;\n visit(node);\n for (let child = node.lastChild; child; child = child.previousSibling) {\n stack.push(child);\n }\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 eventName: string;\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 // Clean up any nested instances, including null-component comment hosts.\n try {\n forEachDescendantNode(node, (descendant) => {\n try {\n cleanupSingleInstance(descendant 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 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 teardownSingleElement(node, errors, strict);\n forEachDescendantNode(node, (descendant) => {\n if (descendant instanceof Element) {\n teardownSingleElement(descendant, errors, strict);\n } else {\n cleanupSingleInstance(descendant as InstanceHost, errors, strict);\n }\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 entry of map.values()) {\n incDevCounter('listenerRemoves');\n if (entry.isDelegated) {\n removeDelegatedListener(element, entry.eventName);\n } else {\n if (entry.options !== undefined)\n element.removeEventListener(\n entry.eventName,\n entry.handler,\n entry.options\n );\n else element.removeEventListener(entry.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":"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"}
|
|
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 & {\n __ASKR_INSTANCE?: unknown;\n __ASKR_INSTANCES?: unknown[];\n};\n\ntype Ref<T> =\n | ((value: T | null) => void)\n | { current: T | null }\n | null\n | undefined;\n\nexport const elementRefs = new WeakMap<Element, unknown>();\n\nfunction applyRefValue<T>(ref: unknown, value: T | null): void {\n const resolvedRef = ref as Ref<T>;\n\n if (!resolvedRef) {\n return;\n }\n\n if (typeof resolvedRef === 'function') {\n resolvedRef(value);\n return;\n }\n\n if (Object.isExtensible(resolvedRef)) {\n (resolvedRef as { current: T | null }).current = value;\n }\n}\n\nexport function updateElementRef<T extends Element>(\n element: T,\n ref: unknown\n): void {\n const previousRef = elementRefs.get(element);\n\n if (previousRef === ref) {\n return;\n }\n\n if (previousRef) {\n applyRefValue(previousRef, null);\n }\n\n if (ref) {\n applyRefValue(ref, element);\n elementRefs.set(element, ref);\n } else {\n elementRefs.delete(element);\n }\n}\n\nexport function removeElementRef(element: Element): void {\n const ref = elementRefs.get(element);\n\n if (!ref) {\n return;\n }\n\n applyRefValue(ref, null);\n elementRefs.delete(element);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Instance Cleanup Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction cleanupSingleInstance(\n node: InstanceHost,\n errors: unknown[] | null,\n strict: boolean\n): void {\n const instances = new Set<ComponentInstance>();\n\n if (Array.isArray(node.__ASKR_INSTANCES)) {\n for (const instance of node.__ASKR_INSTANCES) {\n if (instance) {\n instances.add(instance as ComponentInstance);\n }\n }\n }\n\n if (node.__ASKR_INSTANCE) {\n instances.add(node.__ASKR_INSTANCE as ComponentInstance);\n }\n\n for (const instance of instances) {\n try {\n cleanupComponent(instance);\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] cleanupComponent failed:', err);\n }\n }\n\n try {\n delete node.__ASKR_INSTANCE;\n delete node.__ASKR_INSTANCES;\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 removeElementRef(element);\n } catch (err) {\n if (strict) errors!.push(err);\n else logger.warn('[Askr] removeElementRef failed:', err);\n }\n\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 forEachDescendantNode(root: Node, visit: (node: Node) => void) {\n try {\n const doc = root.ownerDocument;\n const createTreeWalker = doc?.createTreeWalker;\n if (typeof createTreeWalker === 'function') {\n // NodeFilter.SHOW_ALL === 0xffffffff\n const walker = createTreeWalker.call(doc, root, 0xffffffff);\n let node = walker.nextNode();\n while (node) {\n visit(node);\n node = walker.nextNode();\n }\n return;\n }\n } catch {\n // SLOW PATH: TreeWalker unavailable\n }\n\n const stack = Array.from(root.childNodes).reverse();\n while (stack.length > 0) {\n const node = stack.pop()!;\n visit(node);\n for (let child = node.lastChild; child; child = child.previousSibling) {\n stack.push(child);\n }\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 eventName: string;\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 // Clean up any nested instances, including null-component comment hosts.\n try {\n forEachDescendantNode(node, (descendant) => {\n try {\n cleanupSingleInstance(descendant 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 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 teardownSingleElement(node, errors, strict);\n forEachDescendantNode(node, (descendant) => {\n if (descendant instanceof Element) {\n teardownSingleElement(descendant, errors, strict);\n } else {\n cleanupSingleInstance(descendant as InstanceHost, errors, strict);\n }\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 entry of map.values()) {\n incDevCounter('listenerRemoves');\n if (entry.isDelegated) {\n removeDelegatedListener(element, entry.eventName);\n } else {\n if (entry.options !== undefined)\n element.removeEventListener(\n entry.eventName,\n entry.handler,\n entry.options\n );\n else element.removeEventListener(entry.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 removeElementRef(el);\n removeElementListeners(el);\n removeElementReactiveProps(el);\n });\n}\n"],"mappings":"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"}
|
package/dist/renderer/dom.js
CHANGED
|
@@ -13,7 +13,7 @@ import { createMutableWrappedHandler, extractKey, getEventListenerKey, getEventL
|
|
|
13
13
|
import { keyedElements } from "./keyed.js";
|
|
14
14
|
import { captureInlineRenderSnapshot, cleanupComponent, createComponentInstance, getCurrentInstance, mountInstanceInline, renderComponentInline, warnUnusedStateReads } from "../runtime/component.js";
|
|
15
15
|
import { addDelegatedListener, getDelegatedHandlerForElement, getDelegatedHandlersForElement, isDelegatedEvent, isEventDelegationEnabled, removeDelegatedListener, updateDelegatedListener } from "../runtime/events.js";
|
|
16
|
-
import { REACTIVE_CHILDREN_KEY, elementListeners, elementReactivePropsCleanup, removeAllListeners, removeElementListeners, removeElementReactiveProps, teardownNodeSubtree } from "./cleanup.js";
|
|
16
|
+
import { REACTIVE_CHILDREN_KEY, elementListeners, elementReactivePropsCleanup, removeAllListeners, removeElementListeners, removeElementReactiveProps, teardownNodeSubtree, updateElementRef } from "./cleanup.js";
|
|
17
17
|
import { ROUTE_ROOT_COMPONENT } from "../common/router-internal.js";
|
|
18
18
|
import "../jsx-runtime.js";
|
|
19
19
|
import { createChildScope, disposeChildScope, rerenderChildScope } from "../runtime/child-scope.js";
|
|
@@ -178,6 +178,31 @@ function cleanupDetachedComponentHost(host, retainedInstance) {
|
|
|
178
178
|
delete host.__ASKR_WRAPPER_HOST;
|
|
179
179
|
} catch {}
|
|
180
180
|
}
|
|
181
|
+
function pruneComponentHostInstances(host, retainedInstances) {
|
|
182
|
+
const retained = new Set(retainedInstances);
|
|
183
|
+
const nextInstances = [];
|
|
184
|
+
const staleInstances = /* @__PURE__ */ new Set();
|
|
185
|
+
const retainOrMarkStale = (instance) => {
|
|
186
|
+
if (!instance) return;
|
|
187
|
+
if (retained.has(instance)) {
|
|
188
|
+
if (!nextInstances.includes(instance)) nextInstances.push(instance);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
staleInstances.add(instance);
|
|
192
|
+
};
|
|
193
|
+
for (const instance of host.__ASKR_INSTANCES ?? []) retainOrMarkStale(instance);
|
|
194
|
+
retainOrMarkStale(host.__ASKR_INSTANCE);
|
|
195
|
+
for (const instance of staleInstances) cleanupComponent(instance);
|
|
196
|
+
try {
|
|
197
|
+
if (nextInstances.length > 0) {
|
|
198
|
+
host.__ASKR_INSTANCES = nextInstances;
|
|
199
|
+
host.__ASKR_INSTANCE = nextInstances[0];
|
|
200
|
+
} else {
|
|
201
|
+
delete host.__ASKR_INSTANCES;
|
|
202
|
+
delete host.__ASKR_INSTANCE;
|
|
203
|
+
}
|
|
204
|
+
} catch {}
|
|
205
|
+
}
|
|
181
206
|
function canUpdateReactiveChildBoundarySequenceSource(previousSource, nextSource) {
|
|
182
207
|
if (!Array.isArray(previousSource) || previousSource.length !== nextSource.length) return false;
|
|
183
208
|
for (let index = 0; index < nextSource.length; index += 1) {
|
|
@@ -938,7 +963,7 @@ function applyPropsToElement(el, props, tagName) {
|
|
|
938
963
|
for (const key in props) {
|
|
939
964
|
const value = props[key];
|
|
940
965
|
if (key === "ref") {
|
|
941
|
-
|
|
966
|
+
updateElementRef(el, value);
|
|
942
967
|
continue;
|
|
943
968
|
}
|
|
944
969
|
if (isSkippedProp(key)) continue;
|
|
@@ -966,15 +991,6 @@ function applyPropsToElement(el, props, tagName) {
|
|
|
966
991
|
else setRenderedAttribute(el, key, String(value));
|
|
967
992
|
}
|
|
968
993
|
}
|
|
969
|
-
function applyRef(el, ref) {
|
|
970
|
-
const r = ref;
|
|
971
|
-
if (!r) return;
|
|
972
|
-
if (typeof r === "function") {
|
|
973
|
-
r(el);
|
|
974
|
-
return;
|
|
975
|
-
}
|
|
976
|
-
if (Object.isExtensible(r)) r.current = el;
|
|
977
|
-
}
|
|
978
994
|
function removeStaleAttributes(el, vnode, props) {
|
|
979
995
|
const desiredAttributes = /* @__PURE__ */ new Set();
|
|
980
996
|
if (extractKey(vnode) !== void 0) desiredAttributes.add("data-key");
|
|
@@ -1301,11 +1317,12 @@ function isFragmentVNode(node) {
|
|
|
1301
1317
|
}
|
|
1302
1318
|
function normalizeComponentChildren(result) {
|
|
1303
1319
|
if (result === null || result === void 0 || result === false) return [];
|
|
1304
|
-
if (Array.isArray(result))
|
|
1305
|
-
|
|
1306
|
-
const
|
|
1307
|
-
return
|
|
1320
|
+
if (Array.isArray(result)) {
|
|
1321
|
+
const children = [];
|
|
1322
|
+
for (const child of result) children.push(...normalizeComponentChildren(child));
|
|
1323
|
+
return children;
|
|
1308
1324
|
}
|
|
1325
|
+
if (isFragmentVNode(result)) return normalizeComponentChildren(result.props?.children ?? result.children ?? []);
|
|
1309
1326
|
return [result];
|
|
1310
1327
|
}
|
|
1311
1328
|
function syncComponentElement(currentDom, node, type, props, parentNamespace, forceChildrenUpdate = false, retainedHostInstances) {
|
|
@@ -1324,6 +1341,7 @@ function syncComponentElement(currentDom, node, type, props, parentNamespace, fo
|
|
|
1324
1341
|
if (isPromiseLike(result)) throw new Error("Async components are not supported. Components must return synchronously.");
|
|
1325
1342
|
const scopedResult = markVNodeTreeWithContextFrame(result, snapshot ?? null);
|
|
1326
1343
|
if (scopedResult && typeof scopedResult === "object" && "type" in scopedResult && typeof scopedResult.type === "string" && tagNamesEqualIgnoreCase(existingHost.tagName, scopedResult.type)) {
|
|
1344
|
+
pruneComponentHostInstances(existingHost, retainedHostInstances ? [hydrationInstance, ...retainedHostInstances] : [hydrationInstance]);
|
|
1327
1345
|
withContext(snapshot, () => {
|
|
1328
1346
|
updateElementFromVnode(existingHost, inheritComponentKey(scopedResult, node), true, forceChildrenUpdate || hydrationInstance.mounted === false);
|
|
1329
1347
|
materializeKey(existingHost, node, props);
|
|
@@ -1369,6 +1387,7 @@ function syncComponentElement(currentDom, node, type, props, parentNamespace, fo
|
|
|
1369
1387
|
return existingHost;
|
|
1370
1388
|
}
|
|
1371
1389
|
if (scopedResult && typeof scopedResult === "object" && "type" in scopedResult && typeof scopedResult.type === "string" && tagNamesEqualIgnoreCase(existingHost.tagName, scopedResult.type)) {
|
|
1390
|
+
pruneComponentHostInstances(existingHost, retainedHostInstances ? [existingInstance, ...retainedHostInstances] : [existingInstance]);
|
|
1372
1391
|
withContext(snapshot, () => {
|
|
1373
1392
|
updateElementFromVnode(existingHost, inheritComponentKey(scopedResult, node), true, forceChildrenUpdate || existingInstance.mounted === false);
|
|
1374
1393
|
materializeKey(existingHost, node, props);
|
|
@@ -1766,6 +1785,7 @@ function updateElementFromVnode(el, vnode, updateChildren = true, forceChildrenU
|
|
|
1766
1785
|
return;
|
|
1767
1786
|
}
|
|
1768
1787
|
materializeKey(el, vnode, props);
|
|
1788
|
+
updateElementRef(el, props.ref);
|
|
1769
1789
|
const existingListeners = elementListeners.get(el);
|
|
1770
1790
|
const existingReactiveProps = elementReactivePropsCleanup.get(el);
|
|
1771
1791
|
if ((!existingListeners || existingListeners.size === 0) && (!existingReactiveProps || existingReactiveProps.size === 0)) {
|
|
@@ -1785,6 +1805,7 @@ function updateElementFromVnode(el, vnode, updateChildren = true, forceChildrenU
|
|
|
1785
1805
|
if (usesReactiveChildren) (desiredReactivePropNames ??= /* @__PURE__ */ new Set()).add(REACTIVE_CHILDREN_KEY);
|
|
1786
1806
|
for (const key in props) {
|
|
1787
1807
|
const value = props[key];
|
|
1808
|
+
if (key === "ref") continue;
|
|
1788
1809
|
if (isSkippedProp(key)) continue;
|
|
1789
1810
|
const eventProp = parseEventProp(key);
|
|
1790
1811
|
const eventName = eventProp?.eventName;
|
|
@@ -1973,21 +1994,22 @@ function updateElementChildren(el, children, forceUpdate = false) {
|
|
|
1973
1994
|
return;
|
|
1974
1995
|
}
|
|
1975
1996
|
if (Array.isArray(children)) {
|
|
1976
|
-
|
|
1997
|
+
const normalizedChildren = normalizeComponentChildren(children);
|
|
1998
|
+
if (trySyncScalarChildSequenceInPlace(el, normalizedChildren)) {
|
|
1977
1999
|
keyedElements.delete(el);
|
|
1978
2000
|
return;
|
|
1979
2001
|
}
|
|
1980
|
-
if (hasKeyedVNodeChildren(
|
|
1981
|
-
const newKeyMap = reconcileKeyedChildren(el,
|
|
2002
|
+
if (hasKeyedVNodeChildren(normalizedChildren)) {
|
|
2003
|
+
const newKeyMap = reconcileKeyedChildren(el, normalizedChildren, getOrBuildDomKeyMap(el));
|
|
1982
2004
|
keyedElements.set(el, newKeyMap);
|
|
1983
2005
|
return;
|
|
1984
2006
|
}
|
|
1985
|
-
if (isBulkTextFastPathEligible(el,
|
|
1986
|
-
performBulkTextReplace(el,
|
|
2007
|
+
if (isBulkTextFastPathEligible(el, normalizedChildren)) {
|
|
2008
|
+
performBulkTextReplace(el, normalizedChildren);
|
|
1987
2009
|
keyedElements.delete(el);
|
|
1988
2010
|
return;
|
|
1989
2011
|
}
|
|
1990
|
-
updateUnkeyedChildren(el,
|
|
2012
|
+
updateUnkeyedChildren(el, normalizedChildren, forceUpdate);
|
|
1991
2013
|
return;
|
|
1992
2014
|
}
|
|
1993
2015
|
if (_isDOMElement(children)) {
|