kerfjs 5.0.0-beta.8 → 5.0.0-beta.80
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/CHANGELOG.md +2577 -117
- package/README.md +96 -37
- package/ai/cursorrules +110 -61
- package/ai/manifest.json +16 -7
- package/ai/skill.md +120 -75
- package/dist/actions.d.ts +1 -1
- package/dist/actions.js +10 -6
- package/dist/actions.js.map +1 -1
- package/dist/array-signal.js +1 -1
- package/dist/async.js +22 -19
- package/dist/async.js.map +1 -1
- package/dist/attach.js +1 -66
- package/dist/attach.js.map +1 -1
- package/dist/{chunk-HW7KSM2Y.js → chunk-3C6F6NM4.js} +1 -1
- package/dist/{chunk-HW7KSM2Y.js.map → chunk-3C6F6NM4.js.map} +1 -1
- package/dist/{chunk-GY4XV2UV.js → chunk-4TX6YJDM.js} +10 -3
- package/dist/chunk-4TX6YJDM.js.map +1 -0
- package/dist/{chunk-QIP723L4.js → chunk-5WRGJZV6.js} +3 -2
- package/dist/chunk-5WRGJZV6.js.map +1 -0
- package/dist/{chunk-KZJXHFIB.js → chunk-BI4HGICV.js} +3 -3
- package/dist/chunk-BI4HGICV.js.map +1 -0
- package/dist/chunk-FM6N2B4N.js +70 -0
- package/dist/chunk-FM6N2B4N.js.map +1 -0
- package/dist/{chunk-LVH3GC6B.js → chunk-H7WTRM6F.js} +3 -3
- package/dist/chunk-H7WTRM6F.js.map +1 -0
- package/dist/{chunk-U6FK33SG.js → chunk-IHMDBBUJ.js} +2 -2
- package/dist/chunk-IHMDBBUJ.js.map +1 -0
- package/dist/chunk-JN7EJM3B.js +86 -0
- package/dist/chunk-JN7EJM3B.js.map +1 -0
- package/dist/{chunk-UZJ6I4T6.js → chunk-MK42GLPV.js} +1 -1
- package/dist/chunk-MK42GLPV.js.map +1 -0
- package/dist/{chunk-SVATPF5R.js → chunk-TLL3KXT6.js} +249 -160
- package/dist/chunk-TLL3KXT6.js.map +1 -0
- package/dist/{chunk-ZDCJZCNO.js → chunk-UGNPDLNI.js} +78 -45
- package/dist/chunk-UGNPDLNI.js.map +1 -0
- package/dist/{chunk-V46JKE44.js → chunk-WKIPLNVO.js} +1 -1
- package/dist/chunk-WKIPLNVO.js.map +1 -0
- package/dist/{delegate-CL9VTZFb.d.ts → delegate--CrfgYtC.d.ts} +0 -34
- package/dist/dev.d.ts +31 -13
- package/dist/dev.js +101 -35
- package/dist/dev.js.map +1 -1
- package/dist/html.js +5 -4
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +14 -9
- package/dist/index.js +29 -20
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.d.ts +13 -5
- package/dist/jsx-runtime.js +1 -1
- package/dist/list.d.ts +7 -3
- package/dist/list.js +126 -28
- package/dist/list.js.map +1 -1
- package/dist/overlay.d.ts +128 -129
- package/dist/overlay.js +675 -366
- package/dist/overlay.js.map +1 -1
- package/dist/remount.js +2 -2
- package/dist/router.d.ts +13 -4
- package/dist/router.js +37 -10
- package/dist/router.js.map +1 -1
- package/dist/scope.d.ts +35 -8
- package/dist/scope.js +61 -30
- package/dist/scope.js.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/timing.d.ts +3 -2
- package/dist/timing.js +1 -1
- package/dist/timing.js.map +1 -1
- package/llms.txt +12 -5
- package/package.json +33 -8
- package/setup/cli.mjs +90 -0
- package/setup/index.d.mts +37 -0
- package/setup/index.mjs +1347 -0
- package/setup/jsonc.mjs +201 -0
- package/setup/state.schema.json +56 -0
- package/dist/chunk-GY4XV2UV.js.map +0 -1
- package/dist/chunk-KPXIOG2C.js +0 -54
- package/dist/chunk-KPXIOG2C.js.map +0 -1
- package/dist/chunk-KZJXHFIB.js.map +0 -1
- package/dist/chunk-LVH3GC6B.js.map +0 -1
- package/dist/chunk-QIP723L4.js.map +0 -1
- package/dist/chunk-SVATPF5R.js.map +0 -1
- package/dist/chunk-U6FK33SG.js.map +0 -1
- package/dist/chunk-UZJ6I4T6.js.map +0 -1
- package/dist/chunk-V46JKE44.js.map +0 -1
- package/dist/chunk-ZDCJZCNO.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/attr.ts"],"names":[],"mappings":";AAoEA,SAAS,eAAe,KAAA,EAAuB;AAC7C,EAAA,IAAI,UAAU,EAAA,EAAI;AAChB,IAAA,MAAM,IAAI,MAAM,wCAAwC,CAAA;AAAA,EAC1D;AACA,EAAA,MAAM,GAAA,GAAM,OAAO,KAAK,CAAA;AACxB,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,GAAA,CAAI,QAAQ,CAAA,EAAA,EAAK;AACnC,IAAA,MAAM,EAAA,GAAK,GAAA,CAAI,UAAA,CAAW,CAAC,CAAA;AAC3B,IAAA,MAAM,EAAA,GAAK,GAAA,CAAI,MAAA,CAAO,CAAC,CAAA;AAGvB,IAAA,IAAI,OAAO,CAAA,EAAQ;AACjB,MAAA,MAAA,IAAU,QAAA;AACV,MAAA;AAAA,IACF;AAEA,IAAA,IAAK,EAAA,IAAM,CAAA,IAAU,EAAA,IAAM,EAAA,IAAW,OAAO,GAAA,EAAQ;AACnD,MAAA,MAAA,IAAU,IAAA,GAAO,EAAA,CAAG,QAAA,CAAS,EAAE,CAAA,GAAI,GAAA;AACnC,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAA,KAAM,CAAA,IAAK,EAAA,IAAM,EAAA,IAAU,MAAM,EAAA,EAAQ;AAC3C,MAAA,MAAA,IAAU,IAAA,GAAO,EAAA,CAAG,QAAA,CAAS,EAAE,CAAA,GAAI,GAAA;AACnC,MAAA;AAAA,IACF;AAEA,IAAA,IACE,CAAA,KAAM,CAAA,IACN,EAAA,IAAM,EAAA,IACN,EAAA,IAAM,MACN,GAAA,CAAI,UAAA,CAAW,CAAC,CAAA,KAAM,EAAA,EACtB;AACA,MAAA,MAAA,IAAU,IAAA,GAAO,EAAA,CAAG,QAAA,CAAS,EAAE,CAAA,GAAI,GAAA;AACnC,MAAA;AAAA,IACF;AAEA,IAAA,IACE,EAAA,IAAM,OACN,EAAA,KAAO,EAAA;AAAA,IACP,EAAA,KAAO,EAAA;AAAA,IACN,EAAA,IAAM,MAAU,EAAA,IAAM,EAAA;AAAA,IACtB,EAAA,IAAM,MAAU,EAAA,IAAM,EAAA;AAAA,IACtB,EAAA,IAAM,EAAA,IAAU,EAAA,IAAM,GAAA,EACvB;AACA,MAAA,MAAA,IAAU,EAAA;AACV,MAAA;AAAA,IACF;AAEA,IAAA,MAAA,IAAU,IAAA,GAAO,EAAA;AAAA,EACnB;AACA,EAAA,OAAO,MAAA;AACT;AAMA,SAAS,gBAAgB,KAAA,EAAuB;AAC9C,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,IAAA,MAAM,EAAA,GAAK,KAAA,CAAM,UAAA,CAAW,CAAC,CAAA;AAC7B,IAAA,MAAM,EAAA,GAAK,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA;AACzB,IAAA,IAAI,OAAO,CAAA,EAAQ;AACjB,MAAA,MAAA,IAAU,QAAA;AAAA,IACZ,WAAY,EAAA,IAAM,CAAA,IAAU,EAAA,IAAM,EAAA,IAAW,OAAO,GAAA,EAAQ;AAE1D,MAAA,MAAA,IAAU,IAAA,GAAO,EAAA,CAAG,QAAA,CAAS,EAAE,CAAA,GAAI,GAAA;AAAA,IACrC,CAAA,MAAA,IAAW,OAAO,EAAA,EAAQ;AAExB,MAAA,MAAA,IAAU,MAAA;AAAA,IACZ,CAAA,MAAA,IAAW,OAAO,EAAA,EAAQ;AAExB,MAAA,MAAA,IAAU,KAAA;AAAA,IACZ,CAAA,MAAO;AACL,MAAA,MAAA,IAAU,EAAA;AAAA,IACZ;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAuBO,SAAS,IAAA,CACd,MACA,KAAA,EACqE;AACrE,EAAA,MAAM,WAAA,GAAc,eAAe,IAAI,CAAA;AACvC,EAAA,IAAI,UAAU,MAAA,EAAW;AACvB,IAAA,MAAM,WAAW,CAAA,CAAA,EAAI,WAAW,CAAA,EAAA,EAAK,eAAA,CAAgB,KAAK,CAAC,CAAA,EAAA,CAAA;AAC3D,IAAA,OAAO,OAAO,MAAA,CAAO;AAAA,MACnB,IAAA;AAAA,MACA,KAAA;AAAA,MACA,QAAA;AAAA,MACA,KAAA,EAAO,OAAO,MAAA,CAAO,EAAE,CAAC,IAAI,GAAG,OAAO;AAAA,KACvC,CAAA;AAAA,EACH;AACA,EAAA,OAAO,CAAC,MACN,MAAA,CAAO,MAAA,CAAO,EAAE,CAAC,IAAI,GAAG,CAAA,EAAG,CAAA;AAC/B","file":"chunk-MK42GLPV.js","sourcesContent":["/**\n * `attr(name, value)` — create a pre-computed attribute descriptor (static form).\n * `attr(name)` — create a per-render factory for dynamic attribute values (dynamic form).\n *\n * **Static form** — best for fixed action names, filter keys, role values, etc.\n * Escapes once at module-load time; produces a full {@link AttrSpec} with\n * `.name`, `.value`, `.selector`, and `.attrs`.\n *\n * const ACTIONS = {\n * toggle: attr('data-action', 'toggle'),\n * remove: attr('data-action', 'remove'),\n * } as const satisfies Record<string, AttrSpec<'data-action'>>;\n *\n * // In JSX — spread .attrs (rename-safe; no hardcoded attribute name):\n * <button {...ACTIONS.toggle.attrs}>Toggle</button>\n *\n * // In delegate — use the pre-computed selector:\n * delegate(root, 'click', ACTIONS.toggle.selector, handler);\n *\n * **Dynamic form** — best for per-row data like `data-id`, where the value\n * changes per item but the attribute name is constant.\n * The name is validated and pre-escaped at definition time; calling the\n * returned factory is cheap (it just freezes a one-key object — the value is\n * escaped later by the JSX attribute renderer when the result is spread).\n *\n * const ITEM = { id: attr('data-id') } as const;\n *\n * // In JSX — call the factory inline:\n * <li {...ITEM.id(String(item.id))}>…</li>\n *\n * For ad-hoc compound selectors, concatenate `.selector` strings:\n *\n * delegate(root, 'click',\n * ACTIONS.toggle.selector + attr('data-id', id).selector,\n * handler);\n *\n * Escaping:\n * - Attribute name: escaped as a CSS identifier via `cssEscapeIdent`, which is\n * an SSR-safe (no `CSS.escape`) adaptation of the Mathias Bynens polyfill\n * (https://github.com/mathiasbynens/CSS.escape, MIT licensed — see the\n * Acknowledgements section of LICENSE). Handles\n * control chars, leading digits, non-ASCII, and CSS metacharacters.\n * - Attribute value: embedded in double quotes as a CSS string. Backslashes and\n * double-quote characters are backslash-escaped; control characters are\n * hex-escaped per CSS Syntax Level 3 §3.4.\n *\n * Throws on an empty attribute name (not a valid CSS identifier).\n */\n\n/** Descriptor created by the static {@link attr} overload. */\nexport interface AttrSpec<\n N extends string = string,\n V extends string = string,\n> {\n /** The raw attribute name passed to `attr()`. */\n readonly name: N;\n /** The raw attribute value passed to `attr()`. */\n readonly value: V;\n /** Pre-computed `[name=\"value\"]` CSS selector string, safe to pass to `delegate()`. */\n readonly selector: string;\n /** Spreadable JSX object — `{ [name]: value }` — keeps the attribute name out of JSX literals. */\n readonly attrs: { readonly [K in N]: V };\n}\n\n/**\n * Escape `value` as a CSS identifier (for attribute names, id fragments, etc.).\n * Adapted from the CSS.escape polyfill by Mathias Bynens (MIT).\n */\nfunction cssEscapeIdent(value: string): string {\n if (value === '') {\n throw new Error('attr: attribute name must not be empty');\n }\n const str = String(value);\n let result = '';\n for (let i = 0; i < str.length; i++) {\n const cp = str.charCodeAt(i);\n const ch = str.charAt(i);\n\n // U+0000 NULL → replacement character\n if (cp === 0x0000) {\n result += '�';\n continue;\n }\n // Control characters and DEL: hex-escape\n if ((cp >= 0x0001 && cp <= 0x001f) || cp === 0x007f) {\n result += '\\\\' + cp.toString(16) + ' ';\n continue;\n }\n // Leading digit: hex-escape to avoid \"-NN\" / \"3px\"-style ambiguity\n if (i === 0 && cp >= 0x0030 && cp <= 0x0039) {\n result += '\\\\' + cp.toString(16) + ' ';\n continue;\n }\n // Second char is a digit when first is '-' (e.g. \"-3foo\"): hex-escape digit\n if (\n i === 1 &&\n cp >= 0x0030 &&\n cp <= 0x0039 &&\n str.charCodeAt(0) === 0x002d\n ) {\n result += '\\\\' + cp.toString(16) + ' ';\n continue;\n }\n // Non-ASCII, safe identifier chars (letters, digits, underscore, hyphen)\n if (\n cp >= 0x0080 ||\n cp === 0x002d || // `-`\n cp === 0x005f || // `_`\n (cp >= 0x0030 && cp <= 0x0039) || // 0-9\n (cp >= 0x0041 && cp <= 0x005a) || // A-Z\n (cp >= 0x0061 && cp <= 0x007a) // a-z\n ) {\n result += ch;\n continue;\n }\n // Everything else: backslash-escape\n result += '\\\\' + ch;\n }\n return result;\n}\n\n/**\n * Escape `value` as a CSS double-quoted string (for attribute values in\n * `[attr=\"value\"]` selectors).\n */\nfunction escapeCSSString(value: string): string {\n let result = '';\n for (let i = 0; i < value.length; i++) {\n const cp = value.charCodeAt(i);\n const ch = value.charAt(i);\n if (cp === 0x0000) {\n result += '�';\n } else if ((cp >= 0x0001 && cp <= 0x001f) || cp === 0x007f) {\n // Control chars: hex-escape\n result += '\\\\' + cp.toString(16) + ' ';\n } else if (cp === 0x005c) {\n // Backslash\n result += '\\\\\\\\';\n } else if (cp === 0x0022) {\n // Double quote (the string delimiter we use)\n result += '\\\\\"';\n } else {\n result += ch;\n }\n }\n return result;\n}\n\n/**\n * Static overload — pre-computes the full descriptor at definition time.\n * Returns an {@link AttrSpec} with `.name`, `.value`, `.selector`, and `.attrs`.\n */\nexport function attr<N extends string, V extends string>(\n name: N,\n value: V,\n): AttrSpec<N, V>;\n\n/**\n * Dynamic overload — pre-validates and pre-escapes the attribute name, returns a\n * factory that accepts a per-render value and produces a frozen spreadable object.\n * Use for per-row attributes like `data-id` where the value changes per item.\n * The optional `V` generic constrains which values the factory accepts:\n * `attr<'data-id', 'a'|'b'>('data-id')` → `(value: 'a'|'b') => { 'data-id': 'a'|'b' }`.\n * Leaving both generics off infers `N` from the argument and defaults `V` to `string`.\n */\nexport function attr<N extends string, V extends string = string>(\n name: N,\n): (value: V) => { readonly [K in N]: V };\n\nexport function attr<N extends string, V extends string>(\n name: N,\n value?: V,\n): AttrSpec<N, V> | ((value: string) => { readonly [K in N]: string }) {\n const escapedName = cssEscapeIdent(name); // validates + pre-escapes name in both paths\n if (value !== undefined) {\n const selector = `[${escapedName}=\"${escapeCSSString(value)}\"]`;\n return Object.freeze({\n name,\n value,\n selector,\n attrs: Object.freeze({ [name]: value }) as { readonly [K in N]: V },\n }) as AttrSpec<N, V>;\n }\n return (v: string): { readonly [K in N]: string } =>\n Object.freeze({ [name]: v }) as { readonly [K in N]: string };\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { itemVersion } from './chunk-
|
|
2
|
-
import { parseRowTemplate, rowContractError, parseSingleRow, collectTemplateChildren } from './chunk-
|
|
3
|
-
import { captureRowBindings, listSafeHtml, boundTextNodeOf, syncFormProp, newBindingContext,
|
|
4
|
-
import { effect } from './chunk-
|
|
5
|
-
import { LIST_MARKER_PREFIX, flattenWithoutListItems, collectLists, flatten } from './chunk-
|
|
6
|
-
import { devHooks } from './chunk-
|
|
1
|
+
import { itemVersion } from './chunk-5WRGJZV6.js';
|
|
2
|
+
import { parseRowTemplate, rowContractError, parseSingleRow, collectTemplateChildren } from './chunk-WKIPLNVO.js';
|
|
3
|
+
import { captureRowBindings, listSafeHtml, boundTextNodeOf, syncFormProp, newBindingContext, wireBindings, disposeRowBindings, isSafeHtml, _setBindingContext, TEXT_MARKER_PREFIX, ROW_TEXT_PREFIX, wireRowBindings, carryOrRewireRowBindings, granularListSafeHtml } from './chunk-UGNPDLNI.js';
|
|
4
|
+
import { effect } from './chunk-IHMDBBUJ.js';
|
|
5
|
+
import { LIST_MARKER_PREFIX, flattenWithoutListItems, collectLists, isOverlayHost, flatten } from './chunk-4TX6YJDM.js';
|
|
6
|
+
import { devHooks } from './chunk-3C6F6NM4.js';
|
|
7
7
|
|
|
8
8
|
// src/list-render-state.ts
|
|
9
9
|
function deriveListRenderState(bindingCount) {
|
|
@@ -18,7 +18,8 @@ function decideListPath(state, patches, snapshotLength, previousBindingCount) {
|
|
|
18
18
|
for (const p of patches) {
|
|
19
19
|
if (p.type === "insert") netDelta += 1;
|
|
20
20
|
else if (p.type === "remove") netDelta -= 1;
|
|
21
|
-
else if (p.type === "replace")
|
|
21
|
+
else if (p.type === "replace")
|
|
22
|
+
return { path: "snapshot", reason: "replace" };
|
|
22
23
|
}
|
|
23
24
|
const count = previousBindingCount ?? 0;
|
|
24
25
|
if (count + netDelta !== snapshotLength) {
|
|
@@ -153,10 +154,12 @@ function eachGranular(sig, render, cacheKey, listKey) {
|
|
|
153
154
|
return eachSnapshotById(snapshot, render, cacheKey, id, sig);
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
|
-
const renderRow = (item, index) => captureRowBindings(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
const renderRow = (item, index) => captureRowBindings(
|
|
158
|
+
() => inRowScope(() => {
|
|
159
|
+
const out = render(item, index);
|
|
160
|
+
return isSafeHtml(out) ? out.toString() : out;
|
|
161
|
+
})
|
|
162
|
+
);
|
|
160
163
|
const internalPatches = new Array(patches.length);
|
|
161
164
|
const cache = ctx.caches.get(id);
|
|
162
165
|
try {
|
|
@@ -223,13 +226,16 @@ function eachSnapshotById(items, render, cacheKey, id, source) {
|
|
|
223
226
|
devHooks.staleIndex?.(id);
|
|
224
227
|
}
|
|
225
228
|
} else {
|
|
226
|
-
const captured = captureRowBindings(
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
229
|
+
const captured = captureRowBindings(
|
|
230
|
+
() => inRowScope(() => {
|
|
231
|
+
const out = render(item, i);
|
|
232
|
+
return isSafeHtml(out) ? out.toString() : out;
|
|
233
|
+
})
|
|
234
|
+
);
|
|
230
235
|
html = captured.html;
|
|
231
236
|
bindings = captured.bindings;
|
|
232
|
-
if (cache !== null)
|
|
237
|
+
if (cache !== null)
|
|
238
|
+
cache.set(item, { cacheKey: k, html, bindings, version, index: i });
|
|
233
239
|
}
|
|
234
240
|
segItems[i] = { ref: item, cacheKey: k, html, bindings };
|
|
235
241
|
}
|
|
@@ -281,16 +287,19 @@ function restoreFocus(snap) {
|
|
|
281
287
|
if (domSelection === null) return;
|
|
282
288
|
const [anchorNode, anchorOffset, focusNode, focusOffset] = domSelection;
|
|
283
289
|
try {
|
|
284
|
-
document.getSelection()?.setBaseAndExtent(
|
|
285
|
-
anchorNode,
|
|
286
|
-
anchorOffset,
|
|
287
|
-
focusNode,
|
|
288
|
-
focusOffset
|
|
289
|
-
);
|
|
290
|
+
document.getSelection()?.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
|
|
290
291
|
} catch {
|
|
291
292
|
}
|
|
292
293
|
}
|
|
293
294
|
|
|
295
|
+
// src/utils/isUserAgentOwnedAttr.ts
|
|
296
|
+
function isUserAgentOwnedAttr(tagNameUpper, name) {
|
|
297
|
+
if (tagNameUpper.includes("-")) {
|
|
298
|
+
return name === "open" || name === "role" || name.startsWith("aria-");
|
|
299
|
+
}
|
|
300
|
+
return name === "open" && (tagNameUpper === "DETAILS" || tagNameUpper === "DIALOG");
|
|
301
|
+
}
|
|
302
|
+
|
|
294
303
|
// src/utils/moveNode.ts
|
|
295
304
|
function moveNode(parent, node, ref) {
|
|
296
305
|
const move = parent.moveBefore;
|
|
@@ -343,7 +352,11 @@ function protectionTag(node) {
|
|
|
343
352
|
const { dataset } = node;
|
|
344
353
|
return (dataset.morphSkip !== void 0 ? "s" : "") + (dataset.morphSkipChildren !== void 0 ? "c" : "") + (dataset.morphPreserve !== void 0 ? "p" : "");
|
|
345
354
|
}
|
|
346
|
-
var MARKER_PREFIXES = [
|
|
355
|
+
var MARKER_PREFIXES = [
|
|
356
|
+
LIST_MARKER_PREFIX,
|
|
357
|
+
TEXT_MARKER_PREFIX,
|
|
358
|
+
ROW_TEXT_PREFIX
|
|
359
|
+
];
|
|
347
360
|
function isMarker(node) {
|
|
348
361
|
if (node.nodeType !== COMMENT_NODE) return false;
|
|
349
362
|
const { data } = node;
|
|
@@ -421,7 +434,8 @@ function morphChildren(fromParent, toParent, ownedItems) {
|
|
|
421
434
|
if (matched === null && fromChild !== null && toChild.nodeType === COMMENT_NODE && toChild.data.startsWith(LIST_MARKER_PREFIX)) {
|
|
422
435
|
const wantData = toChild.data;
|
|
423
436
|
for (let scan = fromChild.nextSibling; scan !== null; scan = scan.nextSibling) {
|
|
424
|
-
if (scan.nodeType !== COMMENT_NODE || scan.data !== wantData)
|
|
437
|
+
if (scan.nodeType !== COMMENT_NODE || scan.data !== wantData)
|
|
438
|
+
continue;
|
|
425
439
|
const regionEnd = afterListRegion(scan, ownedItems);
|
|
426
440
|
const run = [];
|
|
427
441
|
for (let r = scan; r !== null && r !== regionEnd; r = r.nextSibling) {
|
|
@@ -487,9 +501,6 @@ function morphElement(fromEl, toEl, ownedItems) {
|
|
|
487
501
|
fromEl.value = toEl.textContent;
|
|
488
502
|
}
|
|
489
503
|
}
|
|
490
|
-
function isUserAgentOwnedAttr(tagName, name) {
|
|
491
|
-
return name === "open" && (tagName === "DETAILS" || tagName === "DIALOG");
|
|
492
|
-
}
|
|
493
504
|
function morphAttributes(fromEl, toEl) {
|
|
494
505
|
const toAttrs = toEl.attributes;
|
|
495
506
|
for (let i = 0; i < toAttrs.length; i++) {
|
|
@@ -534,7 +545,10 @@ function preserveTextEntryState(fromEl, toEl) {
|
|
|
534
545
|
const toInput = toEl;
|
|
535
546
|
toInput.value = fromInput.value;
|
|
536
547
|
try {
|
|
537
|
-
toInput.setSelectionRange(
|
|
548
|
+
toInput.setSelectionRange(
|
|
549
|
+
fromInput.selectionStart,
|
|
550
|
+
fromInput.selectionEnd
|
|
551
|
+
);
|
|
538
552
|
} catch {
|
|
539
553
|
}
|
|
540
554
|
}
|
|
@@ -567,7 +581,8 @@ function tryAttributeOnlyFastPath(liveNode, oldHtml, newHtml) {
|
|
|
567
581
|
if (oldGt === -1 || newGt === -1) return false;
|
|
568
582
|
if (oldHtml.length - oldGt !== newHtml.length - newGt) return false;
|
|
569
583
|
if (oldHtml.slice(oldGt) !== newHtml.slice(newGt)) return false;
|
|
570
|
-
if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml))
|
|
584
|
+
if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml))
|
|
585
|
+
return false;
|
|
571
586
|
const oldTag = parseOpeningTag(oldHtml, oldGt);
|
|
572
587
|
const newTag = parseOpeningTag(newHtml, newGt);
|
|
573
588
|
if (oldTag === null || newTag === null) return false;
|
|
@@ -588,14 +603,15 @@ function tryAttributeOnlyFastPath(liveNode, oldHtml, newHtml) {
|
|
|
588
603
|
}
|
|
589
604
|
for (const name of oldTag.attrs.keys()) {
|
|
590
605
|
if (newTag.attrs.has(name)) continue;
|
|
591
|
-
if (
|
|
606
|
+
if (isUserAgentOwnedAttr(liveTagUpper, name)) continue;
|
|
592
607
|
liveNode.removeAttribute(name);
|
|
593
608
|
syncFormProp(liveNode, name, "", false);
|
|
594
609
|
}
|
|
595
610
|
return true;
|
|
596
611
|
}
|
|
597
612
|
function tryTextContentFastPath(liveNode, oldHtml, newHtml) {
|
|
598
|
-
if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml))
|
|
613
|
+
if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml))
|
|
614
|
+
return false;
|
|
599
615
|
let p = 0;
|
|
600
616
|
const minLen = Math.min(oldHtml.length, newHtml.length);
|
|
601
617
|
while (p < minLen && oldHtml.charCodeAt(p) === newHtml.charCodeAt(p)) p++;
|
|
@@ -610,7 +626,8 @@ function tryTextContentFastPath(liveNode, oldHtml, newHtml) {
|
|
|
610
626
|
if (!isPureTextWindow(newHtml, p, newWinEnd)) return false;
|
|
611
627
|
if (p === 0) return false;
|
|
612
628
|
const boundaryCc = oldHtml.charCodeAt(p - 1);
|
|
613
|
-
if (boundaryCc === LT || boundaryCc === DQUOTE || boundaryCc === SQUOTE || boundaryCc === EQ || boundaryCc === AMP)
|
|
629
|
+
if (boundaryCc === LT || boundaryCc === DQUOTE || boundaryCc === SQUOTE || boundaryCc === EQ || boundaryCc === AMP)
|
|
630
|
+
return false;
|
|
614
631
|
const textStart = lastIndexOfChar(oldHtml, GT, p - 1);
|
|
615
632
|
if (textStart === -1) return false;
|
|
616
633
|
const textEnd = oldHtml.indexOf("<", p);
|
|
@@ -637,7 +654,8 @@ function containsDataMorphSkip(html) {
|
|
|
637
654
|
function isPureTextWindow(html, start, end) {
|
|
638
655
|
for (let i = start; i < end; i++) {
|
|
639
656
|
const cc = html.charCodeAt(i);
|
|
640
|
-
if (cc === LT || cc === GT || cc === DQUOTE || cc === SQUOTE || cc === AMP || cc === EQ)
|
|
657
|
+
if (cc === LT || cc === GT || cc === DQUOTE || cc === SQUOTE || cc === AMP || cc === EQ)
|
|
658
|
+
return false;
|
|
641
659
|
}
|
|
642
660
|
return true;
|
|
643
661
|
}
|
|
@@ -730,9 +748,6 @@ function unescapeAttrValue(s) {
|
|
|
730
748
|
if (s.indexOf("&") === -1) return s;
|
|
731
749
|
return s.replace(/"/g, '"').replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
732
750
|
}
|
|
733
|
-
function isUserAgentOwnedAttr2(tagNameUpper, name) {
|
|
734
|
-
return name === "open" && (tagNameUpper === "DETAILS" || tagNameUpper === "DIALOG");
|
|
735
|
-
}
|
|
736
751
|
|
|
737
752
|
// src/list-reconcile-granular.ts
|
|
738
753
|
function reconcileGranular(binding, patches) {
|
|
@@ -769,7 +784,14 @@ function reconcileGranular(binding, patches) {
|
|
|
769
784
|
if (runLen === 1) {
|
|
770
785
|
applySingleInsert(liveParent, items, patch, endAnchor(binding));
|
|
771
786
|
} else {
|
|
772
|
-
applyBulkInsert(
|
|
787
|
+
applyBulkInsert(
|
|
788
|
+
liveParent,
|
|
789
|
+
items,
|
|
790
|
+
patches,
|
|
791
|
+
i,
|
|
792
|
+
runEnd,
|
|
793
|
+
endAnchor(binding)
|
|
794
|
+
);
|
|
773
795
|
}
|
|
774
796
|
i = runEnd;
|
|
775
797
|
continue;
|
|
@@ -881,7 +903,10 @@ function applyBulkUpdate(liveParent, items, patches, start, end) {
|
|
|
881
903
|
morphChanges.push({ patchIdx: k, html: p.html });
|
|
882
904
|
}
|
|
883
905
|
if (morphChanges.length === 0) return;
|
|
884
|
-
const { content, count } = parseRowTemplate(
|
|
906
|
+
const { content, count } = parseRowTemplate(
|
|
907
|
+
morphChanges.map((c) => c.html).join(""),
|
|
908
|
+
liveParent
|
|
909
|
+
);
|
|
885
910
|
if (count !== morphChanges.length) {
|
|
886
911
|
throw findOffendingChange(patches, morphChanges, liveParent);
|
|
887
912
|
}
|
|
@@ -924,18 +949,30 @@ function applyBulkInsert(liveParent, items, patches, start, end, tailAnchor) {
|
|
|
924
949
|
function findOffendingInsert(patches, start, htmls, liveParent) {
|
|
925
950
|
for (let i = 0; i < htmls.length; i++) {
|
|
926
951
|
if (parseRowTemplate(htmls[i], liveParent).count !== 1) {
|
|
927
|
-
return rowContractError(
|
|
952
|
+
return rowContractError(
|
|
953
|
+
patches[start + i].index,
|
|
954
|
+
htmls[i],
|
|
955
|
+
liveParent
|
|
956
|
+
);
|
|
928
957
|
}
|
|
929
958
|
}
|
|
930
|
-
return new Error(
|
|
959
|
+
return new Error(
|
|
960
|
+
"each(): bulk-insert mismatch with no per-row offender (kerf bug)."
|
|
961
|
+
);
|
|
931
962
|
}
|
|
932
963
|
function findOffendingChange(patches, changes, liveParent) {
|
|
933
964
|
for (const c of changes) {
|
|
934
965
|
if (parseRowTemplate(c.html, liveParent).count !== 1) {
|
|
935
|
-
return rowContractError(
|
|
966
|
+
return rowContractError(
|
|
967
|
+
patches[c.patchIdx].index,
|
|
968
|
+
c.html,
|
|
969
|
+
liveParent
|
|
970
|
+
);
|
|
936
971
|
}
|
|
937
972
|
}
|
|
938
|
-
return new Error(
|
|
973
|
+
return new Error(
|
|
974
|
+
"each(): bulk-update mismatch with no per-row offender (kerf bug)."
|
|
975
|
+
);
|
|
939
976
|
}
|
|
940
977
|
|
|
941
978
|
// src/list-reconcile-inplace.ts
|
|
@@ -960,7 +997,12 @@ function tryInPlaceContentUpdate(binding, listSeg) {
|
|
|
960
997
|
}
|
|
961
998
|
function updateRowInPlace(liveParent, old, ni, index) {
|
|
962
999
|
if (old.html === ni.html || tryAttributeOnlyFastPath(old.node, old.html, ni.html) || tryTextContentFastPath(old.node, old.html, ni.html)) {
|
|
963
|
-
const kept = carryOrRewireRowBindings(
|
|
1000
|
+
const kept = carryOrRewireRowBindings(
|
|
1001
|
+
old.node,
|
|
1002
|
+
old.bindings,
|
|
1003
|
+
old.bindingDisposers,
|
|
1004
|
+
ni.bindings
|
|
1005
|
+
);
|
|
964
1006
|
return {
|
|
965
1007
|
ref: ni.ref,
|
|
966
1008
|
cacheKey: ni.cacheKey,
|
|
@@ -973,7 +1015,12 @@ function updateRowInPlace(liveParent, old, ni, index) {
|
|
|
973
1015
|
const newNode = parseSingleRow(ni.html, index, liveParent);
|
|
974
1016
|
if (old.node.tagName === newNode.tagName) {
|
|
975
1017
|
_morphElement(old.node, newNode);
|
|
976
|
-
const kept = carryOrRewireRowBindings(
|
|
1018
|
+
const kept = carryOrRewireRowBindings(
|
|
1019
|
+
old.node,
|
|
1020
|
+
old.bindings,
|
|
1021
|
+
old.bindingDisposers,
|
|
1022
|
+
ni.bindings
|
|
1023
|
+
);
|
|
977
1024
|
return {
|
|
978
1025
|
ref: ni.ref,
|
|
979
1026
|
cacheKey: ni.cacheKey,
|
|
@@ -985,7 +1032,12 @@ function updateRowInPlace(liveParent, old, ni, index) {
|
|
|
985
1032
|
}
|
|
986
1033
|
disposeRowBindings(old.bindingDisposers);
|
|
987
1034
|
liveParent.replaceChild(newNode, old.node);
|
|
988
|
-
const fresh = carryOrRewireRowBindings(
|
|
1035
|
+
const fresh = carryOrRewireRowBindings(
|
|
1036
|
+
newNode,
|
|
1037
|
+
void 0,
|
|
1038
|
+
void 0,
|
|
1039
|
+
ni.bindings
|
|
1040
|
+
);
|
|
989
1041
|
return {
|
|
990
1042
|
ref: ni.ref,
|
|
991
1043
|
cacheKey: ni.cacheKey,
|
|
@@ -1070,15 +1122,22 @@ function buildFreshNodes(newRecord, freshIndices, freshHtmls, liveParent) {
|
|
|
1070
1122
|
function findOffendingRow(newRecord, freshIndices, freshHtmls, liveParent) {
|
|
1071
1123
|
for (let i = 0; i < freshHtmls.length; i++) {
|
|
1072
1124
|
if (parseRowTemplate(freshHtmls[i], liveParent).count !== 1) {
|
|
1073
|
-
return rowContractError(
|
|
1125
|
+
return rowContractError(
|
|
1126
|
+
freshIndices[i],
|
|
1127
|
+
newRecord[freshIndices[i]].html,
|
|
1128
|
+
liveParent
|
|
1129
|
+
);
|
|
1074
1130
|
}
|
|
1075
1131
|
}
|
|
1076
|
-
return new Error(
|
|
1132
|
+
return new Error(
|
|
1133
|
+
"each(): bulk-parse mismatch with no per-row offender (kerf bug)."
|
|
1134
|
+
);
|
|
1077
1135
|
}
|
|
1078
1136
|
function removeOldNodes(liveParent, removedItems) {
|
|
1079
1137
|
for (const item of removedItems) {
|
|
1080
1138
|
disposeRowBindings(item.bindingDisposers);
|
|
1081
|
-
if (item.node.parentElement === liveParent)
|
|
1139
|
+
if (item.node.parentElement === liveParent)
|
|
1140
|
+
liveParent.removeChild(item.node);
|
|
1082
1141
|
}
|
|
1083
1142
|
}
|
|
1084
1143
|
function applyMoves(liveParent, newRecord, prevIdx, stable, tailAnchor) {
|
|
@@ -1130,6 +1189,111 @@ function reconcileList(binding, listSeg) {
|
|
|
1130
1189
|
reconcileSnapshot(binding, listSeg);
|
|
1131
1190
|
}
|
|
1132
1191
|
|
|
1192
|
+
// src/mount-list-bindings.ts
|
|
1193
|
+
function anyRebuiltListIsGranular(segment, rebuilt) {
|
|
1194
|
+
if (!rebuilt.size) return false;
|
|
1195
|
+
const lists = collectLists(segment);
|
|
1196
|
+
for (const id of rebuilt) {
|
|
1197
|
+
if (lists.get(id)?.patches) return true;
|
|
1198
|
+
}
|
|
1199
|
+
return false;
|
|
1200
|
+
}
|
|
1201
|
+
function bindListsFromMarkers(rootEl, segment, bindings, inlinedItems, rebuiltLists) {
|
|
1202
|
+
const lists = collectLists(segment);
|
|
1203
|
+
const found = [];
|
|
1204
|
+
collectComments(rootEl, found);
|
|
1205
|
+
for (const marker of found) {
|
|
1206
|
+
if (!marker.data.startsWith(LIST_MARKER_PREFIX)) continue;
|
|
1207
|
+
const id = marker.data.slice(LIST_MARKER_PREFIX.length);
|
|
1208
|
+
const existing = bindings.get(id);
|
|
1209
|
+
if (existing) {
|
|
1210
|
+
if (existing.marker === marker && rootEl.contains(existing.marker))
|
|
1211
|
+
continue;
|
|
1212
|
+
for (const item of existing.items) {
|
|
1213
|
+
disposeRowBindings(item.bindingDisposers);
|
|
1214
|
+
if (rootEl.contains(item.node)) {
|
|
1215
|
+
item.node.parentElement?.removeChild(item.node);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
bindings.delete(id);
|
|
1219
|
+
rebuiltLists?.add(id);
|
|
1220
|
+
devHooks.listRebind?.(id, marker.parentElement);
|
|
1221
|
+
}
|
|
1222
|
+
const listSeg = lists.get(id);
|
|
1223
|
+
const liveParent = marker.parentElement;
|
|
1224
|
+
const items = [];
|
|
1225
|
+
if (inlinedItems) {
|
|
1226
|
+
let next = marker.nextElementSibling;
|
|
1227
|
+
for (let i = 0; i < listSeg.items.length && next !== null; i++) {
|
|
1228
|
+
validateInlinedRowMatch(listSeg.items[i].html, i, next, liveParent);
|
|
1229
|
+
items.push({
|
|
1230
|
+
ref: listSeg.items[i].ref,
|
|
1231
|
+
cacheKey: listSeg.items[i].cacheKey,
|
|
1232
|
+
html: listSeg.items[i].html,
|
|
1233
|
+
node: next,
|
|
1234
|
+
bindings: listSeg.items[i].bindings
|
|
1235
|
+
});
|
|
1236
|
+
next = next.nextElementSibling;
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
const binding = { liveParent, items, marker };
|
|
1240
|
+
if (items.length > 0) {
|
|
1241
|
+
devHooks.missingRowKey?.(items[0].node, items[0].html, binding);
|
|
1242
|
+
}
|
|
1243
|
+
devHooks.eachInMorphSkip?.(id, liveParent, rootEl);
|
|
1244
|
+
bindings.set(id, binding);
|
|
1245
|
+
for (const bound of items) {
|
|
1246
|
+
if (bound.bindings?.length) {
|
|
1247
|
+
bound.bindingDisposers = wireRowBindings(bound.node, bound.bindings);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
function validateInlinedRowMatch(expectedHtml, index, boundEl, liveParent) {
|
|
1253
|
+
if (boundEl.outerHTML === expectedHtml) return;
|
|
1254
|
+
const { content, count } = parseRowTemplate(expectedHtml, liveParent);
|
|
1255
|
+
if (count !== 1) throw rowContractError(index, expectedHtml, liveParent);
|
|
1256
|
+
const expectedTag = content.firstElementChild.tagName;
|
|
1257
|
+
if (boundEl.tagName !== expectedTag)
|
|
1258
|
+
throw rowStructureError(index, boundEl.tagName, expectedTag);
|
|
1259
|
+
}
|
|
1260
|
+
function rowStructureError(index, gotTag, wantTag) {
|
|
1261
|
+
const got = gotTag.toLowerCase();
|
|
1262
|
+
const want = wantTag.toLowerCase();
|
|
1263
|
+
return new Error(
|
|
1264
|
+
`each(): row ${index} renders <${want}>, but the HTML parser wrapped the rows in <${got}> \u2014 so kerf cannot bind one row per element. This happens when an each() of <${want}> sits directly inside a table: the parser inserts <${got}> around the whole run. Put the each() inside an explicit <${got}> (e.g. <table><${got}>{each(...)}</${got}></table>) so the rows are the direct children kerf binds.`
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
function collectOwnedItems(bindings) {
|
|
1268
|
+
const owned = /* @__PURE__ */ new Set();
|
|
1269
|
+
for (const b of bindings.values()) {
|
|
1270
|
+
for (const item of b.items) owned.add(item.node);
|
|
1271
|
+
}
|
|
1272
|
+
return owned;
|
|
1273
|
+
}
|
|
1274
|
+
function cleanupOrphanBindings(segment, bindings, renderCtx) {
|
|
1275
|
+
const liveIds = collectLists(segment);
|
|
1276
|
+
for (const [id, binding] of bindings) {
|
|
1277
|
+
if (liveIds.has(id)) continue;
|
|
1278
|
+
for (const item of binding.items) {
|
|
1279
|
+
disposeRowBindings(item.bindingDisposers);
|
|
1280
|
+
item.node.remove();
|
|
1281
|
+
}
|
|
1282
|
+
binding.marker.remove();
|
|
1283
|
+
bindings.delete(id);
|
|
1284
|
+
renderCtx.bindingCounts.delete(id);
|
|
1285
|
+
renderCtx.bindingSources.delete(id);
|
|
1286
|
+
renderCtx.caches.delete(id);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
function collectComments(node, out) {
|
|
1290
|
+
for (let c = node.firstChild; c; c = c.nextSibling) {
|
|
1291
|
+
if (c.nodeType === Node.COMMENT_NODE) out.push(c);
|
|
1292
|
+
else if (c.nodeType === Node.ELEMENT_NODE && !isOverlayHost(c))
|
|
1293
|
+
collectComments(c, out);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1133
1297
|
// src/mount.ts
|
|
1134
1298
|
var MOUNTED_MARKER = /* @__PURE__ */ Symbol.for("kerfjs.mounted");
|
|
1135
1299
|
var NESTED_MOUNT_MSG = "mount: rootEl is already inside (or contains) a mounted tree. kerf supports one mount per tree \u2014 compose with plain functions that return JSX instead of nesting mounts.";
|
|
@@ -1155,15 +1319,17 @@ function assertNotInsideMountedTree(rootEl) {
|
|
|
1155
1319
|
);
|
|
1156
1320
|
}
|
|
1157
1321
|
let ancestor = rootEl.parentElement;
|
|
1158
|
-
while (ancestor) {
|
|
1322
|
+
while (ancestor && !isOverlayHost(ancestor)) {
|
|
1159
1323
|
if (isMounted(ancestor)) throw new Error(NESTED_MOUNT_MSG);
|
|
1160
1324
|
ancestor = ancestor.parentElement;
|
|
1161
1325
|
}
|
|
1162
1326
|
const stack = [];
|
|
1163
|
-
for (let i = 0; i < rootEl.children.length; i++)
|
|
1327
|
+
for (let i = 0; i < rootEl.children.length; i++)
|
|
1328
|
+
stack.push(rootEl.children[i]);
|
|
1164
1329
|
while (stack.length > 0) {
|
|
1165
1330
|
const cur = stack.pop();
|
|
1166
1331
|
if (isMounted(cur)) throw new Error(NESTED_MOUNT_MSG);
|
|
1332
|
+
if (isOverlayHost(cur)) continue;
|
|
1167
1333
|
for (let i = 0; i < cur.children.length; i++) stack.push(cur.children[i]);
|
|
1168
1334
|
}
|
|
1169
1335
|
}
|
|
@@ -1241,7 +1407,8 @@ function mount(rootEl, render) {
|
|
|
1241
1407
|
valueOnlyWarnCtx
|
|
1242
1408
|
);
|
|
1243
1409
|
if (anyRebuiltListIsGranular(segment, renderCtx.rebuiltLists)) {
|
|
1244
|
-
for (const id of renderCtx.rebuiltLists)
|
|
1410
|
+
for (const id of renderCtx.rebuiltLists)
|
|
1411
|
+
renderCtx.bindingCounts.delete(id);
|
|
1245
1412
|
result = runRenderPass();
|
|
1246
1413
|
segment = resultToSegment(result);
|
|
1247
1414
|
nextStaticHtml = runSubsequentRender(
|
|
@@ -1255,7 +1422,8 @@ function mount(rootEl, render) {
|
|
|
1255
1422
|
}
|
|
1256
1423
|
if (nextStaticHtml !== prevStaticHtml) {
|
|
1257
1424
|
bindingDisposers = wireBindings(rootEl, bindingCtx, bindingDisposers);
|
|
1258
|
-
if (devHooks.staleBindingEnabled?.())
|
|
1425
|
+
if (devHooks.staleBindingEnabled?.())
|
|
1426
|
+
prevWiredBindings = bindingCtx.list;
|
|
1259
1427
|
} else {
|
|
1260
1428
|
devHooks.staleBinding?.(prevWiredBindings, bindingCtx.list);
|
|
1261
1429
|
}
|
|
@@ -1283,11 +1451,7 @@ function mount(rootEl, render) {
|
|
|
1283
1451
|
renderCtx.previousCallCount = renderCtx.counter;
|
|
1284
1452
|
devHooks.listInvariants?.(rootEl, bindings, expectedCounts || void 0);
|
|
1285
1453
|
}
|
|
1286
|
-
const
|
|
1287
|
-
reconcileAndCommitListPhase(renderStaticPhase());
|
|
1288
|
-
});
|
|
1289
|
-
return () => {
|
|
1290
|
-
disposeEffect();
|
|
1454
|
+
const releaseResources = () => {
|
|
1291
1455
|
for (const d of bindingDisposers) d();
|
|
1292
1456
|
bindingDisposers = [];
|
|
1293
1457
|
for (const b of bindings.values()) {
|
|
@@ -1296,6 +1460,21 @@ function mount(rootEl, render) {
|
|
|
1296
1460
|
listenerWarnObserver?.disconnect();
|
|
1297
1461
|
setMounted(rootEl, false);
|
|
1298
1462
|
};
|
|
1463
|
+
const priorChildren = rootEl.firstChild ? Array.from(rootEl.childNodes) : [];
|
|
1464
|
+
let disposeEffect;
|
|
1465
|
+
try {
|
|
1466
|
+
disposeEffect = effect(() => {
|
|
1467
|
+
reconcileAndCommitListPhase(renderStaticPhase());
|
|
1468
|
+
});
|
|
1469
|
+
} catch (err) {
|
|
1470
|
+
releaseResources();
|
|
1471
|
+
rootEl.replaceChildren(...priorChildren);
|
|
1472
|
+
throw err;
|
|
1473
|
+
}
|
|
1474
|
+
return () => {
|
|
1475
|
+
disposeEffect();
|
|
1476
|
+
releaseResources();
|
|
1477
|
+
};
|
|
1299
1478
|
}
|
|
1300
1479
|
function runFirstRender(rootEl, segment, bindings) {
|
|
1301
1480
|
rootEl.innerHTML = flatten(segment, true);
|
|
@@ -1307,12 +1486,22 @@ function runSubsequentRender(rootEl, segment, bindings, renderCtx, prevStaticHtm
|
|
|
1307
1486
|
if (currentStaticHtml === prevStaticHtml) {
|
|
1308
1487
|
return prevStaticHtml;
|
|
1309
1488
|
}
|
|
1310
|
-
devHooks.valueOnlyRerender?.(
|
|
1489
|
+
devHooks.valueOnlyRerender?.(
|
|
1490
|
+
prevStaticHtml,
|
|
1491
|
+
currentStaticHtml,
|
|
1492
|
+
valueOnlyWarnCtx
|
|
1493
|
+
);
|
|
1311
1494
|
cleanupOrphanBindings(segment, bindings, renderCtx);
|
|
1312
1495
|
const template = rootEl.cloneNode(false);
|
|
1313
1496
|
template.innerHTML = currentStaticHtml;
|
|
1314
1497
|
morph(rootEl, template, collectOwnedItems(bindings));
|
|
1315
|
-
bindListsFromMarkers(
|
|
1498
|
+
bindListsFromMarkers(
|
|
1499
|
+
rootEl,
|
|
1500
|
+
segment,
|
|
1501
|
+
bindings,
|
|
1502
|
+
false,
|
|
1503
|
+
renderCtx.rebuiltLists
|
|
1504
|
+
);
|
|
1316
1505
|
return currentStaticHtml;
|
|
1317
1506
|
}
|
|
1318
1507
|
function coerceRenderResult(result) {
|
|
@@ -1322,107 +1511,7 @@ function coerceRenderResult(result) {
|
|
|
1322
1511
|
function resultToSegment(result) {
|
|
1323
1512
|
return isSafeHtml(result) ? result.__segment ?? { kind: "static", html: result.__html } : { kind: "static", html: coerceRenderResult(result) };
|
|
1324
1513
|
}
|
|
1325
|
-
function anyRebuiltListIsGranular(segment, rebuilt) {
|
|
1326
|
-
if (!rebuilt.size) return false;
|
|
1327
|
-
const lists = collectLists(segment);
|
|
1328
|
-
for (const id of rebuilt) {
|
|
1329
|
-
if (lists.get(id)?.patches) return true;
|
|
1330
|
-
}
|
|
1331
|
-
return false;
|
|
1332
|
-
}
|
|
1333
|
-
function bindListsFromMarkers(rootEl, segment, bindings, inlinedItems, rebuiltLists) {
|
|
1334
|
-
const lists = collectLists(segment);
|
|
1335
|
-
const found = [];
|
|
1336
|
-
collectComments(rootEl, found);
|
|
1337
|
-
for (const marker of found) {
|
|
1338
|
-
if (!marker.data.startsWith(LIST_MARKER_PREFIX)) continue;
|
|
1339
|
-
const id = marker.data.slice(LIST_MARKER_PREFIX.length);
|
|
1340
|
-
const existing = bindings.get(id);
|
|
1341
|
-
if (existing) {
|
|
1342
|
-
if (existing.marker === marker && rootEl.contains(existing.marker)) continue;
|
|
1343
|
-
for (const item of existing.items) {
|
|
1344
|
-
disposeRowBindings(item.bindingDisposers);
|
|
1345
|
-
if (rootEl.contains(item.node)) {
|
|
1346
|
-
item.node.parentElement?.removeChild(item.node);
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
bindings.delete(id);
|
|
1350
|
-
rebuiltLists?.add(id);
|
|
1351
|
-
devHooks.listRebind?.(id, marker.parentElement);
|
|
1352
|
-
}
|
|
1353
|
-
const listSeg = lists.get(id);
|
|
1354
|
-
const liveParent = marker.parentElement;
|
|
1355
|
-
const items = [];
|
|
1356
|
-
if (inlinedItems) {
|
|
1357
|
-
let next = marker.nextElementSibling;
|
|
1358
|
-
for (let i = 0; i < listSeg.items.length && next !== null; i++) {
|
|
1359
|
-
validateInlinedRowMatch(listSeg.items[i].html, i, next, liveParent);
|
|
1360
|
-
const rowBindings = listSeg.items[i].bindings;
|
|
1361
|
-
const bound = {
|
|
1362
|
-
ref: listSeg.items[i].ref,
|
|
1363
|
-
cacheKey: listSeg.items[i].cacheKey,
|
|
1364
|
-
html: listSeg.items[i].html,
|
|
1365
|
-
node: next,
|
|
1366
|
-
bindings: rowBindings
|
|
1367
|
-
};
|
|
1368
|
-
if (rowBindings?.length) {
|
|
1369
|
-
bound.bindingDisposers = wireRowBindings(next, rowBindings);
|
|
1370
|
-
}
|
|
1371
|
-
items.push(bound);
|
|
1372
|
-
next = next.nextElementSibling;
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
const binding = { liveParent, items, marker };
|
|
1376
|
-
if (items.length > 0) {
|
|
1377
|
-
devHooks.missingRowKey?.(items[0].node, items[0].html, binding);
|
|
1378
|
-
}
|
|
1379
|
-
devHooks.eachInMorphSkip?.(id, liveParent, rootEl);
|
|
1380
|
-
bindings.set(id, binding);
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
function validateInlinedRowMatch(expectedHtml, index, boundEl, liveParent) {
|
|
1384
|
-
if (boundEl.outerHTML === expectedHtml) return;
|
|
1385
|
-
const { content, count } = parseRowTemplate(expectedHtml, liveParent);
|
|
1386
|
-
if (count !== 1) throw rowContractError(index, expectedHtml, liveParent);
|
|
1387
|
-
const expectedTag = content.firstElementChild.tagName;
|
|
1388
|
-
if (boundEl.tagName !== expectedTag) throw rowStructureError(index, boundEl.tagName, expectedTag);
|
|
1389
|
-
}
|
|
1390
|
-
function rowStructureError(index, gotTag, wantTag) {
|
|
1391
|
-
const got = gotTag.toLowerCase();
|
|
1392
|
-
const want = wantTag.toLowerCase();
|
|
1393
|
-
return new Error(
|
|
1394
|
-
`each(): row ${index} renders <${want}>, but the HTML parser wrapped the rows in <${got}> \u2014 so kerf cannot bind one row per element. This happens when an each() of <${want}> sits directly inside a table: the parser inserts <${got}> around the whole run. Put the each() inside an explicit <${got}> (e.g. <table><${got}>{each(...)}</${got}></table>) so the rows are the direct children kerf binds.`
|
|
1395
|
-
);
|
|
1396
|
-
}
|
|
1397
|
-
function collectOwnedItems(bindings) {
|
|
1398
|
-
const owned = /* @__PURE__ */ new Set();
|
|
1399
|
-
for (const b of bindings.values()) {
|
|
1400
|
-
for (const item of b.items) owned.add(item.node);
|
|
1401
|
-
}
|
|
1402
|
-
return owned;
|
|
1403
|
-
}
|
|
1404
|
-
function cleanupOrphanBindings(segment, bindings, renderCtx) {
|
|
1405
|
-
const liveIds = collectLists(segment);
|
|
1406
|
-
for (const [id, binding] of bindings) {
|
|
1407
|
-
if (liveIds.has(id)) continue;
|
|
1408
|
-
for (const item of binding.items) {
|
|
1409
|
-
disposeRowBindings(item.bindingDisposers);
|
|
1410
|
-
item.node.remove();
|
|
1411
|
-
}
|
|
1412
|
-
binding.marker.remove();
|
|
1413
|
-
bindings.delete(id);
|
|
1414
|
-
renderCtx.bindingCounts.delete(id);
|
|
1415
|
-
renderCtx.bindingSources.delete(id);
|
|
1416
|
-
renderCtx.caches.delete(id);
|
|
1417
|
-
}
|
|
1418
|
-
}
|
|
1419
|
-
function collectComments(node, out) {
|
|
1420
|
-
for (let c = node.firstChild; c; c = c.nextSibling) {
|
|
1421
|
-
if (c.nodeType === Node.COMMENT_NODE) out.push(c);
|
|
1422
|
-
else if (c.nodeType === Node.ELEMENT_NODE) collectComments(c, out);
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
1514
|
|
|
1426
1515
|
export { captureFocus, each, morph, mount, moveNode, restoreFocus };
|
|
1427
1516
|
|
|
1428
|
-
//# sourceMappingURL=chunk-
|
|
1517
|
+
//# sourceMappingURL=chunk-TLL3KXT6.js.map
|