kerfjs 0.5.1 → 0.7.0
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 +50 -5
- package/README.md +17 -1
- package/dist/array-signal.js +2 -2
- package/dist/array-signal.js.map +1 -1
- package/dist/{chunk-KFOSUCCC.js → chunk-4VT4YZOO.js} +13 -4
- package/dist/chunk-4VT4YZOO.js.map +1 -0
- package/dist/chunk-UU2YJEJY.js +41 -0
- package/dist/chunk-UU2YJEJY.js.map +1 -0
- package/dist/{chunk-GQGJFCWL.js → chunk-Z7ZHXD2R.js} +14 -4
- package/dist/chunk-Z7ZHXD2R.js.map +1 -0
- package/dist/index.d.ts +97 -17
- package/dist/index.js +387 -43
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.d.ts +52 -2
- package/dist/jsx-runtime.js +1 -1
- package/dist/testing.js +2 -2
- package/package.json +4 -2
- package/dist/chunk-FN2ID4QO.js +0 -3
- package/dist/chunk-FN2ID4QO.js.map +0 -1
- package/dist/chunk-GQGJFCWL.js.map +0 -1
- package/dist/chunk-KFOSUCCC.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { ArraySignal } from './array-signal.js';
|
|
2
2
|
import { SafeHtml } from './jsx-runtime.js';
|
|
3
3
|
export { Fragment, isSafeHtml, raw } from './jsx-runtime.js';
|
|
4
|
-
|
|
4
|
+
import { Signal } from '@preact/signals-core';
|
|
5
|
+
export { ReadonlySignal, Signal, batch, computed, effect } from '@preact/signals-core';
|
|
5
6
|
export { S as Store, d as defineStore, r as resetAllStores } from './testing-CdMgVVoI.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -60,39 +61,104 @@ declare function delegate(rootEl: HTMLElement, type: string, selector: string, h
|
|
|
60
61
|
declare function delegateCapture(rootEl: HTMLElement, type: string, selector: string, handler: Handler): () => void;
|
|
61
62
|
|
|
62
63
|
/**
|
|
63
|
-
* `each(items, render,
|
|
64
|
+
* `each(items, render, cacheKey?)` — keyed list iteration with per-item memoization.
|
|
64
65
|
*
|
|
65
66
|
* Drops in as the body of a list-rendering JSX expression inside a `mount()`
|
|
66
67
|
* render function. Returns a `SafeHtml` carrying a structured list segment,
|
|
67
68
|
* so `mount()` can run a native keyed reconciler instead of the general-
|
|
68
69
|
* purpose morph for these children.
|
|
69
70
|
*
|
|
70
|
-
* Two layers of
|
|
71
|
+
* Two layers of optimization:
|
|
71
72
|
*
|
|
72
|
-
* 1. Per-item
|
|
73
|
-
* identity (and optional `
|
|
74
|
-
* Their HTML strings come from a `WeakMap` keyed by item reference.
|
|
75
|
-
* immutable-update style ("replace the row object" instead of "mutate
|
|
76
|
-
* makes the cache work automatically.
|
|
73
|
+
* 1. Per-item memoization. `render(item)` is skipped for items whose object
|
|
74
|
+
* identity (and optional `cacheKey`) are unchanged since the previous
|
|
75
|
+
* call. Their HTML strings come from a `WeakMap` keyed by item reference.
|
|
76
|
+
* The immutable-update style ("replace the row object" instead of "mutate
|
|
77
|
+
* it") makes the cache work automatically.
|
|
77
78
|
*
|
|
78
|
-
* 2. Structural handoff. `mount()`
|
|
79
|
+
* 2. Structural handoff. `mount()` recognizes the list segment and bypasses
|
|
79
80
|
* the parse-the-whole-table round trip: only fresh items get parsed (one
|
|
80
81
|
* at a time, into the smallest detached element), and only changed rows
|
|
81
82
|
* get patched in the live DOM. Unchanged rows are physically the same
|
|
82
83
|
* nodes they were before — never visited.
|
|
83
84
|
*
|
|
84
|
-
* `
|
|
85
|
-
* what the row should render (e.g. a
|
|
86
|
-
* class on one row). Same item identity
|
|
87
|
-
*
|
|
88
|
-
*
|
|
85
|
+
* `cacheKey` is a passive comparator — it covers the case where external
|
|
86
|
+
* state, not the item itself, drives what the row should render (e.g. a
|
|
87
|
+
* "currently selected" id flips a CSS class on one row). Same item identity
|
|
88
|
+
* but a different `cacheKey` return value means "the cached HTML is stale —
|
|
89
|
+
* re-render this row." Not a reactive subscription: it's evaluated once per
|
|
90
|
+
* mount-effect run and compared against the previous run's return value. If
|
|
91
|
+
* you don't pass `cacheKey`, only object-identity changes invalidate the
|
|
92
|
+
* cache. (Renamed from `key` for clarity — it shared a name with React's
|
|
93
|
+
* `key` prop but has different semantics; the new name says what the
|
|
94
|
+
* parameter actually does. Positional callers — the canonical form — are
|
|
95
|
+
* unaffected.)
|
|
89
96
|
*
|
|
90
97
|
* Items must be objects (cache is a `WeakMap`); wrap primitives if you need
|
|
91
98
|
* to iterate them. Each item's render output must produce exactly one
|
|
92
99
|
* top-level element — the list reconciler binds one live DOM node per item.
|
|
93
100
|
*/
|
|
94
101
|
|
|
95
|
-
declare function each<T extends object>(items: readonly T[] | ArraySignal<T>, render: (item: T, index: number) => SafeHtml | string,
|
|
102
|
+
declare function each<T extends object>(items: readonly T[] | ArraySignal<T>, render: (item: T, index: number) => SafeHtml | string, cacheKey?: (item: T, index: number) => unknown): SafeHtml;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* `morph(liveRoot, template)` — minimum-mutation DOM reconciliation.
|
|
106
|
+
*
|
|
107
|
+
* Public primitive (KF-150): one-shot reconciliation against an
|
|
108
|
+
* already-populated element. `mount()` first paints by writing
|
|
109
|
+
* `innerHTML`; `morph()` is the "I have a live tree and a template,
|
|
110
|
+
* reconcile them in place" sibling. Use it for SSR hydration of static
|
|
111
|
+
* fragments, page-refresh diffs, third-party widget remounts, etc.
|
|
112
|
+
*
|
|
113
|
+
* Replaces our previous dependency on `morphdom`. The algorithm is the
|
|
114
|
+
* classic two-tree walk: match children by key (id, then data-key, then
|
|
115
|
+
* positional same-tag), morph matches in place, insert / remove / clone
|
|
116
|
+
* the rest. Specialized for what kerf needs:
|
|
117
|
+
*
|
|
118
|
+
* - `childrenOnly` is always true; the live root is never replaced.
|
|
119
|
+
* - Per-element short-circuits: `data-morph-skip` (library-owned, leave
|
|
120
|
+
* element AND subtree verbatim), `data-morph-skip-children` (KF-152 —
|
|
121
|
+
* morph attrs on the element, leave its subtree verbatim — for
|
|
122
|
+
* client-hydrated slots whose loading/state classes still need to flow
|
|
123
|
+
* through), and `isEqualNode` (byte-identical, no work needed).
|
|
124
|
+
* - **`data-morph-preserve`** (KF-151) is honored in the trailing-removal
|
|
125
|
+
* pass: an unmatched live element with this attribute is skipped instead
|
|
126
|
+
* of removed. Lets imperatively-injected nodes (autoplay `<video>`s,
|
|
127
|
+
* tour-widget tooltips, analytics pixels) survive across renders without
|
|
128
|
+
* having to `data-morph-skip` the entire parent.
|
|
129
|
+
* - **`ownedItems`** is the set of element nodes owned by an `each()`
|
|
130
|
+
* list reconciler. The morph skips them in every children walk —
|
|
131
|
+
* they're not added to the keyed-lookup map, the from-cursor advances
|
|
132
|
+
* past them, and the trailing-removal pass leaves them in place. This
|
|
133
|
+
* lets each() coexist with non-list siblings inside the same parent
|
|
134
|
+
* (KF-102 round 2): the morph still walks the parent's children to
|
|
135
|
+
* reconcile siblings, but never disturbs list rows. The parameter is an
|
|
136
|
+
* internal coordination channel between `mount()` and the reconciler;
|
|
137
|
+
* public callers should omit it (the default empty set is correct for
|
|
138
|
+
* any tree that isn't being managed by an active `mount()`).
|
|
139
|
+
* - Focused text inputs (`<input>`/`<textarea>`) keep their value +
|
|
140
|
+
* selection across the morph; focused `[contenteditable]` keeps its
|
|
141
|
+
* entire subtree (typed content + caret + multi-range selection).
|
|
142
|
+
*
|
|
143
|
+
* Algorithm credit: based on the design of
|
|
144
|
+
* https://github.com/patrick-steele-idem/morphdom by Patrick Steele-Idem
|
|
145
|
+
* (MIT licensed). Reimplemented here so kerf can specialize the hot paths
|
|
146
|
+
* (segment-aware list dispatch, lighter callback surface) and drop the
|
|
147
|
+
* runtime dependency. Original copyright preserved in `LICENSE`.
|
|
148
|
+
*/
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Reconcile the children of `liveRoot` to match `template`.
|
|
152
|
+
*
|
|
153
|
+
* `template` can be an `Element` (used directly), a `SafeHtml`, or a raw
|
|
154
|
+
* HTML string — the latter two are stringified and parsed into a transient
|
|
155
|
+
* element whose tag matches `liveRoot`. The active text-entry / focused-
|
|
156
|
+
* contenteditable preservation rules apply in all cases.
|
|
157
|
+
*
|
|
158
|
+
* `ownedItems` is an internal coordination channel for `mount()`'s list
|
|
159
|
+
* reconciler; public callers should omit it.
|
|
160
|
+
*/
|
|
161
|
+
declare function morph(liveRoot: Element, template: Element | SafeHtml | string, ownedItems?: ReadonlySet<Element>): void;
|
|
96
162
|
|
|
97
163
|
/**
|
|
98
164
|
* `mount(rootEl, render)` — kerf's render primitive.
|
|
@@ -106,7 +172,7 @@ declare function each<T extends object>(items: readonly T[] | ArraySignal<T>, re
|
|
|
106
172
|
* Two phases per render:
|
|
107
173
|
*
|
|
108
174
|
* - Static surrounds (everything outside `each()` lists): kerf's native
|
|
109
|
-
* `
|
|
175
|
+
* `morph()` reconciler walks a freshly-built template against the live
|
|
110
176
|
* tree. Conventions: id/data-key matching, `data-morph-skip`, focus
|
|
111
177
|
* preservation.
|
|
112
178
|
*
|
|
@@ -147,6 +213,20 @@ declare function each<T extends object>(items: readonly T[] | ArraySignal<T>, re
|
|
|
147
213
|
type MountResult = SafeHtml | string | number | boolean | null | undefined;
|
|
148
214
|
declare function mount(rootEl: HTMLElement, render: () => MountResult): () => void;
|
|
149
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Re-exports of `@preact/signals-core`. Lets the rest of the codebase depend
|
|
218
|
+
* on `'./reactive.js'` without naming the underlying lib, so swapping it out
|
|
219
|
+
* later (or fronting it with a hand-rolled implementation) is a one-file
|
|
220
|
+
* change.
|
|
221
|
+
*
|
|
222
|
+
* The `signal()` factory is dev-gated: when `NODE_ENV !== 'production'` and
|
|
223
|
+
* `KERF_DEV_WARN_UNTRACKED_SIGNALS === '1'`, it returns a `DevSignal` that
|
|
224
|
+
* warns on writes to signals with no subscribers (KF-176). Off by default;
|
|
225
|
+
* production always returns the bare `@preact/signals-core` signal.
|
|
226
|
+
*/
|
|
227
|
+
|
|
228
|
+
declare function signal<T>(value?: T): Signal<T>;
|
|
229
|
+
|
|
150
230
|
/**
|
|
151
231
|
* `toElement(jsx)` — JSX → DOM, with SVG-aware namespace handling.
|
|
152
232
|
*
|
|
@@ -163,4 +243,4 @@ declare function mount(rootEl: HTMLElement, render: () => MountResult): () => vo
|
|
|
163
243
|
|
|
164
244
|
declare function toElement(jsx: SafeHtml | string): Element;
|
|
165
245
|
|
|
166
|
-
export { type MountResult, SafeHtml, delegate, delegateCapture, each, mount, toElement };
|
|
246
|
+
export { type MountResult, SafeHtml, delegate, delegateCapture, each, morph, mount, signal, toElement };
|