@llui/dom 0.0.17 → 0.0.19
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/binding.d.ts.map +1 -1
- package/dist/binding.js +5 -0
- package/dist/binding.js.map +1 -1
- package/dist/devtools.d.ts +109 -0
- package/dist/devtools.d.ts.map +1 -1
- package/dist/devtools.js +401 -1
- package/dist/devtools.js.map +1 -1
- package/dist/hmr.d.ts.map +1 -1
- package/dist/hmr.js +2 -0
- package/dist/hmr.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mount.d.ts.map +1 -1
- package/dist/mount.js +18 -2
- package/dist/mount.js.map +1 -1
- package/dist/primitives/branch.d.ts.map +1 -1
- package/dist/primitives/branch.js +10 -1
- package/dist/primitives/branch.js.map +1 -1
- package/dist/primitives/child.d.ts.map +1 -1
- package/dist/primitives/child.js +25 -5
- package/dist/primitives/child.js.map +1 -1
- package/dist/primitives/context.d.ts +30 -6
- package/dist/primitives/context.d.ts.map +1 -1
- package/dist/primitives/context.js +30 -6
- package/dist/primitives/context.js.map +1 -1
- package/dist/primitives/each.d.ts.map +1 -1
- package/dist/primitives/each.js +79 -6
- package/dist/primitives/each.js.map +1 -1
- package/dist/primitives/foreign.d.ts.map +1 -1
- package/dist/primitives/foreign.js +1 -0
- package/dist/primitives/foreign.js.map +1 -1
- package/dist/primitives/lazy.d.ts.map +1 -1
- package/dist/primitives/lazy.js +1 -0
- package/dist/primitives/lazy.js.map +1 -1
- package/dist/primitives/portal.d.ts.map +1 -1
- package/dist/primitives/portal.js +1 -0
- package/dist/primitives/portal.js.map +1 -1
- package/dist/primitives/show.d.ts.map +1 -1
- package/dist/primitives/show.js +4 -0
- package/dist/primitives/show.js.map +1 -1
- package/dist/primitives/virtual-each.d.ts.map +1 -1
- package/dist/primitives/virtual-each.js +3 -0
- package/dist/primitives/virtual-each.js.map +1 -1
- package/dist/render-context.d.ts.map +1 -1
- package/dist/render-context.js.map +1 -1
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js +68 -1
- package/dist/scope.js.map +1 -1
- package/dist/ssr.d.ts.map +1 -1
- package/dist/ssr.js +5 -1
- package/dist/ssr.js.map +1 -1
- package/dist/tracking/coverage.d.ts +27 -0
- package/dist/tracking/coverage.d.ts.map +1 -0
- package/dist/tracking/coverage.js +39 -0
- package/dist/tracking/coverage.js.map +1 -0
- package/dist/tracking/disposer-log.d.ts +22 -0
- package/dist/tracking/disposer-log.d.ts.map +1 -0
- package/dist/tracking/disposer-log.js +3 -0
- package/dist/tracking/disposer-log.js.map +1 -0
- package/dist/tracking/each-diff.d.ts +62 -0
- package/dist/tracking/each-diff.d.ts.map +1 -0
- package/dist/tracking/each-diff.js +26 -0
- package/dist/tracking/each-diff.js.map +1 -0
- package/dist/tracking/effect-timeline.d.ts +73 -0
- package/dist/tracking/effect-timeline.d.ts.map +1 -0
- package/dist/tracking/effect-timeline.js +89 -0
- package/dist/tracking/effect-timeline.js.map +1 -0
- package/dist/types.d.ts +18 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/update-loop.d.ts.map +1 -1
- package/dist/update-loop.js +78 -0
- package/dist/update-loop.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch.js","sourceRoot":"","sources":["../../src/primitives/branch.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"branch.js","sourceRoot":"","sources":["../../src/primitives/branch.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BranchOptions, Scope } from '../types.js'\nimport { getRenderContext, setRenderContext, clearRenderContext } from '../render-context.js'\nimport { createScope, disposeScope, addDisposer } from '../scope.js'\nimport { setFlatBindings } from '../binding.js'\nimport { createView } from '../view-helpers.js'\nimport { FULL_MASK } from '../update-loop.js'\nimport { pushMountQueue, popMountQueue, flushMountQueue } from './on-mount.js'\nimport type { StructuralBlock } from '../structural.js'\n\nexport function branch<S, M = unknown>(opts: BranchOptions<S, M>): Node[] {\n const ctx = getRenderContext('branch')\n const parentScope = ctx.rootScope\n const blocks = ctx.structuralBlocks\n const send = ctx.send as (msg: M) => void\n\n const anchor = document.createComment('branch')\n\n let currentKey = opts.on(ctx.state as S)\n let currentScope: Scope | null = null\n let currentNodes: Node[] = []\n\n const block: StructuralBlock = {\n mask: (opts as { __mask?: number }).__mask ?? FULL_MASK,\n reconcile(state: unknown) {\n const newKey = opts.on(state as S)\n if (Object.is(newKey, currentKey)) return\n\n const parent = anchor.parentNode\n if (!parent) return\n\n const leavingNodes = currentNodes\n const leavingScope = currentScope\n\n // Build new arm first (before removing old — for FLIP animations)\n currentNodes = []\n currentScope = null\n currentKey = newKey\n\n const newCaseKey = String(newKey)\n const newBuilder = opts.cases[newCaseKey]\n // Collect onMount callbacks from the new case into a local queue,\n // then flush them SYNCHRONOUSLY after the new nodes are inserted.\n // Without this, onMount inside a branch case would see stale DOM\n // (nodes not yet attached) OR fall back to queueMicrotask and\n // race with synchronous event dispatches after the reconcile.\n let onMountQueue: Array<() => void> | null = null\n if (newBuilder) {\n const mq = pushMountQueue()\n onMountQueue = mq.queue\n currentScope = createScope(parentScope)\n currentScope._kind = opts.__disposalCause === 'show-hide' ? 'show' : 'branch'\n setFlatBindings(ctx.allBindings)\n setRenderContext({ ...ctx, rootScope: currentScope, state })\n currentNodes = newBuilder(createView<S, M>(send))\n clearRenderContext()\n setFlatBindings(null)\n popMountQueue(mq.prev)\n\n const ref = anchor.nextSibling\n for (const node of currentNodes) {\n parent.insertBefore(node, ref)\n }\n }\n if (onMountQueue) flushMountQueue(onMountQueue)\n\n // Fire enter for new nodes\n if (opts.enter && currentNodes.length > 0) {\n opts.enter(currentNodes)\n }\n\n // Handle leave — may be deferred via Promise\n const removeOld = () => {\n for (const node of leavingNodes) {\n if (node.parentNode) node.parentNode.removeChild(node)\n }\n if (leavingScope) {\n // Tag BEFORE dispose so the disposer log records the cause.\n // `show()` passes `__disposalCause: 'show-hide'`; raw branch()\n // defaults to `'branch-swap'`. Tag wins over any pre-existing\n // value set by an inner primitive so the outermost cause is\n // reported (matches how humans describe the event).\n leavingScope.disposalCause = opts.__disposalCause ?? 'branch-swap'\n disposeScope(leavingScope)\n }\n }\n\n if (leavingNodes.length > 0 && opts.leave) {\n const result = opts.leave(leavingNodes)\n if (result && typeof (result as Promise<void>).then === 'function') {\n ;(result as Promise<void>).then(removeOld)\n } else {\n removeOld()\n }\n } else {\n removeOld()\n }\n },\n }\n\n // Register the block BEFORE running the initial builder so that parent\n // blocks always precede their nested children in the flat blocks array.\n // This guarantees correct Phase 1 iteration order: parents reconcile\n // first, so a parent that unmounts its old arm can dispose nested child\n // blocks (splicing them out of this array) without corrupting the loop\n // index — the splice only affects entries to the RIGHT of the parent.\n blocks.push(block)\n\n const caseKey = String(currentKey)\n const builder = opts.cases[caseKey]\n // Initial-mount onMount callbacks are handled by the outer mountApp\n // queue — we're still inside the first view() call. branch doesn't\n // insert into the DOM at this point (the anchor + initial children\n // are returned to the parent), so we don't need to flush here.\n if (builder) {\n currentScope = createScope(parentScope)\n currentScope._kind = opts.__disposalCause === 'show-hide' ? 'show' : 'branch'\n setRenderContext({ ...ctx, rootScope: currentScope })\n currentNodes = builder(createView<S, M>(send))\n clearRenderContext()\n setRenderContext(ctx)\n\n // Fire enter on initial mount\n if (opts.enter && currentNodes.length > 0) {\n opts.enter(currentNodes)\n }\n }\n\n addDisposer(parentScope, () => {\n const idx = blocks.indexOf(block)\n if (idx !== -1) blocks.splice(idx, 1)\n if (currentScope) {\n disposeScope(currentScope)\n currentScope = null\n }\n })\n\n return [anchor, ...currentNodes]\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"child.d.ts","sourceRoot":"","sources":["../../src/primitives/child.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAgB,MAAM,aAAa,CAAA;AAU7D,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"child.d.ts","sourceRoot":"","sources":["../../src/primitives/child.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAgB,MAAM,aAAa,CAAA;AAU7D,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,IAAI,EAAE,CAsGtE"}
|
package/dist/primitives/child.js
CHANGED
|
@@ -15,6 +15,13 @@ export function child(opts) {
|
|
|
15
15
|
const parentCtx = getRenderContext('child');
|
|
16
16
|
const parentScope = parentCtx.rootScope;
|
|
17
17
|
const childScope = createScope(parentScope);
|
|
18
|
+
childScope._kind = 'child';
|
|
19
|
+
// Tag eagerly: childScope lives as long as the child component is mounted,
|
|
20
|
+
// so disposing it IS the child-unmount event. Setting the cause up front
|
|
21
|
+
// (instead of inside the disposer closure below) ensures the parent's
|
|
22
|
+
// _disposerLog sees it when `disposeScope` walks up the parent chain —
|
|
23
|
+
// `childInst.rootScope` is an orphan (parent = null) and cannot emit.
|
|
24
|
+
childScope.disposalCause = 'child-unmount';
|
|
18
25
|
const parentSend = parentCtx.send;
|
|
19
26
|
const childDef = opts.def;
|
|
20
27
|
const initialProps = opts.props(parentCtx.state);
|
|
@@ -35,7 +42,11 @@ export function child(opts) {
|
|
|
35
42
|
};
|
|
36
43
|
// Track props for shallow-diff
|
|
37
44
|
let prevProps = { ...initialProps };
|
|
38
|
-
// Register a binding on the child scope that watches parent props changes
|
|
45
|
+
// Register a binding on the child scope that watches parent props changes.
|
|
46
|
+
// This is a side-effect-only (`kind: 'effect'`) binding: Phase 2 runs the
|
|
47
|
+
// accessor purely to fire the diff + propsMsg dispatch below. There is no
|
|
48
|
+
// DOM output — the comment node is a detached anchor kept only so the
|
|
49
|
+
// Binding shape stays uniform with other kinds.
|
|
39
50
|
createBinding(childScope, {
|
|
40
51
|
mask: FULL_MASK,
|
|
41
52
|
accessor: ((parentState) => {
|
|
@@ -49,19 +60,27 @@ export function child(opts) {
|
|
|
49
60
|
}
|
|
50
61
|
if (changed && childDef.propsMsg) {
|
|
51
62
|
const msg = childDef.propsMsg(newProps);
|
|
52
|
-
|
|
63
|
+
// Dispatch via `originalSend` so framework-synthesized propsMsg
|
|
64
|
+
// traffic bypasses the `onMsg` wrapper below. Otherwise a naive
|
|
65
|
+
// `onMsg: m => forward(m)` echoes props/set back to the parent,
|
|
66
|
+
// which mutates parent state, re-fires this accessor, and loops
|
|
67
|
+
// forever.
|
|
68
|
+
originalSend(msg);
|
|
53
69
|
flushInstance(childInst);
|
|
54
70
|
}
|
|
55
71
|
prevProps = { ...newProps };
|
|
56
|
-
return newProps;
|
|
57
72
|
}),
|
|
58
|
-
kind: '
|
|
73
|
+
kind: 'effect',
|
|
59
74
|
node: document.createComment('child:' + opts.key),
|
|
60
75
|
perItem: false,
|
|
61
76
|
});
|
|
62
77
|
// Run the child's view within the child's render context
|
|
63
78
|
setFlatBindings(childInst.allBindings);
|
|
64
|
-
setRenderContext({
|
|
79
|
+
setRenderContext({
|
|
80
|
+
...childInst,
|
|
81
|
+
send: childInst.send,
|
|
82
|
+
instance: childInst,
|
|
83
|
+
});
|
|
65
84
|
const nodes = childDef.view(createView(childInst.send));
|
|
66
85
|
clearRenderContext();
|
|
67
86
|
setFlatBindings(parentCtx.allBindings);
|
|
@@ -71,6 +90,7 @@ export function child(opts) {
|
|
|
71
90
|
// Cleanup: dispose child instance when parent scope disposes
|
|
72
91
|
addDisposer(childScope, () => {
|
|
73
92
|
unregisterChild(opts.key);
|
|
93
|
+
childInst.rootScope.disposalCause = 'child-unmount';
|
|
74
94
|
disposeScope(childInst.rootScope);
|
|
75
95
|
});
|
|
76
96
|
return nodes;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"child.js","sourceRoot":"","sources":["../../src/primitives/child.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC7F,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACpE,OAAO,EAAE,uBAAuB,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"child.js","sourceRoot":"","sources":["../../src/primitives/child.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChildOptions, ComponentDef } from '../types.js'\nimport { getRenderContext, setRenderContext, clearRenderContext } from '../render-context.js'\nimport { createScope, disposeScope, addDisposer } from '../scope.js'\nimport { createComponentInstance, flushInstance, type ComponentInstance } from '../update-loop.js'\nimport { createBinding, setFlatBindings } from '../binding.js'\nimport { registerChild, unregisterChild } from '../addressed.js'\nimport { createView } from '../view-helpers.js'\n\nconst FULL_MASK = 0xffffffff\n\nexport function child<S, ChildM>(opts: ChildOptions<S, ChildM>): Node[] {\n // Dev-mode guard: props must be a function, not a static object\n if (typeof opts.props !== 'function') {\n throw new Error(\n `child(\"${String(opts.key)}\"): props must be a reactive accessor function ` +\n `(s => ({ ... })), not a static object. Static props are captured once at mount ` +\n `and never update.`,\n )\n }\n\n const parentCtx = getRenderContext('child')\n const parentScope = parentCtx.rootScope\n const childScope = createScope(parentScope)\n childScope._kind = 'child'\n // Tag eagerly: childScope lives as long as the child component is mounted,\n // so disposing it IS the child-unmount event. Setting the cause up front\n // (instead of inside the disposer closure below) ensures the parent's\n // _disposerLog sees it when `disposeScope` walks up the parent chain —\n // `childInst.rootScope` is an orphan (parent = null) and cannot emit.\n childScope.disposalCause = 'child-unmount'\n const parentSend = parentCtx.send\n\n const childDef = opts.def as ComponentDef<unknown, ChildM, unknown>\n const initialProps = opts.props(parentCtx.state as S)\n const childInst = createComponentInstance(childDef, initialProps)\n\n // Wrap child's send to intercept messages for onMsg → parent\n const originalSend = childInst.send\n childInst.send = (msg: ChildM) => {\n originalSend(msg)\n if (opts.onMsg && parentSend) {\n // Defer to after the child processes — use microtask\n queueMicrotask(() => {\n const parentMsg = opts.onMsg!(msg)\n if (parentMsg != null) {\n parentSend(parentMsg)\n }\n })\n }\n }\n\n // Track props for shallow-diff\n let prevProps: Record<string, unknown> = { ...initialProps }\n\n // Register a binding on the child scope that watches parent props changes.\n // This is a side-effect-only (`kind: 'effect'`) binding: Phase 2 runs the\n // accessor purely to fire the diff + propsMsg dispatch below. There is no\n // DOM output — the comment node is a detached anchor kept only so the\n // Binding shape stays uniform with other kinds.\n createBinding(childScope, {\n mask: FULL_MASK,\n accessor: ((parentState: S) => {\n const newProps = opts.props(parentState)\n\n let changed = false\n for (const key of Object.keys(newProps)) {\n if (!Object.is(newProps[key], prevProps[key])) {\n changed = true\n break\n }\n }\n\n if (changed && childDef.propsMsg) {\n const msg = childDef.propsMsg(newProps)\n // Dispatch via `originalSend` so framework-synthesized propsMsg\n // traffic bypasses the `onMsg` wrapper below. Otherwise a naive\n // `onMsg: m => forward(m)` echoes props/set back to the parent,\n // which mutates parent state, re-fires this accessor, and loops\n // forever.\n originalSend(msg)\n flushInstance(childInst)\n }\n prevProps = { ...newProps }\n }) as (state: never) => unknown,\n kind: 'effect',\n node: document.createComment('child:' + opts.key),\n perItem: false,\n })\n\n // Run the child's view within the child's render context\n setFlatBindings(childInst.allBindings)\n setRenderContext({\n ...childInst,\n send: childInst.send as (msg: unknown) => void,\n instance: childInst as ComponentInstance,\n })\n const nodes = childDef.view(createView(childInst.send))\n clearRenderContext()\n setFlatBindings(parentCtx.allBindings)\n setRenderContext(parentCtx)\n\n // Register in component registry for addressed effects\n registerChild(opts.key, { send: childInst.send as (msg: unknown) => void })\n\n // Cleanup: dispose child instance when parent scope disposes\n addDisposer(childScope, () => {\n unregisterChild(opts.key)\n childInst.rootScope.disposalCause = 'child-unmount'\n disposeScope(childInst.rootScope)\n })\n\n return nodes\n}\n"]}
|
|
@@ -80,12 +80,36 @@ export declare function provideValue<T>(ctx: Context<T>, value: T, children: ()
|
|
|
80
80
|
* three-step "look up the accessor, ignore the state arg, get the
|
|
81
81
|
* value" dance.
|
|
82
82
|
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
83
|
+
* ## Value capture contract
|
|
84
|
+
*
|
|
85
|
+
* **The returned value is captured once, at view-construction time.**
|
|
86
|
+
* Any reference you store from `useContextValue(ctx)` into a closure
|
|
87
|
+
* — for example, by assigning it to a local `const` inside `view(...)`
|
|
88
|
+
* and reading it from an event handler — sees the value as it was
|
|
89
|
+
* when the view ran. The closure does NOT re-read the context on each
|
|
90
|
+
* event dispatch.
|
|
91
|
+
*
|
|
92
|
+
* That's fine, and usually what you want, for stable dispatcher bags:
|
|
93
|
+
* the bag's methods close over the layout's `send`, and `send` itself
|
|
94
|
+
* is stable across the layout's lifetime, so the methods work
|
|
95
|
+
* correctly regardless of when the handler fires. Pages can stash
|
|
96
|
+
* `const toast = useContextValue(ToastContext)` at the top of their
|
|
97
|
+
* `view()` and call `toast.show(...)` from any event handler below.
|
|
98
|
+
*
|
|
99
|
+
* **Do NOT use `useContextValue` when the consumer needs to see
|
|
100
|
+
* updates to the context value.** If a parent re-`provideValue`s the
|
|
101
|
+
* context with a different object later, existing consumers already
|
|
102
|
+
* holding the captured value will still see the old one. For
|
|
103
|
+
* reactive consumption, use `useContext(ctx)` — that returns an
|
|
104
|
+
* accessor that re-reads the provider on each binding evaluation, so
|
|
105
|
+
* reactive bindings (`class`, `text`, etc.) pick up updates
|
|
106
|
+
* automatically.
|
|
107
|
+
*
|
|
108
|
+
* **Do NOT use `useContextValue` against a provider whose accessor
|
|
109
|
+
* reads from state.** The accessor is invoked with `undefined` here,
|
|
110
|
+
* so any `(s) => s.something` provider will throw or return garbage.
|
|
111
|
+
* Match `provideValue` on the producer side with `useContextValue` on
|
|
112
|
+
* the consumer side, and `provide` with `useContext`.
|
|
89
113
|
*/
|
|
90
114
|
export declare function useContextValue<T>(ctx: Context<T>): T;
|
|
91
115
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/primitives/context.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,OAAO,CAAC,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,SAAS,CAAA;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAE5E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,EAC1B,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EACf,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EACrB,QAAQ,EAAE,MAAM,IAAI,EAAE,GACrB,IAAI,EAAE,CAoBR;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CA0B7D;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,IAAI,EAAE,GAAG,IAAI,EAAE,CAEzF;AAED
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/primitives/context.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,OAAO,CAAC,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,SAAS,CAAA;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAE5E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,EAC1B,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EACf,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EACrB,QAAQ,EAAE,MAAM,IAAI,EAAE,GACrB,IAAI,EAAE,CAoBR;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CA0B7D;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,IAAI,EAAE,GAAG,IAAI,EAAE,CAEzF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAKrD"}
|
|
@@ -132,12 +132,36 @@ export function provideValue(ctx, value, children) {
|
|
|
132
132
|
* three-step "look up the accessor, ignore the state arg, get the
|
|
133
133
|
* value" dance.
|
|
134
134
|
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
135
|
+
* ## Value capture contract
|
|
136
|
+
*
|
|
137
|
+
* **The returned value is captured once, at view-construction time.**
|
|
138
|
+
* Any reference you store from `useContextValue(ctx)` into a closure
|
|
139
|
+
* — for example, by assigning it to a local `const` inside `view(...)`
|
|
140
|
+
* and reading it from an event handler — sees the value as it was
|
|
141
|
+
* when the view ran. The closure does NOT re-read the context on each
|
|
142
|
+
* event dispatch.
|
|
143
|
+
*
|
|
144
|
+
* That's fine, and usually what you want, for stable dispatcher bags:
|
|
145
|
+
* the bag's methods close over the layout's `send`, and `send` itself
|
|
146
|
+
* is stable across the layout's lifetime, so the methods work
|
|
147
|
+
* correctly regardless of when the handler fires. Pages can stash
|
|
148
|
+
* `const toast = useContextValue(ToastContext)` at the top of their
|
|
149
|
+
* `view()` and call `toast.show(...)` from any event handler below.
|
|
150
|
+
*
|
|
151
|
+
* **Do NOT use `useContextValue` when the consumer needs to see
|
|
152
|
+
* updates to the context value.** If a parent re-`provideValue`s the
|
|
153
|
+
* context with a different object later, existing consumers already
|
|
154
|
+
* holding the captured value will still see the old one. For
|
|
155
|
+
* reactive consumption, use `useContext(ctx)` — that returns an
|
|
156
|
+
* accessor that re-reads the provider on each binding evaluation, so
|
|
157
|
+
* reactive bindings (`class`, `text`, etc.) pick up updates
|
|
158
|
+
* automatically.
|
|
159
|
+
*
|
|
160
|
+
* **Do NOT use `useContextValue` against a provider whose accessor
|
|
161
|
+
* reads from state.** The accessor is invoked with `undefined` here,
|
|
162
|
+
* so any `(s) => s.something` provider will throw or return garbage.
|
|
163
|
+
* Match `provideValue` on the producer side with `useContextValue` on
|
|
164
|
+
* the consumer side, and `provide` with `useContext`.
|
|
141
165
|
*/
|
|
142
166
|
export function useContextValue(ctx) {
|
|
143
167
|
const accessor = useContext(ctx);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/primitives/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC7F,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzC;;;GAGG;AACH,MAAM,UAAU,GAAG,IAAI,OAAO,EAA+C,CAAA;AAQ7E;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAI,YAAgB,EAAE,IAAa;IAC9D,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,IAAI,UAAU,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;AACjF,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,OAAO,CACrB,GAAe,EACf,QAAqB,EACrB,QAAsB;IAEtB,MAAM,SAAS,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAA;IAC7C,MAAM,WAAW,GAAG,SAAS,CAAC,SAAS,CAAA;IACvC,wEAAwE;IACxE,wEAAwE;IACxE,qEAAqE;IACrE,uEAAuE;IACvE,MAAM,aAAa,GAAG,WAAW,CAAC,WAAW,CAAC,CAAA;IAC9C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAmC,CAAA;IACtD,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,QAAmC,CAAC,CAAA;IACrD,UAAU,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,CAAA;IAClC,sEAAsE;IACtE,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,GAAG,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAA;IAC5D,IAAI,CAAC;QACH,OAAO,QAAQ,EAAE,CAAA;IACnB,CAAC;YAAS,CAAC;QACT,kBAAkB,EAAE,CAAA;QACpB,gBAAgB,CAAC,SAAS,CAAC,CAAA;IAC7B,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CAAO,GAAe;IAC9C,IAAI,KAAK,GAAiB,IAAI,CAAA;IAC9B,IAAI,CAAC;QACH,KAAK,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAC,SAAS,CAAA;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,iEAAiE;QACjE,4CAA4C;IAC9C,CAAC;IACD,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACjC,IAAI,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAE,CAAA;YAClC,OAAO,QAAuB,CAAA;QAChC,CAAC;QACD,KAAK,GAAG,KAAK,CAAC,MAAM,CAAA;IACtB,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAA;QACtB,OAAO,GAAG,EAAE,CAAC,CAAC,CAAA;IAChB,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,WAAW,IAAI,SAAS,CAAA;IAC3D,MAAM,IAAI,KAAK,CACb,qBAAqB,KAAK,6CAA6C;QACrE,sCAAsC,KAAK,2BAA2B;QACtE,uCAAuC,CAC1C,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,YAAY,CAAI,GAAe,EAAE,KAAQ,EAAE,QAAsB;IAC/E,OAAO,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;AAC5C,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/primitives/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC7F,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzC;;;GAGG;AACH,MAAM,UAAU,GAAG,IAAI,OAAO,EAA+C,CAAA;AAQ7E;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAI,YAAgB,EAAE,IAAa;IAC9D,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,IAAI,UAAU,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;AACjF,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,OAAO,CACrB,GAAe,EACf,QAAqB,EACrB,QAAsB;IAEtB,MAAM,SAAS,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAA;IAC7C,MAAM,WAAW,GAAG,SAAS,CAAC,SAAS,CAAA;IACvC,wEAAwE;IACxE,wEAAwE;IACxE,qEAAqE;IACrE,uEAAuE;IACvE,MAAM,aAAa,GAAG,WAAW,CAAC,WAAW,CAAC,CAAA;IAC9C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAmC,CAAA;IACtD,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,QAAmC,CAAC,CAAA;IACrD,UAAU,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,CAAA;IAClC,sEAAsE;IACtE,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,GAAG,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAA;IAC5D,IAAI,CAAC;QACH,OAAO,QAAQ,EAAE,CAAA;IACnB,CAAC;YAAS,CAAC;QACT,kBAAkB,EAAE,CAAA;QACpB,gBAAgB,CAAC,SAAS,CAAC,CAAA;IAC7B,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CAAO,GAAe;IAC9C,IAAI,KAAK,GAAiB,IAAI,CAAA;IAC9B,IAAI,CAAC;QACH,KAAK,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAC,SAAS,CAAA;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,iEAAiE;QACjE,4CAA4C;IAC9C,CAAC;IACD,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACjC,IAAI,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAE,CAAA;YAClC,OAAO,QAAuB,CAAA;QAChC,CAAC;QACD,KAAK,GAAG,KAAK,CAAC,MAAM,CAAA;IACtB,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAA;QACtB,OAAO,GAAG,EAAE,CAAC,CAAC,CAAA;IAChB,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,WAAW,IAAI,SAAS,CAAA;IAC3D,MAAM,IAAI,KAAK,CACb,qBAAqB,KAAK,6CAA6C;QACrE,sCAAsC,KAAK,2BAA2B;QACtE,uCAAuC,CAC1C,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,YAAY,CAAI,GAAe,EAAE,KAAQ,EAAE,QAAsB;IAC/E,OAAO,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;AAC5C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,MAAM,UAAU,eAAe,CAAI,GAAe;IAChD,MAAM,QAAQ,GAAG,UAAU,CAAa,GAAG,CAAC,CAAA;IAC5C,+DAA+D;IAC/D,8CAA8C;IAC9C,OAAO,QAAQ,CAAC,SAAS,CAAC,CAAA;AAC5B,CAAC","sourcesContent":["import type { Scope } from '../types.js'\nimport { getRenderContext, setRenderContext, clearRenderContext } from '../render-context.js'\nimport { createScope } from '../scope.js'\n\n/**\n * Per-scope storage: scope → (context-id → accessor).\n * WeakMap so disposed scopes are GC'd.\n */\nconst contextMap = new WeakMap<Scope, Map<symbol, (s: unknown) => unknown>>()\n\nexport interface Context<T> {\n readonly _id: symbol\n readonly _default: T | undefined\n readonly _name: string | undefined\n}\n\n/**\n * Create a typed context key. Pass a default value to make consumers without a\n * provider resolve to it; omit to make unprovided consumption throw.\n *\n * ```ts\n * const ThemeContext = createContext<'light' | 'dark'>('light')\n * ```\n */\nexport function createContext<T>(defaultValue?: T, name?: string): Context<T> {\n return { _id: Symbol(name ?? 'llui-ctx'), _default: defaultValue, _name: name }\n}\n\n/**\n * Provide a reactive value for `ctx` to every descendant rendered inside `children`.\n * The accessor `(s: S) => T` is evaluated lazily at binding read time, so providers\n * can thread state slices down without prop drilling.\n *\n * ```ts\n * view: ({ send }) => [\n * provide(ThemeContext, (s: State) => s.theme, () => [\n * header(send),\n * main(send),\n * ]),\n * ]\n * ```\n *\n * Nested providers shadow outer ones within their subtree. The outer value is\n * restored after `children()` returns, so sibling subtrees aren't affected.\n */\nexport function provide<S, T>(\n ctx: Context<T>,\n accessor: (s: S) => T,\n children: () => Node[],\n): Node[] {\n const renderCtx = getRenderContext('provide')\n const parentScope = renderCtx.rootScope\n // Create a sub-scope so the context is attached to THIS provider alone.\n // Descendants (including those mounted later via show/branch/each) walk\n // up to this scope via their own parent chain and find the accessor.\n // Nested providers create their own sub-scope, shadowing outer values.\n const providerScope = createScope(parentScope)\n const map = new Map<symbol, (s: unknown) => unknown>()\n map.set(ctx._id, accessor as (s: unknown) => unknown)\n contextMap.set(providerScope, map)\n // Render children with the provider scope as the new rootScope so any\n // primitives (bindings, structural blocks, nested providers) attach here.\n setRenderContext({ ...renderCtx, rootScope: providerScope })\n try {\n return children()\n } finally {\n clearRenderContext()\n setRenderContext(renderCtx)\n }\n}\n\n/**\n * Read a context accessor within a view or view-function. Walks the scope chain\n * from the current render point to find the nearest provider. Returns an\n * `(s: S) => T` accessor that can be passed to bindings (text/class/etc.).\n *\n * ```ts\n * export function themedCard(): Node[] {\n * const theme = useContext(ThemeContext)\n * return div({ class: (s) => `card theme-${theme(s)}` }, [...])\n * }\n * ```\n */\nexport function useContext<S, T>(ctx: Context<T>): (s: S) => T {\n let scope: Scope | null = null\n try {\n scope = getRenderContext('useContext').rootScope\n } catch {\n // No render context (e.g. called from connect() in a unit test).\n // Fall through to default resolution below.\n }\n while (scope) {\n const map = contextMap.get(scope)\n if (map?.has(ctx._id)) {\n const accessor = map.get(ctx._id)!\n return accessor as (s: S) => T\n }\n scope = scope.parent\n }\n if (ctx._default !== undefined) {\n const d = ctx._default\n return () => d\n }\n const label = ctx._name ?? ctx._id.description ?? 'unknown'\n throw new Error(\n `[LLui] useContext(${label}): no provider found and no default value. ` +\n `Wrap a parent element with provide(${label}, accessor, () => [...]) ` +\n `or pass a default to createContext().`,\n )\n}\n\n/**\n * Provide a state-independent value to every descendant. Companion to\n * `provide()` for the common case of publishing a stable dispatcher\n * bag, callback record, or DI container — anything that doesn't depend\n * on the parent's state.\n *\n * ```ts\n * provideValue(ToastContext, { show: (m) => send({ type: 'toast', m }) }, () => [\n * main([pageSlot()]),\n * ])\n * ```\n *\n * Equivalent to `provide(ctx, () => value, children)`, but exists so\n * the call site reads as \"provide this value\" rather than \"provide an\n * accessor that ignores its state argument and returns a value.\" Pair\n * with `useContextValue` for symmetric ergonomics on the consumer side.\n *\n * Internally still uses the accessor mechanism: the value is wrapped\n * in a constant lambda. Consumers that read via the reactive\n * `useContext` form will get an `(s) => T` whose accessor ignores `s`.\n */\nexport function provideValue<T>(ctx: Context<T>, value: T, children: () => Node[]): Node[] {\n return provide(ctx, () => value, children)\n}\n\n/**\n * Read a state-independent value from the nearest provider. Companion\n * to `useContext()` for the common case of consuming a stable\n * dispatcher bag, callback record, or DI container.\n *\n * ```ts\n * const toast = useContextValue(ToastContext)\n * button({ onClick: () => toast.show('Saved') }, [text('Save')])\n * ```\n *\n * Equivalent to calling the accessor returned by `useContext` with\n * `undefined`, but reads as a single function call instead of a\n * three-step \"look up the accessor, ignore the state arg, get the\n * value\" dance.\n *\n * ## Value capture contract\n *\n * **The returned value is captured once, at view-construction time.**\n * Any reference you store from `useContextValue(ctx)` into a closure\n * — for example, by assigning it to a local `const` inside `view(...)`\n * and reading it from an event handler — sees the value as it was\n * when the view ran. The closure does NOT re-read the context on each\n * event dispatch.\n *\n * That's fine, and usually what you want, for stable dispatcher bags:\n * the bag's methods close over the layout's `send`, and `send` itself\n * is stable across the layout's lifetime, so the methods work\n * correctly regardless of when the handler fires. Pages can stash\n * `const toast = useContextValue(ToastContext)` at the top of their\n * `view()` and call `toast.show(...)` from any event handler below.\n *\n * **Do NOT use `useContextValue` when the consumer needs to see\n * updates to the context value.** If a parent re-`provideValue`s the\n * context with a different object later, existing consumers already\n * holding the captured value will still see the old one. For\n * reactive consumption, use `useContext(ctx)` — that returns an\n * accessor that re-reads the provider on each binding evaluation, so\n * reactive bindings (`class`, `text`, etc.) pick up updates\n * automatically.\n *\n * **Do NOT use `useContextValue` against a provider whose accessor\n * reads from state.** The accessor is invoked with `undefined` here,\n * so any `(s) => s.something` provider will throw or return garbage.\n * Match `provideValue` on the producer side with `useContextValue` on\n * the consumer side, and `provide` with `useContext`.\n */\nexport function useContextValue<T>(ctx: Context<T>): T {\n const accessor = useContext<unknown, T>(ctx)\n // The contract above: the producer side promised this accessor\n // doesn't read its state arg. Pass undefined.\n return accessor(undefined)\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"each.d.ts","sourceRoot":"","sources":["../../src/primitives/each.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,aAAa,CAAA;AAuBnE,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAEpD;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,GAAG,IAAI,CAEzE;AAgCD,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"each.d.ts","sourceRoot":"","sources":["../../src/primitives/each.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,aAAa,CAAA;AAuBnE,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAEpD;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,GAAG,IAAI,CAEzE;AAgCD,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAmR1E"}
|
package/dist/primitives/each.js
CHANGED
|
@@ -46,6 +46,58 @@ export function each(opts) {
|
|
|
46
46
|
const leaving = [];
|
|
47
47
|
const initialItems = opts.items(ctx.state);
|
|
48
48
|
let lastItemsRef = initialItems;
|
|
49
|
+
// Dev-only diff tracking: if the owning component has an _eachDiffLog
|
|
50
|
+
// (installed by devtools), we capture key sets before/after each
|
|
51
|
+
// key-mutating reconcile call and emit an EachDiff entry. The siteId
|
|
52
|
+
// is derived from this each() block's position in the flat block
|
|
53
|
+
// array at registration time — stable for the lifetime of the block.
|
|
54
|
+
const inst = ctx.instance;
|
|
55
|
+
const eachSiteId = inst?._eachDiffLog !== undefined ? `each#${blocks.length}` : '';
|
|
56
|
+
const snapshotKeys = () => {
|
|
57
|
+
if (inst?._eachDiffLog === undefined)
|
|
58
|
+
return null;
|
|
59
|
+
const keys = [];
|
|
60
|
+
for (let i = 0; i < entries.length; i++)
|
|
61
|
+
keys.push(String(entries[i].key));
|
|
62
|
+
return keys;
|
|
63
|
+
};
|
|
64
|
+
const emitDiff = (oldKeys) => {
|
|
65
|
+
if (oldKeys === null || inst?._eachDiffLog === undefined)
|
|
66
|
+
return;
|
|
67
|
+
const newKeys = [];
|
|
68
|
+
for (let i = 0; i < entries.length; i++)
|
|
69
|
+
newKeys.push(String(entries[i].key));
|
|
70
|
+
const oldKeySet = new Set(oldKeys);
|
|
71
|
+
const newKeySet = new Set(newKeys);
|
|
72
|
+
const added = [];
|
|
73
|
+
const removed = [];
|
|
74
|
+
const moved = [];
|
|
75
|
+
const reused = [];
|
|
76
|
+
for (const k of newKeys)
|
|
77
|
+
if (!oldKeySet.has(k))
|
|
78
|
+
added.push(k);
|
|
79
|
+
for (const k of oldKeys)
|
|
80
|
+
if (!newKeySet.has(k))
|
|
81
|
+
removed.push(k);
|
|
82
|
+
for (let i = 0; i < newKeys.length; i++) {
|
|
83
|
+
const k = newKeys[i];
|
|
84
|
+
if (!oldKeySet.has(k))
|
|
85
|
+
continue;
|
|
86
|
+
const from = oldKeys.indexOf(k);
|
|
87
|
+
if (from !== i)
|
|
88
|
+
moved.push({ key: k, from, to: i });
|
|
89
|
+
else
|
|
90
|
+
reused.push(k);
|
|
91
|
+
}
|
|
92
|
+
inst._eachDiffLog.push({
|
|
93
|
+
updateIndex: inst._updateCounter ?? 0,
|
|
94
|
+
eachSiteId,
|
|
95
|
+
added,
|
|
96
|
+
removed,
|
|
97
|
+
moved,
|
|
98
|
+
reused,
|
|
99
|
+
});
|
|
100
|
+
};
|
|
49
101
|
const block = {
|
|
50
102
|
mask: opts.__mask ?? FULL_MASK,
|
|
51
103
|
reconcile(state) {
|
|
@@ -57,11 +109,13 @@ export function each(opts) {
|
|
|
57
109
|
if (newItems === lastItemsRef)
|
|
58
110
|
return;
|
|
59
111
|
lastItemsRef = newItems;
|
|
112
|
+
const oldKeys = snapshotKeys();
|
|
60
113
|
const report = opts.onTransition ? { entering: [], leaving: [] } : null;
|
|
61
114
|
reconcileEntries(entries, newItems, opts, parentScope, parent, anchor, ctx, state, leaving, report);
|
|
62
115
|
if (opts.onTransition && report) {
|
|
63
116
|
opts.onTransition({ entering: report.entering, leaving: report.leaving, parent });
|
|
64
117
|
}
|
|
118
|
+
emitDiff(oldKeys);
|
|
65
119
|
},
|
|
66
120
|
/** Same keys, only item data changed — skip mismatch/swap detection.
|
|
67
121
|
* Compiler calls this when it knows the array structure is unchanged. */
|
|
@@ -85,6 +139,7 @@ export function each(opts) {
|
|
|
85
139
|
return;
|
|
86
140
|
if (entries.length === 0)
|
|
87
141
|
return;
|
|
142
|
+
const oldKeys = snapshotKeys();
|
|
88
143
|
// Call registered clear callbacks (e.g., selector registry.clear())
|
|
89
144
|
// BEFORE scope disposal — avoids 1000 individual Set.delete calls
|
|
90
145
|
for (let i = 0; i < clearCallbacks.length; i++)
|
|
@@ -99,11 +154,15 @@ export function each(opts) {
|
|
|
99
154
|
// Bulk scope disposal — disposers that were replaced by clearCallbacks
|
|
100
155
|
// are now no-ops, making this much faster
|
|
101
156
|
const scopes = [];
|
|
102
|
-
for (let i = 0; i < entries.length; i++)
|
|
103
|
-
|
|
157
|
+
for (let i = 0; i < entries.length; i++) {
|
|
158
|
+
const s = entries[i].scope;
|
|
159
|
+
s.disposalCause = 'each-remove';
|
|
160
|
+
scopes.push(s);
|
|
161
|
+
}
|
|
104
162
|
disposeScopesBulk(scopes);
|
|
105
163
|
removeOrphanedChildren(parentScope);
|
|
106
164
|
entries.length = 0;
|
|
165
|
+
emitDiff(oldKeys);
|
|
107
166
|
},
|
|
108
167
|
/** Remove entries not present in the new items. Optimized for filter()
|
|
109
168
|
* patterns where items are removed but order is preserved. Walks old
|
|
@@ -114,11 +173,13 @@ export function each(opts) {
|
|
|
114
173
|
const parent = anchor.parentNode;
|
|
115
174
|
if (!parent)
|
|
116
175
|
return;
|
|
176
|
+
const oldKeys = snapshotKeys();
|
|
117
177
|
const oldLen = entries.length;
|
|
118
178
|
const newLen = newItems.length;
|
|
119
179
|
if (newLen >= oldLen) {
|
|
120
180
|
// Not a removal — fallback (shouldn't happen if compiler detected correctly)
|
|
121
181
|
reconcileEntries(entries, newItems, opts, parentScope, parent, anchor, ctx, state, leaving, null);
|
|
182
|
+
emitDiff(oldKeys);
|
|
122
183
|
return;
|
|
123
184
|
}
|
|
124
185
|
// Parallel walk: new items are a subsequence of old items (same order, some removed)
|
|
@@ -139,6 +200,7 @@ export function each(opts) {
|
|
|
139
200
|
removeCallbacks[ci](entry.key);
|
|
140
201
|
for (const node of entry.nodes)
|
|
141
202
|
parent.removeChild(node);
|
|
203
|
+
entry.scope.disposalCause = 'each-remove';
|
|
142
204
|
disposeScope(entry.scope, true);
|
|
143
205
|
entries[oi] = null;
|
|
144
206
|
didRemove = true;
|
|
@@ -158,6 +220,7 @@ export function each(opts) {
|
|
|
158
220
|
for (let i = 0; i < entries.length; i++) {
|
|
159
221
|
entries[i].index = i;
|
|
160
222
|
}
|
|
223
|
+
emitDiff(oldKeys);
|
|
161
224
|
},
|
|
162
225
|
/** Update only entries at stride intervals — O(k) where k = n/stride.
|
|
163
226
|
* The compiler passes the stride from the detected for-loop pattern. */
|
|
@@ -234,6 +297,7 @@ function removeEntry(entry, opts, leaving) {
|
|
|
234
297
|
if (node.parentNode)
|
|
235
298
|
node.parentNode.removeChild(node);
|
|
236
299
|
}
|
|
300
|
+
entry.scope.disposalCause = 'each-remove';
|
|
237
301
|
disposeScope(entry.scope);
|
|
238
302
|
const idx = leaving.indexOf(entry);
|
|
239
303
|
if (idx !== -1)
|
|
@@ -260,6 +324,7 @@ function buildEntry(item, index, opts, parentScope, ctx, state) {
|
|
|
260
324
|
// Full scope features (disposers, bindings, children) are only needed when
|
|
261
325
|
// the render callback uses structural primitives or selector.bind().
|
|
262
326
|
const scope = createScope(parentScope);
|
|
327
|
+
scope._kind = 'each';
|
|
263
328
|
const currentState = (state ?? ctx.state);
|
|
264
329
|
const send = ctx.send;
|
|
265
330
|
// Create entry before render so itemAccessor closures can capture it
|
|
@@ -307,6 +372,7 @@ function buildEntry(item, index, opts, parentScope, ctx, state) {
|
|
|
307
372
|
buildCtx.state = currentState;
|
|
308
373
|
buildCtx.allBindings = ctx.allBindings;
|
|
309
374
|
buildCtx.structuralBlocks = ctx.structuralBlocks;
|
|
375
|
+
buildCtx.instance = ctx.instance;
|
|
310
376
|
const prevFlatBindings = getFlatBindings();
|
|
311
377
|
setFlatBindings(ctx.allBindings);
|
|
312
378
|
setRenderContext(buildCtx);
|
|
@@ -369,8 +435,11 @@ function reconcileEntries(entries, newItems, opts, parentScope, parent, anchor,
|
|
|
369
435
|
}
|
|
370
436
|
// Bulk dispose all entry scopes — avoids per-scope function call overhead
|
|
371
437
|
const scopes = [];
|
|
372
|
-
for (let i = 0; i < entries.length; i++)
|
|
373
|
-
|
|
438
|
+
for (let i = 0; i < entries.length; i++) {
|
|
439
|
+
const s = entries[i].scope;
|
|
440
|
+
s.disposalCause = 'each-remove';
|
|
441
|
+
scopes.push(s);
|
|
442
|
+
}
|
|
374
443
|
disposeScopesBulk(scopes);
|
|
375
444
|
removeOrphanedChildren(parentScope);
|
|
376
445
|
entries.length = 0;
|
|
@@ -479,8 +548,11 @@ function reconcileEntries(entries, newItems, opts, parentScope, parent, anchor,
|
|
|
479
548
|
range.deleteContents();
|
|
480
549
|
// Bulk dispose all old scopes
|
|
481
550
|
const oldScopes = [];
|
|
482
|
-
for (let i = 0; i < entries.length; i++)
|
|
483
|
-
|
|
551
|
+
for (let i = 0; i < entries.length; i++) {
|
|
552
|
+
const s = entries[i].scope;
|
|
553
|
+
s.disposalCause = 'each-remove';
|
|
554
|
+
oldScopes.push(s);
|
|
555
|
+
}
|
|
484
556
|
disposeScopesBulk(oldScopes);
|
|
485
557
|
removeOrphanedChildren(parentScope);
|
|
486
558
|
entries.length = 0;
|
|
@@ -541,6 +613,7 @@ function reconcileEntries(entries, newItems, opts, parentScope, parent, anchor,
|
|
|
541
613
|
else {
|
|
542
614
|
for (const node of entry.nodes)
|
|
543
615
|
parent.removeChild(node);
|
|
616
|
+
entry.scope.disposalCause = 'each-remove';
|
|
544
617
|
disposeScope(entry.scope, true);
|
|
545
618
|
didBulkDetach = true;
|
|
546
619
|
}
|