what-core 0.5.6 → 0.6.1
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/README.md +8 -6
- package/dist/components.js +1 -1
- package/dist/dom.js +127 -451
- package/dist/h.js +1 -1
- package/dist/hooks.js +4 -0
- package/dist/index.js +5983 -123
- package/dist/index.js.map +7 -0
- package/dist/index.min.js +123 -0
- package/dist/index.min.js.map +7 -0
- package/dist/jsx-dev-runtime.js +51 -0
- package/dist/jsx-dev-runtime.js.map +7 -0
- package/dist/jsx-dev-runtime.min.js +2 -0
- package/dist/jsx-dev-runtime.min.js.map +7 -0
- package/dist/jsx-runtime.js +49 -0
- package/dist/jsx-runtime.js.map +7 -0
- package/dist/jsx-runtime.min.js +2 -0
- package/dist/jsx-runtime.min.js.map +7 -0
- package/dist/reactive.js +175 -11
- package/dist/render.js +1549 -272
- package/dist/render.js.map +7 -0
- package/dist/render.min.js +2 -0
- package/dist/render.min.js.map +7 -0
- package/dist/testing.js +1257 -144
- package/dist/testing.js.map +7 -0
- package/dist/testing.min.js +2 -0
- package/dist/testing.min.js.map +7 -0
- package/dist/what.js +3 -2
- package/package.json +9 -4
- package/src/agent-context.js +126 -0
- package/src/components.js +10 -34
- package/src/dom.js +269 -749
- package/src/errors.js +253 -0
- package/src/guardrails.js +224 -0
- package/src/h.js +3 -3
- package/src/hooks.js +121 -52
- package/src/index.js +38 -4
- package/src/reactive.js +450 -64
- package/src/render.js +462 -14
- package/src/testing.js +169 -1
- package/src/warnings.js +110 -0
package/dist/render.js
CHANGED
|
@@ -1,118 +1,1236 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
// packages/core/src/reactive.js
|
|
2
|
+
var __DEV__ = typeof process !== "undefined" ? true : true;
|
|
3
|
+
var __devtools = null;
|
|
4
|
+
var currentEffect = null;
|
|
5
|
+
var currentRoot = null;
|
|
6
|
+
var currentOwner = null;
|
|
7
|
+
var insideComputed = false;
|
|
8
|
+
var batchDepth = 0;
|
|
9
|
+
var pendingEffects = [];
|
|
10
|
+
var pendingNeedSort = false;
|
|
11
|
+
var subSetOwner = /* @__PURE__ */ new WeakMap();
|
|
12
|
+
var NEEDS_UPSTREAM = /* @__PURE__ */ Symbol("needs_upstream");
|
|
13
|
+
function signal(initial, debugName) {
|
|
14
|
+
let value = initial;
|
|
15
|
+
const subs = /* @__PURE__ */ new Set();
|
|
16
|
+
function sig(...args) {
|
|
17
|
+
if (args.length === 0) {
|
|
18
|
+
if (currentEffect) {
|
|
19
|
+
subs.add(currentEffect);
|
|
20
|
+
currentEffect.deps.push(subs);
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
if (__DEV__ && insideComputed) {
|
|
25
|
+
console.warn(
|
|
26
|
+
"[what] Signal.set() called inside a computed function. This may cause infinite loops. Use effect() instead." + (debugName ? ` (signal: ${debugName})` : "")
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
const nextVal = typeof args[0] === "function" ? args[0](value) : args[0];
|
|
30
|
+
if (Object.is(value, nextVal)) return;
|
|
31
|
+
value = nextVal;
|
|
32
|
+
if (__DEV__ && __devtools) __devtools.onSignalUpdate(sig);
|
|
33
|
+
if (subs.size > 0) notify(subs);
|
|
34
|
+
}
|
|
35
|
+
sig.set = (next) => {
|
|
36
|
+
if (__DEV__ && insideComputed) {
|
|
37
|
+
console.warn(
|
|
38
|
+
"[what] Signal.set() called inside a computed function. This may cause infinite loops. Use effect() instead." + (debugName ? ` (signal: ${debugName})` : "")
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
const nextVal = typeof next === "function" ? next(value) : next;
|
|
42
|
+
if (Object.is(value, nextVal)) return;
|
|
43
|
+
value = nextVal;
|
|
44
|
+
if (__DEV__ && __devtools) __devtools.onSignalUpdate(sig);
|
|
45
|
+
if (subs.size > 0) notify(subs);
|
|
46
|
+
};
|
|
47
|
+
sig.peek = () => value;
|
|
48
|
+
sig.subscribe = (fn) => {
|
|
49
|
+
return effect(() => fn(sig()));
|
|
50
|
+
};
|
|
51
|
+
sig._signal = true;
|
|
52
|
+
if (__DEV__) {
|
|
53
|
+
sig._subs = subs;
|
|
54
|
+
if (debugName) sig._debugName = debugName;
|
|
55
|
+
}
|
|
56
|
+
if (__DEV__ && __devtools) __devtools.onSignalCreate(sig);
|
|
57
|
+
return sig;
|
|
58
|
+
}
|
|
59
|
+
function _updateLevel(e) {
|
|
60
|
+
let maxDepLevel = 0;
|
|
61
|
+
const deps = e.deps;
|
|
62
|
+
for (let i = 0; i < deps.length; i++) {
|
|
63
|
+
const owner = subSetOwner.get(deps[i]);
|
|
64
|
+
if (owner) {
|
|
65
|
+
const depLevel = owner._level;
|
|
66
|
+
if (depLevel > maxDepLevel) maxDepLevel = depLevel;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
e._level = maxDepLevel + 1;
|
|
70
|
+
}
|
|
71
|
+
function effect(fn, opts) {
|
|
72
|
+
const e = _createEffect(fn);
|
|
73
|
+
e._level = 1;
|
|
74
|
+
const prev = currentEffect;
|
|
75
|
+
currentEffect = e;
|
|
76
|
+
try {
|
|
77
|
+
const result = e.fn();
|
|
78
|
+
if (typeof result === "function") e._cleanup = result;
|
|
79
|
+
} finally {
|
|
80
|
+
currentEffect = prev;
|
|
81
|
+
}
|
|
82
|
+
_updateLevel(e);
|
|
83
|
+
if (opts?.stable) e._stable = true;
|
|
84
|
+
const dispose = () => _disposeEffect(e);
|
|
85
|
+
if (currentRoot) {
|
|
86
|
+
currentRoot.disposals.push(dispose);
|
|
87
|
+
}
|
|
88
|
+
return dispose;
|
|
89
|
+
}
|
|
90
|
+
function _createEffect(fn, lazy) {
|
|
91
|
+
const e = {
|
|
92
|
+
fn,
|
|
93
|
+
deps: [],
|
|
94
|
+
// array of subscriber sets (cheaper than Set for typical 1-3 deps)
|
|
95
|
+
lazy: lazy || false,
|
|
96
|
+
_onNotify: null,
|
|
97
|
+
disposed: false,
|
|
98
|
+
_pending: false,
|
|
99
|
+
_stable: false,
|
|
100
|
+
// stable effects skip cleanup/re-subscribe on re-run
|
|
101
|
+
_level: 0,
|
|
102
|
+
// topological depth: signals=0, computed/effects=max(deps)+1
|
|
103
|
+
_computed: false,
|
|
104
|
+
// true for computed inner effects
|
|
105
|
+
_computedSubs: null,
|
|
106
|
+
// reference to the computed's subscriber set
|
|
107
|
+
_isDirty: null,
|
|
108
|
+
// function to check if computed is dirty (set by computed())
|
|
109
|
+
_markDirty: null
|
|
110
|
+
// function to mark computed dirty (set by computed())
|
|
111
|
+
};
|
|
112
|
+
if (__DEV__ && __devtools) __devtools.onEffectCreate(e);
|
|
113
|
+
return e;
|
|
114
|
+
}
|
|
115
|
+
function _runEffect(e) {
|
|
116
|
+
if (e.disposed) return;
|
|
117
|
+
if (e._stable) {
|
|
118
|
+
if (e._cleanup) {
|
|
119
|
+
try {
|
|
120
|
+
e._cleanup();
|
|
121
|
+
} catch (err) {
|
|
122
|
+
if (__DEV__) console.warn("[what] Error in effect cleanup:", err);
|
|
123
|
+
}
|
|
124
|
+
e._cleanup = null;
|
|
125
|
+
}
|
|
126
|
+
const prev2 = currentEffect;
|
|
127
|
+
currentEffect = null;
|
|
128
|
+
try {
|
|
129
|
+
const result = e.fn();
|
|
130
|
+
if (typeof result === "function") e._cleanup = result;
|
|
131
|
+
} catch (err) {
|
|
132
|
+
if (__devtools?.onError) __devtools.onError(err, { type: "effect", effect: e });
|
|
133
|
+
if (__DEV__) console.warn("[what] Error in stable effect:", err);
|
|
134
|
+
} finally {
|
|
135
|
+
currentEffect = prev2;
|
|
136
|
+
}
|
|
137
|
+
if (__DEV__ && __devtools?.onEffectRun) __devtools.onEffectRun(e);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
cleanup(e);
|
|
141
|
+
if (e._cleanup) {
|
|
142
|
+
try {
|
|
143
|
+
e._cleanup();
|
|
144
|
+
} catch (err) {
|
|
145
|
+
if (__devtools?.onError) __devtools.onError(err, { type: "effect-cleanup", effect: e });
|
|
146
|
+
if (__DEV__) console.warn("[what] Error in effect cleanup:", err);
|
|
147
|
+
}
|
|
148
|
+
e._cleanup = null;
|
|
149
|
+
}
|
|
150
|
+
const prev = currentEffect;
|
|
151
|
+
currentEffect = e;
|
|
152
|
+
try {
|
|
153
|
+
const result = e.fn();
|
|
154
|
+
if (typeof result === "function") {
|
|
155
|
+
e._cleanup = result;
|
|
156
|
+
}
|
|
157
|
+
} catch (err) {
|
|
158
|
+
if (err === NEEDS_UPSTREAM) throw err;
|
|
159
|
+
if (__devtools?.onError) __devtools.onError(err, { type: "effect", effect: e });
|
|
160
|
+
throw err;
|
|
161
|
+
} finally {
|
|
162
|
+
currentEffect = prev;
|
|
163
|
+
}
|
|
164
|
+
if (__DEV__ && __devtools?.onEffectRun) __devtools.onEffectRun(e);
|
|
165
|
+
}
|
|
166
|
+
function _disposeEffect(e) {
|
|
167
|
+
e.disposed = true;
|
|
168
|
+
if (__DEV__ && __devtools) __devtools.onEffectDispose(e);
|
|
169
|
+
cleanup(e);
|
|
170
|
+
if (e._cleanup) {
|
|
171
|
+
try {
|
|
172
|
+
e._cleanup();
|
|
173
|
+
} catch (err) {
|
|
174
|
+
if (__DEV__) console.warn("[what] Error in effect cleanup on dispose:", err);
|
|
175
|
+
}
|
|
176
|
+
e._cleanup = null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function cleanup(e) {
|
|
180
|
+
const deps = e.deps;
|
|
181
|
+
for (let i = 0; i < deps.length; i++) deps[i].delete(e);
|
|
182
|
+
deps.length = 0;
|
|
183
|
+
}
|
|
184
|
+
var notifyDepth = 0;
|
|
185
|
+
var notifyQueue = null;
|
|
186
|
+
var notifyQueueLen = 0;
|
|
187
|
+
function notify(subs) {
|
|
188
|
+
if (notifyDepth === 0) {
|
|
189
|
+
notifyDepth = 1;
|
|
190
|
+
try {
|
|
191
|
+
for (const e of subs) {
|
|
192
|
+
if (e.disposed) continue;
|
|
193
|
+
if (e._onNotify) {
|
|
194
|
+
e._onNotify();
|
|
195
|
+
} else if (batchDepth === 0 && e._stable) {
|
|
196
|
+
const prev = currentEffect;
|
|
197
|
+
currentEffect = null;
|
|
198
|
+
try {
|
|
199
|
+
const result = e.fn();
|
|
200
|
+
if (typeof result === "function") {
|
|
201
|
+
if (e._cleanup) try {
|
|
202
|
+
e._cleanup();
|
|
203
|
+
} catch (err) {
|
|
204
|
+
}
|
|
205
|
+
e._cleanup = result;
|
|
206
|
+
}
|
|
207
|
+
} catch (err) {
|
|
208
|
+
if (__devtools?.onError) __devtools.onError(err, { type: "effect", effect: e });
|
|
209
|
+
if (__DEV__) console.warn("[what] Error in stable effect:", err);
|
|
210
|
+
} finally {
|
|
211
|
+
currentEffect = prev;
|
|
212
|
+
}
|
|
213
|
+
} else if (!e._pending) {
|
|
214
|
+
e._pending = true;
|
|
215
|
+
const level = e._level;
|
|
216
|
+
const len = pendingEffects.length;
|
|
217
|
+
if (len > 0 && pendingEffects[len - 1]._level > level) {
|
|
218
|
+
pendingNeedSort = true;
|
|
219
|
+
}
|
|
220
|
+
pendingEffects.push(e);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (notifyQueueLen > 0) {
|
|
224
|
+
let qi = 0;
|
|
225
|
+
while (qi < notifyQueueLen) {
|
|
226
|
+
const queuedSubs = notifyQueue[qi];
|
|
227
|
+
notifyQueue[qi] = null;
|
|
228
|
+
qi++;
|
|
229
|
+
for (const e of queuedSubs) {
|
|
230
|
+
if (e.disposed) continue;
|
|
231
|
+
if (e._onNotify) {
|
|
232
|
+
e._onNotify();
|
|
233
|
+
} else if (batchDepth === 0 && e._stable) {
|
|
234
|
+
const prev = currentEffect;
|
|
235
|
+
currentEffect = null;
|
|
236
|
+
try {
|
|
237
|
+
const result = e.fn();
|
|
238
|
+
if (typeof result === "function") {
|
|
239
|
+
if (e._cleanup) try {
|
|
240
|
+
e._cleanup();
|
|
241
|
+
} catch (err) {
|
|
242
|
+
}
|
|
243
|
+
e._cleanup = result;
|
|
244
|
+
}
|
|
245
|
+
} catch (err) {
|
|
246
|
+
if (__devtools?.onError) __devtools.onError(err, { type: "effect", effect: e });
|
|
247
|
+
if (__DEV__) console.warn("[what] Error in stable effect:", err);
|
|
248
|
+
} finally {
|
|
249
|
+
currentEffect = prev;
|
|
250
|
+
}
|
|
251
|
+
} else if (!e._pending) {
|
|
252
|
+
e._pending = true;
|
|
253
|
+
const level = e._level;
|
|
254
|
+
const len = pendingEffects.length;
|
|
255
|
+
if (len > 0 && pendingEffects[len - 1]._level > level) {
|
|
256
|
+
pendingNeedSort = true;
|
|
257
|
+
}
|
|
258
|
+
pendingEffects.push(e);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
notifyQueueLen = 0;
|
|
263
|
+
}
|
|
264
|
+
} finally {
|
|
265
|
+
notifyDepth = 0;
|
|
266
|
+
}
|
|
267
|
+
if (batchDepth === 0 && pendingEffects.length > 0) scheduleMicrotask();
|
|
268
|
+
} else {
|
|
269
|
+
if (notifyQueue === null) notifyQueue = [];
|
|
270
|
+
if (notifyQueueLen >= notifyQueue.length) {
|
|
271
|
+
notifyQueue.push(subs);
|
|
272
|
+
} else {
|
|
273
|
+
notifyQueue[notifyQueueLen] = subs;
|
|
274
|
+
}
|
|
275
|
+
notifyQueueLen++;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
var microtaskScheduled = false;
|
|
279
|
+
function scheduleMicrotask() {
|
|
280
|
+
if (!microtaskScheduled) {
|
|
281
|
+
microtaskScheduled = true;
|
|
282
|
+
queueMicrotask(() => {
|
|
283
|
+
microtaskScheduled = false;
|
|
284
|
+
flush();
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
var isFlushing = false;
|
|
289
|
+
function flush() {
|
|
290
|
+
if (isFlushing) return;
|
|
291
|
+
isFlushing = true;
|
|
292
|
+
try {
|
|
293
|
+
let iterations = 0;
|
|
294
|
+
while (pendingEffects.length > 0 && iterations < 25) {
|
|
295
|
+
const batch2 = pendingEffects;
|
|
296
|
+
pendingEffects = [];
|
|
297
|
+
if (batch2.length > 1 && pendingNeedSort) {
|
|
298
|
+
batch2.sort((a, b) => a._level - b._level);
|
|
299
|
+
}
|
|
300
|
+
pendingNeedSort = false;
|
|
301
|
+
for (let i = 0; i < batch2.length; i++) {
|
|
302
|
+
const e = batch2[i];
|
|
303
|
+
e._pending = false;
|
|
304
|
+
if (!e.disposed && !e._onNotify) {
|
|
305
|
+
const prevDepsLen = e.deps.length;
|
|
306
|
+
_runEffect(e);
|
|
307
|
+
if (!e._computed && e.deps.length !== prevDepsLen) {
|
|
308
|
+
_updateLevel(e);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
iterations++;
|
|
313
|
+
}
|
|
314
|
+
if (iterations >= 25) {
|
|
315
|
+
for (let i = 0; i < pendingEffects.length; i++) pendingEffects[i]._pending = false;
|
|
316
|
+
pendingEffects.length = 0;
|
|
317
|
+
if (__DEV__) {
|
|
318
|
+
const remaining = pendingEffects.slice(0, 3);
|
|
319
|
+
const effectNames = remaining.map((e) => e.fn?.name || e.fn?.toString().slice(0, 60) || "(anonymous)");
|
|
320
|
+
console.warn(
|
|
321
|
+
`[what] Possible infinite effect loop detected (25 iterations). Likely cause: an effect writes to a signal it also reads, creating a cycle. Use untrack() to read signals without subscribing. Looping effects: ${effectNames.join(", ")}`
|
|
322
|
+
);
|
|
323
|
+
} else {
|
|
324
|
+
console.warn("[what] Possible infinite effect loop detected");
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
} finally {
|
|
328
|
+
isFlushing = false;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function untrack(fn) {
|
|
332
|
+
const prev = currentEffect;
|
|
333
|
+
currentEffect = null;
|
|
334
|
+
try {
|
|
335
|
+
return fn();
|
|
336
|
+
} finally {
|
|
337
|
+
currentEffect = prev;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function createRoot(fn) {
|
|
341
|
+
const prevRoot = currentRoot;
|
|
342
|
+
const prevOwner = currentOwner;
|
|
343
|
+
const root = {
|
|
344
|
+
disposals: [],
|
|
345
|
+
owner: currentOwner,
|
|
346
|
+
// parent owner for ownership tree
|
|
347
|
+
children: [],
|
|
348
|
+
// child roots (ownership tree)
|
|
349
|
+
_disposed: false
|
|
350
|
+
};
|
|
351
|
+
if (currentOwner) {
|
|
352
|
+
currentOwner.children.push(root);
|
|
353
|
+
}
|
|
354
|
+
currentRoot = root;
|
|
355
|
+
currentOwner = root;
|
|
356
|
+
try {
|
|
357
|
+
const dispose = () => {
|
|
358
|
+
if (root._disposed) return;
|
|
359
|
+
root._disposed = true;
|
|
360
|
+
for (let i = root.children.length - 1; i >= 0; i--) {
|
|
361
|
+
_disposeRoot(root.children[i]);
|
|
362
|
+
}
|
|
363
|
+
root.children.length = 0;
|
|
364
|
+
for (let i = root.disposals.length - 1; i >= 0; i--) {
|
|
365
|
+
root.disposals[i]();
|
|
366
|
+
}
|
|
367
|
+
root.disposals.length = 0;
|
|
368
|
+
if (root.owner) {
|
|
369
|
+
const idx = root.owner.children.indexOf(root);
|
|
370
|
+
if (idx >= 0) root.owner.children.splice(idx, 1);
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
return fn(dispose);
|
|
374
|
+
} finally {
|
|
375
|
+
currentRoot = prevRoot;
|
|
376
|
+
currentOwner = prevOwner;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function _disposeRoot(root) {
|
|
380
|
+
if (root._disposed) return;
|
|
381
|
+
root._disposed = true;
|
|
382
|
+
for (let i = root.children.length - 1; i >= 0; i--) {
|
|
383
|
+
_disposeRoot(root.children[i]);
|
|
384
|
+
}
|
|
385
|
+
root.children.length = 0;
|
|
386
|
+
for (let i = root.disposals.length - 1; i >= 0; i--) {
|
|
387
|
+
root.disposals[i]();
|
|
388
|
+
}
|
|
389
|
+
root.disposals.length = 0;
|
|
15
390
|
}
|
|
16
391
|
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return textNode;
|
|
392
|
+
// packages/core/src/components.js
|
|
393
|
+
var _getCurrentComponent = null;
|
|
394
|
+
function _injectGetCurrentComponent(fn) {
|
|
395
|
+
_getCurrentComponent = fn;
|
|
396
|
+
}
|
|
397
|
+
function reportError(error, startCtx) {
|
|
398
|
+
let ctx = startCtx || _getCurrentComponent?.();
|
|
399
|
+
while (ctx) {
|
|
400
|
+
if (ctx._errorBoundary) {
|
|
401
|
+
ctx._errorBoundary(error);
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
ctx = ctx._parentCtx;
|
|
31
405
|
}
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
32
408
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
409
|
+
// packages/core/src/helpers.js
|
|
410
|
+
var _getCurrentComponentRef = null;
|
|
411
|
+
function _setComponentRef(fn) {
|
|
412
|
+
_getCurrentComponentRef = fn;
|
|
413
|
+
}
|
|
37
414
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
415
|
+
// packages/core/src/dom.js
|
|
416
|
+
var SVG_ELEMENTS = /* @__PURE__ */ new Set([
|
|
417
|
+
"svg",
|
|
418
|
+
"path",
|
|
419
|
+
"circle",
|
|
420
|
+
"rect",
|
|
421
|
+
"line",
|
|
422
|
+
"polyline",
|
|
423
|
+
"polygon",
|
|
424
|
+
"ellipse",
|
|
425
|
+
"g",
|
|
426
|
+
"defs",
|
|
427
|
+
"use",
|
|
428
|
+
"symbol",
|
|
429
|
+
"clipPath",
|
|
430
|
+
"mask",
|
|
431
|
+
"pattern",
|
|
432
|
+
"image",
|
|
433
|
+
"text",
|
|
434
|
+
"tspan",
|
|
435
|
+
"textPath",
|
|
436
|
+
"foreignObject",
|
|
437
|
+
"linearGradient",
|
|
438
|
+
"radialGradient",
|
|
439
|
+
"stop",
|
|
440
|
+
"marker",
|
|
441
|
+
"animate",
|
|
442
|
+
"animateTransform",
|
|
443
|
+
"animateMotion",
|
|
444
|
+
"set",
|
|
445
|
+
"filter",
|
|
446
|
+
"feBlend",
|
|
447
|
+
"feColorMatrix",
|
|
448
|
+
"feComponentTransfer",
|
|
449
|
+
"feComposite",
|
|
450
|
+
"feConvolveMatrix",
|
|
451
|
+
"feDiffuseLighting",
|
|
452
|
+
"feDisplacementMap",
|
|
453
|
+
"feFlood",
|
|
454
|
+
"feGaussianBlur",
|
|
455
|
+
"feImage",
|
|
456
|
+
"feMerge",
|
|
457
|
+
"feMergeNode",
|
|
458
|
+
"feMorphology",
|
|
459
|
+
"feOffset",
|
|
460
|
+
"feSpecularLighting",
|
|
461
|
+
"feTile",
|
|
462
|
+
"feTurbulence"
|
|
463
|
+
]);
|
|
464
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
465
|
+
var mountedComponents = /* @__PURE__ */ new Set();
|
|
466
|
+
var _commentCtxMap = /* @__PURE__ */ new WeakMap();
|
|
467
|
+
function isDomNode(value) {
|
|
468
|
+
if (!value || typeof value !== "object") return false;
|
|
469
|
+
if (typeof Node !== "undefined" && value instanceof Node) return true;
|
|
470
|
+
return typeof value.nodeType === "number" && typeof value.nodeName === "string";
|
|
471
|
+
}
|
|
472
|
+
function isVNode(value) {
|
|
473
|
+
return !!value && typeof value === "object" && (value._vnode === true || "tag" in value);
|
|
474
|
+
}
|
|
475
|
+
function disposeComponent(ctx) {
|
|
476
|
+
if (ctx.disposed) return;
|
|
477
|
+
ctx.disposed = true;
|
|
478
|
+
if (ctx.cleanups) {
|
|
479
|
+
for (const cleanup2 of ctx.cleanups) {
|
|
480
|
+
try {
|
|
481
|
+
cleanup2();
|
|
482
|
+
} catch (e) {
|
|
483
|
+
console.error("[what] cleanup error:", e);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (ctx.effects) {
|
|
488
|
+
for (const dispose of ctx.effects) {
|
|
489
|
+
try {
|
|
490
|
+
dispose();
|
|
491
|
+
} catch (e) {
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (ctx.hooks) {
|
|
496
|
+
for (const hook of ctx.hooks) {
|
|
497
|
+
if (hook && typeof hook.cleanup === "function") {
|
|
498
|
+
try {
|
|
499
|
+
hook.cleanup();
|
|
500
|
+
} catch (e) {
|
|
501
|
+
console.error("[what] hook cleanup error:", e);
|
|
45
502
|
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
if (ctx._cleanupCallbacks) {
|
|
507
|
+
for (const fn of ctx._cleanupCallbacks) {
|
|
508
|
+
try {
|
|
509
|
+
fn();
|
|
510
|
+
} catch (e) {
|
|
511
|
+
console.error("[what] onCleanup error:", e);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if (__DEV__ && __devtools?.onComponentUnmount) __devtools.onComponentUnmount(ctx);
|
|
516
|
+
mountedComponents.delete(ctx);
|
|
517
|
+
}
|
|
518
|
+
function disposeTree(node) {
|
|
519
|
+
if (!node) return;
|
|
520
|
+
if (node._componentCtx) {
|
|
521
|
+
disposeComponent(node._componentCtx);
|
|
522
|
+
}
|
|
523
|
+
const commentCtx = _commentCtxMap.get(node);
|
|
524
|
+
if (commentCtx) {
|
|
525
|
+
disposeComponent(commentCtx);
|
|
526
|
+
}
|
|
527
|
+
if (node._dispose) {
|
|
528
|
+
try {
|
|
529
|
+
node._dispose();
|
|
530
|
+
} catch (e) {
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
if (node._propEffects) {
|
|
534
|
+
for (const key in node._propEffects) {
|
|
535
|
+
try {
|
|
536
|
+
node._propEffects[key]();
|
|
537
|
+
} catch (e) {
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (node.childNodes) {
|
|
542
|
+
for (const child of node.childNodes) {
|
|
543
|
+
disposeTree(child);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function createDOM(vnode, parent, isSvg) {
|
|
548
|
+
if (vnode == null || vnode === false || vnode === true) {
|
|
549
|
+
return document.createComment("");
|
|
550
|
+
}
|
|
551
|
+
if (typeof vnode === "string" || typeof vnode === "number") {
|
|
552
|
+
return document.createTextNode(String(vnode));
|
|
553
|
+
}
|
|
554
|
+
if (isDomNode(vnode)) {
|
|
555
|
+
return vnode;
|
|
556
|
+
}
|
|
557
|
+
if (typeof vnode === "function") {
|
|
558
|
+
const startMarker = document.createComment("fn");
|
|
559
|
+
const endMarker = document.createComment("/fn");
|
|
560
|
+
let currentNodes = [];
|
|
561
|
+
const frag = document.createDocumentFragment();
|
|
562
|
+
frag.appendChild(startMarker);
|
|
563
|
+
frag.appendChild(endMarker);
|
|
564
|
+
const dispose = effect(() => {
|
|
565
|
+
const val = vnode();
|
|
566
|
+
const vnodes = val == null || val === false || val === true ? [] : Array.isArray(val) ? val : [val];
|
|
567
|
+
const realParent = endMarker.parentNode;
|
|
568
|
+
if (!realParent) return;
|
|
569
|
+
for (const old of currentNodes) {
|
|
570
|
+
disposeTree(old);
|
|
571
|
+
if (old.parentNode === realParent) realParent.removeChild(old);
|
|
572
|
+
}
|
|
573
|
+
currentNodes = [];
|
|
574
|
+
for (const v of vnodes) {
|
|
575
|
+
const node = createDOM(v, realParent, parent?._isSvg);
|
|
576
|
+
if (node) {
|
|
577
|
+
if (node.nodeType === 11) {
|
|
578
|
+
const children = Array.from(node.childNodes);
|
|
579
|
+
realParent.insertBefore(node, endMarker);
|
|
580
|
+
for (const child of children) currentNodes.push(child);
|
|
581
|
+
} else {
|
|
582
|
+
realParent.insertBefore(node, endMarker);
|
|
583
|
+
currentNodes.push(node);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
startMarker._dispose = dispose;
|
|
589
|
+
endMarker._dispose = dispose;
|
|
590
|
+
return frag;
|
|
591
|
+
}
|
|
592
|
+
if (Array.isArray(vnode)) {
|
|
593
|
+
const frag = document.createDocumentFragment();
|
|
594
|
+
for (const child of vnode) {
|
|
595
|
+
const node = createDOM(child, parent, isSvg);
|
|
596
|
+
if (node) frag.appendChild(node);
|
|
597
|
+
}
|
|
598
|
+
return frag;
|
|
599
|
+
}
|
|
600
|
+
if (isVNode(vnode) && typeof vnode.tag === "function") {
|
|
601
|
+
return createComponent(vnode, parent, isSvg);
|
|
602
|
+
}
|
|
603
|
+
if (isVNode(vnode)) {
|
|
604
|
+
return createElementFromVNode(vnode, parent, isSvg);
|
|
605
|
+
}
|
|
606
|
+
return document.createTextNode(String(vnode));
|
|
607
|
+
}
|
|
608
|
+
var componentStack = [];
|
|
609
|
+
function getCurrentComponent() {
|
|
610
|
+
return componentStack[componentStack.length - 1];
|
|
611
|
+
}
|
|
612
|
+
_injectGetCurrentComponent(getCurrentComponent);
|
|
613
|
+
_setComponentRef(getCurrentComponent);
|
|
614
|
+
function getComponentStack() {
|
|
615
|
+
return componentStack;
|
|
616
|
+
}
|
|
617
|
+
function createComponent(vnode, parent, isSvg) {
|
|
618
|
+
let { tag: Component, props, children } = vnode;
|
|
619
|
+
if (typeof Component === "function" && (Component.prototype?.isReactComponent || Component.prototype?.render)) {
|
|
620
|
+
const ClassComp = Component;
|
|
621
|
+
Component = function ClassComponentBridge(props2) {
|
|
622
|
+
const instance = new ClassComp(props2);
|
|
623
|
+
return instance.render();
|
|
624
|
+
};
|
|
625
|
+
Component.displayName = ClassComp.displayName || ClassComp.name || "ClassComponent";
|
|
626
|
+
}
|
|
627
|
+
if (Component === "__errorBoundary" || vnode.tag === "__errorBoundary") {
|
|
628
|
+
return createErrorBoundary(vnode, parent);
|
|
629
|
+
}
|
|
630
|
+
if (Component === "__suspense" || vnode.tag === "__suspense") {
|
|
631
|
+
return createSuspenseBoundary(vnode, parent);
|
|
632
|
+
}
|
|
633
|
+
if (Component === "__portal" || vnode.tag === "__portal") {
|
|
634
|
+
return createPortalDOM(vnode, parent);
|
|
635
|
+
}
|
|
636
|
+
const ctx = {
|
|
637
|
+
hooks: [],
|
|
638
|
+
hookIndex: 0,
|
|
639
|
+
effects: [],
|
|
640
|
+
cleanups: [],
|
|
641
|
+
mounted: false,
|
|
642
|
+
disposed: false,
|
|
643
|
+
Component,
|
|
644
|
+
_parentCtx: componentStack[componentStack.length - 1] || null,
|
|
645
|
+
_errorBoundary: (() => {
|
|
646
|
+
let p = componentStack[componentStack.length - 1];
|
|
647
|
+
while (p) {
|
|
648
|
+
if (p._errorBoundary) return p._errorBoundary;
|
|
649
|
+
p = p._parentCtx;
|
|
650
|
+
}
|
|
651
|
+
return null;
|
|
652
|
+
})()
|
|
653
|
+
};
|
|
654
|
+
const startComment = document.createComment("c:start");
|
|
655
|
+
const endComment = document.createComment("c:end");
|
|
656
|
+
_commentCtxMap.set(startComment, ctx);
|
|
657
|
+
ctx._startComment = startComment;
|
|
658
|
+
ctx._endComment = endComment;
|
|
659
|
+
const container = document.createDocumentFragment();
|
|
660
|
+
container._componentCtx = ctx;
|
|
661
|
+
ctx._wrapper = startComment;
|
|
662
|
+
mountedComponents.add(ctx);
|
|
663
|
+
if (__DEV__ && __devtools?.onComponentMount) __devtools.onComponentMount(ctx);
|
|
664
|
+
const propsChildren = children.length === 0 ? void 0 : children.length === 1 ? children[0] : children;
|
|
665
|
+
const propsSignal = signal({ ...props, children: propsChildren });
|
|
666
|
+
ctx._propsSignal = propsSignal;
|
|
667
|
+
const reactiveProps = new Proxy({}, {
|
|
668
|
+
get(_, key) {
|
|
669
|
+
const current = propsSignal();
|
|
670
|
+
return current[key];
|
|
671
|
+
},
|
|
672
|
+
has(_, key) {
|
|
673
|
+
const current = propsSignal();
|
|
674
|
+
return key in current;
|
|
675
|
+
},
|
|
676
|
+
ownKeys() {
|
|
677
|
+
const current = propsSignal();
|
|
678
|
+
return Reflect.ownKeys(current);
|
|
679
|
+
},
|
|
680
|
+
getOwnPropertyDescriptor(_, key) {
|
|
681
|
+
const current = propsSignal();
|
|
682
|
+
if (key in current) {
|
|
683
|
+
return { value: current[key], writable: false, enumerable: true, configurable: true };
|
|
684
|
+
}
|
|
685
|
+
return void 0;
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
componentStack.push(ctx);
|
|
689
|
+
let result;
|
|
690
|
+
try {
|
|
691
|
+
result = Component(reactiveProps);
|
|
692
|
+
} catch (error) {
|
|
693
|
+
componentStack.pop();
|
|
694
|
+
if (!reportError(error, ctx)) {
|
|
695
|
+
console.error("[what] Uncaught error in component:", Component.name || "Anonymous", error);
|
|
696
|
+
throw error;
|
|
697
|
+
}
|
|
698
|
+
container.appendChild(startComment);
|
|
699
|
+
container.appendChild(endComment);
|
|
700
|
+
return container;
|
|
701
|
+
}
|
|
702
|
+
componentStack.pop();
|
|
703
|
+
ctx.mounted = true;
|
|
704
|
+
if (ctx._mountCallbacks) {
|
|
705
|
+
queueMicrotask(() => {
|
|
706
|
+
if (ctx.disposed) return;
|
|
707
|
+
for (const fn of ctx._mountCallbacks) {
|
|
708
|
+
try {
|
|
709
|
+
fn();
|
|
710
|
+
} catch (e) {
|
|
711
|
+
console.error("[what] onMount error:", e);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
container.appendChild(startComment);
|
|
717
|
+
const vnodes = Array.isArray(result) ? result : [result];
|
|
718
|
+
for (const v of vnodes) {
|
|
719
|
+
const node = createDOM(v, container, isSvg);
|
|
720
|
+
if (node) container.appendChild(node);
|
|
721
|
+
}
|
|
722
|
+
container.appendChild(endComment);
|
|
723
|
+
return container;
|
|
724
|
+
}
|
|
725
|
+
function createErrorBoundary(vnode, parent) {
|
|
726
|
+
const { errorState, handleError, fallback, reset } = vnode.props;
|
|
727
|
+
const children = vnode.children;
|
|
728
|
+
const startComment = document.createComment("eb:start");
|
|
729
|
+
const endComment = document.createComment("eb:end");
|
|
730
|
+
const boundaryCtx = {
|
|
731
|
+
hooks: [],
|
|
732
|
+
hookIndex: 0,
|
|
733
|
+
effects: [],
|
|
734
|
+
cleanups: [],
|
|
735
|
+
mounted: false,
|
|
736
|
+
disposed: false,
|
|
737
|
+
_parentCtx: componentStack[componentStack.length - 1] || null,
|
|
738
|
+
_errorBoundary: handleError,
|
|
739
|
+
_startComment: startComment,
|
|
740
|
+
_endComment: endComment
|
|
741
|
+
};
|
|
742
|
+
_commentCtxMap.set(startComment, boundaryCtx);
|
|
743
|
+
const container = document.createDocumentFragment();
|
|
744
|
+
container._componentCtx = boundaryCtx;
|
|
745
|
+
container.appendChild(startComment);
|
|
746
|
+
container.appendChild(endComment);
|
|
747
|
+
const dispose = effect(() => {
|
|
748
|
+
const error = errorState();
|
|
749
|
+
componentStack.push(boundaryCtx);
|
|
750
|
+
if (startComment.parentNode) {
|
|
751
|
+
while (startComment.nextSibling && startComment.nextSibling !== endComment) {
|
|
752
|
+
const old = startComment.nextSibling;
|
|
753
|
+
disposeTree(old);
|
|
754
|
+
old.parentNode.removeChild(old);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
let vnodes;
|
|
758
|
+
if (error) {
|
|
759
|
+
vnodes = typeof fallback === "function" ? [fallback({ error, reset })] : [fallback];
|
|
760
|
+
} else {
|
|
761
|
+
vnodes = children;
|
|
762
|
+
}
|
|
763
|
+
vnodes = Array.isArray(vnodes) ? vnodes : [vnodes];
|
|
764
|
+
for (const v of vnodes) {
|
|
765
|
+
const node = createDOM(v, parent);
|
|
766
|
+
if (node) {
|
|
767
|
+
if (endComment.parentNode) {
|
|
768
|
+
endComment.parentNode.insertBefore(node, endComment);
|
|
769
|
+
} else {
|
|
770
|
+
container.insertBefore(node, endComment);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
componentStack.pop();
|
|
775
|
+
});
|
|
776
|
+
boundaryCtx.effects.push(dispose);
|
|
777
|
+
return container;
|
|
778
|
+
}
|
|
779
|
+
function createSuspenseBoundary(vnode, parent) {
|
|
780
|
+
const { boundary, fallback, loading } = vnode.props;
|
|
781
|
+
const children = vnode.children;
|
|
782
|
+
const startComment = document.createComment("sb:start");
|
|
783
|
+
const endComment = document.createComment("sb:end");
|
|
784
|
+
const boundaryCtx = {
|
|
785
|
+
hooks: [],
|
|
786
|
+
hookIndex: 0,
|
|
787
|
+
effects: [],
|
|
788
|
+
cleanups: [],
|
|
789
|
+
mounted: false,
|
|
790
|
+
disposed: false,
|
|
791
|
+
_parentCtx: componentStack[componentStack.length - 1] || null,
|
|
792
|
+
_startComment: startComment,
|
|
793
|
+
_endComment: endComment
|
|
794
|
+
};
|
|
795
|
+
_commentCtxMap.set(startComment, boundaryCtx);
|
|
796
|
+
const container = document.createDocumentFragment();
|
|
797
|
+
container._componentCtx = boundaryCtx;
|
|
798
|
+
container.appendChild(startComment);
|
|
799
|
+
container.appendChild(endComment);
|
|
800
|
+
const dispose = effect(() => {
|
|
801
|
+
const isLoading = loading();
|
|
802
|
+
const vnodes = isLoading ? [fallback] : children;
|
|
803
|
+
const normalized = Array.isArray(vnodes) ? vnodes : [vnodes];
|
|
804
|
+
componentStack.push(boundaryCtx);
|
|
805
|
+
if (startComment.parentNode) {
|
|
806
|
+
while (startComment.nextSibling && startComment.nextSibling !== endComment) {
|
|
807
|
+
const old = startComment.nextSibling;
|
|
808
|
+
disposeTree(old);
|
|
809
|
+
old.parentNode.removeChild(old);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
for (const v of normalized) {
|
|
813
|
+
const node = createDOM(v, parent);
|
|
814
|
+
if (node) {
|
|
815
|
+
if (endComment.parentNode) {
|
|
816
|
+
endComment.parentNode.insertBefore(node, endComment);
|
|
54
817
|
} else {
|
|
55
|
-
|
|
56
|
-
parent.replaceChild(textNode, currentNode);
|
|
57
|
-
currentNode = textNode;
|
|
818
|
+
container.insertBefore(node, endComment);
|
|
58
819
|
}
|
|
59
820
|
}
|
|
821
|
+
}
|
|
822
|
+
componentStack.pop();
|
|
823
|
+
});
|
|
824
|
+
boundaryCtx.effects.push(dispose);
|
|
825
|
+
return container;
|
|
826
|
+
}
|
|
827
|
+
function createPortalDOM(vnode, parent) {
|
|
828
|
+
const { container } = vnode.props;
|
|
829
|
+
const children = vnode.children;
|
|
830
|
+
if (!container) {
|
|
831
|
+
console.warn("[what] Portal: target container not found");
|
|
832
|
+
return document.createComment("portal:empty");
|
|
833
|
+
}
|
|
834
|
+
const portalCtx = {
|
|
835
|
+
hooks: [],
|
|
836
|
+
hookIndex: 0,
|
|
837
|
+
effects: [],
|
|
838
|
+
cleanups: [],
|
|
839
|
+
mounted: false,
|
|
840
|
+
disposed: false,
|
|
841
|
+
_parentCtx: componentStack[componentStack.length - 1] || null
|
|
842
|
+
};
|
|
843
|
+
const placeholder = document.createComment("portal");
|
|
844
|
+
placeholder._componentCtx = portalCtx;
|
|
845
|
+
const portalNodes = [];
|
|
846
|
+
for (const child of children) {
|
|
847
|
+
const node = createDOM(child, container);
|
|
848
|
+
if (node) {
|
|
849
|
+
container.appendChild(node);
|
|
850
|
+
portalNodes.push(node);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
portalCtx._cleanupCallbacks = [() => {
|
|
854
|
+
for (const node of portalNodes) {
|
|
855
|
+
disposeTree(node);
|
|
856
|
+
if (node.parentNode) node.parentNode.removeChild(node);
|
|
857
|
+
}
|
|
858
|
+
}];
|
|
859
|
+
return placeholder;
|
|
860
|
+
}
|
|
861
|
+
function createElementFromVNode(vnode, parent, isSvg) {
|
|
862
|
+
const { tag, props, children } = vnode;
|
|
863
|
+
const svgContext = isSvg || SVG_ELEMENTS.has(tag);
|
|
864
|
+
const el = svgContext ? document.createElementNS(SVG_NS, tag) : document.createElement(tag);
|
|
865
|
+
if (props) {
|
|
866
|
+
applyProps(el, props, {}, svgContext);
|
|
867
|
+
}
|
|
868
|
+
for (const child of children) {
|
|
869
|
+
const node = createDOM(child, el, svgContext && tag !== "foreignObject");
|
|
870
|
+
if (node) el.appendChild(node);
|
|
871
|
+
}
|
|
872
|
+
el._vnode = vnode;
|
|
873
|
+
return el;
|
|
874
|
+
}
|
|
875
|
+
function applyProps(el, newProps, oldProps, isSvg) {
|
|
876
|
+
newProps = newProps || {};
|
|
877
|
+
oldProps = oldProps || {};
|
|
878
|
+
for (const key in newProps) {
|
|
879
|
+
if (key === "key" || key === "children") continue;
|
|
880
|
+
if (key === "ref") {
|
|
881
|
+
if (typeof newProps.ref === "function") newProps.ref(el);
|
|
882
|
+
else if (newProps.ref) newProps.ref.current = el;
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
setProp(el, key, newProps[key], isSvg);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
function setProp(el, key, value, isSvg) {
|
|
889
|
+
if (typeof value === "function" && !(key.startsWith("on") && key.length > 2) && key !== "ref") {
|
|
890
|
+
if (!el._propEffects) el._propEffects = {};
|
|
891
|
+
if (el._propEffects[key]) {
|
|
892
|
+
try {
|
|
893
|
+
el._propEffects[key]();
|
|
894
|
+
} catch (e) {
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
el._propEffects[key] = effect(() => {
|
|
898
|
+
const resolved = value();
|
|
899
|
+
setProp(el, key, resolved, isSvg);
|
|
60
900
|
});
|
|
61
|
-
|
|
62
|
-
return currentNode;
|
|
901
|
+
return;
|
|
63
902
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
903
|
+
if (key.startsWith("on") && key.length > 2) {
|
|
904
|
+
let eventName = key.slice(2);
|
|
905
|
+
let useCapture = false;
|
|
906
|
+
if (eventName.endsWith("Capture")) {
|
|
907
|
+
eventName = eventName.slice(0, -7);
|
|
908
|
+
useCapture = true;
|
|
909
|
+
}
|
|
910
|
+
const event = eventName.toLowerCase();
|
|
911
|
+
const storageKey = useCapture ? event + "_capture" : event;
|
|
912
|
+
const old = el._events?.[storageKey];
|
|
913
|
+
if (old && old._original === value) return;
|
|
914
|
+
if (old) el.removeEventListener(event, old, useCapture);
|
|
915
|
+
if (value == null) return;
|
|
916
|
+
if (!el._events) el._events = {};
|
|
917
|
+
const wrappedHandler = (e) => {
|
|
918
|
+
if (!e.nativeEvent) e.nativeEvent = e;
|
|
919
|
+
return untrack(() => value(e));
|
|
920
|
+
};
|
|
921
|
+
wrappedHandler._original = value;
|
|
922
|
+
el._events[storageKey] = wrappedHandler;
|
|
923
|
+
const eventOpts = value._eventOpts;
|
|
924
|
+
el.addEventListener(event, wrappedHandler, eventOpts || useCapture || void 0);
|
|
925
|
+
return;
|
|
68
926
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
927
|
+
if (key === "className" || key === "class") {
|
|
928
|
+
if (isSvg) {
|
|
929
|
+
el.setAttribute("class", value || "");
|
|
930
|
+
} else {
|
|
931
|
+
el.className = value || "";
|
|
932
|
+
}
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
if (key === "style") {
|
|
936
|
+
if (typeof value === "string") {
|
|
937
|
+
el.style.cssText = value;
|
|
938
|
+
el._prevStyle = null;
|
|
939
|
+
} else if (typeof value === "object") {
|
|
940
|
+
const oldStyle = el._prevStyle || {};
|
|
941
|
+
for (const prop in oldStyle) {
|
|
942
|
+
if (!(prop in value)) el.style[prop] = "";
|
|
943
|
+
}
|
|
944
|
+
for (const prop in value) {
|
|
945
|
+
el.style[prop] = value[prop] ?? "";
|
|
946
|
+
}
|
|
947
|
+
el._prevStyle = { ...value };
|
|
948
|
+
}
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
if (key === "dangerouslySetInnerHTML") {
|
|
952
|
+
el.innerHTML = value?.__html ?? "";
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
if (key === "innerHTML") {
|
|
956
|
+
if (value == null) return;
|
|
957
|
+
if (value && typeof value === "object" && "__html" in value) {
|
|
958
|
+
el.innerHTML = value.__html ?? "";
|
|
959
|
+
} else {
|
|
960
|
+
if (__DEV__) {
|
|
961
|
+
console.warn(
|
|
962
|
+
"[what] innerHTML received a raw string. This is a security risk (XSS). Use innerHTML={{ __html: trustedString }} or dangerouslySetInnerHTML={{ __html: trustedString }} instead."
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
if (typeof value === "boolean") {
|
|
970
|
+
if (value) el.setAttribute(key, "");
|
|
971
|
+
else el.removeAttribute(key);
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
if (key.startsWith("data-") || key.startsWith("aria-")) {
|
|
975
|
+
el.setAttribute(key, value);
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
if (isSvg) {
|
|
979
|
+
if (value === false || value == null) {
|
|
980
|
+
el.removeAttribute(key);
|
|
981
|
+
} else {
|
|
982
|
+
el.setAttribute(key, value === true ? "" : String(value));
|
|
75
983
|
}
|
|
76
|
-
return
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
if (key in el) {
|
|
987
|
+
el[key] = value;
|
|
988
|
+
} else {
|
|
989
|
+
el.setAttribute(key, value);
|
|
77
990
|
}
|
|
78
991
|
}
|
|
79
992
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
993
|
+
// packages/core/src/render.js
|
|
994
|
+
function _$createComponent(Component, props, children) {
|
|
995
|
+
if (children && children.length > 0) {
|
|
996
|
+
const mergedChildren = children.length === 1 ? children[0] : children;
|
|
997
|
+
props = props ? { ...props, children: mergedChildren } : { children: mergedChildren };
|
|
998
|
+
}
|
|
999
|
+
return createDOM({ tag: Component, props: props || {}, children: children || [], key: null, _vnode: true });
|
|
1000
|
+
}
|
|
1001
|
+
var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "formAction"]);
|
|
1002
|
+
function isSafeUrl(url) {
|
|
1003
|
+
if (typeof url !== "string") return true;
|
|
1004
|
+
const normalized = url.trim().replace(/[\s\x00-\x1f]/g, "").toLowerCase();
|
|
1005
|
+
if (normalized.startsWith("javascript:")) return false;
|
|
1006
|
+
if (normalized.startsWith("data:")) return false;
|
|
1007
|
+
if (normalized.startsWith("vbscript:")) return false;
|
|
1008
|
+
return true;
|
|
1009
|
+
}
|
|
1010
|
+
var TABLE_WRAPPERS = {
|
|
1011
|
+
tr: { depth: 2, wrap: "<table><tbody>", unwrap: "</tbody></table>" },
|
|
1012
|
+
td: { depth: 3, wrap: "<table><tbody><tr>", unwrap: "</tr></tbody></table>" },
|
|
1013
|
+
th: { depth: 3, wrap: "<table><tbody><tr>", unwrap: "</tr></tbody></table>" },
|
|
1014
|
+
thead: { depth: 1, wrap: "<table>", unwrap: "</table>" },
|
|
1015
|
+
tbody: { depth: 1, wrap: "<table>", unwrap: "</table>" },
|
|
1016
|
+
tfoot: { depth: 1, wrap: "<table>", unwrap: "</table>" },
|
|
1017
|
+
colgroup: { depth: 1, wrap: "<table>", unwrap: "</table>" },
|
|
1018
|
+
col: { depth: 1, wrap: "<table>", unwrap: "</table>" },
|
|
1019
|
+
caption: { depth: 1, wrap: "<table>", unwrap: "</table>" }
|
|
1020
|
+
};
|
|
1021
|
+
var SVG_ELEMENTS2 = /* @__PURE__ */ new Set([
|
|
1022
|
+
"svg",
|
|
1023
|
+
"path",
|
|
1024
|
+
"circle",
|
|
1025
|
+
"rect",
|
|
1026
|
+
"line",
|
|
1027
|
+
"polyline",
|
|
1028
|
+
"polygon",
|
|
1029
|
+
"ellipse",
|
|
1030
|
+
"g",
|
|
1031
|
+
"defs",
|
|
1032
|
+
"use",
|
|
1033
|
+
"text",
|
|
1034
|
+
"tspan",
|
|
1035
|
+
"foreignObject",
|
|
1036
|
+
"clipPath",
|
|
1037
|
+
"mask",
|
|
1038
|
+
"pattern",
|
|
1039
|
+
"linearGradient",
|
|
1040
|
+
"radialGradient",
|
|
1041
|
+
"stop",
|
|
1042
|
+
"marker",
|
|
1043
|
+
"symbol",
|
|
1044
|
+
"image",
|
|
1045
|
+
"animate",
|
|
1046
|
+
"animateTransform",
|
|
1047
|
+
"animateMotion",
|
|
1048
|
+
"set",
|
|
1049
|
+
"filter",
|
|
1050
|
+
"feGaussianBlur",
|
|
1051
|
+
"feOffset",
|
|
1052
|
+
"feMerge",
|
|
1053
|
+
"feMergeNode",
|
|
1054
|
+
"feBlend",
|
|
1055
|
+
"feColorMatrix",
|
|
1056
|
+
"feComponentTransfer",
|
|
1057
|
+
"feComposite",
|
|
1058
|
+
"feConvolveMatrix",
|
|
1059
|
+
"feDiffuseLighting",
|
|
1060
|
+
"feDisplacementMap",
|
|
1061
|
+
"feFlood",
|
|
1062
|
+
"feImage",
|
|
1063
|
+
"feMorphology",
|
|
1064
|
+
"feSpecularLighting",
|
|
1065
|
+
"feTile",
|
|
1066
|
+
"feTurbulence",
|
|
1067
|
+
"feDistantLight",
|
|
1068
|
+
"fePointLight",
|
|
1069
|
+
"feSpotLight"
|
|
1070
|
+
]);
|
|
1071
|
+
function getLeadingTag(html) {
|
|
1072
|
+
const m = html.match(/^<([a-zA-Z][a-zA-Z0-9]*)/);
|
|
1073
|
+
return m ? m[1] : "";
|
|
1074
|
+
}
|
|
1075
|
+
function _$templateImpl(html) {
|
|
1076
|
+
const trimmed = html.trim();
|
|
1077
|
+
const tag = getLeadingTag(trimmed);
|
|
1078
|
+
if (SVG_ELEMENTS2.has(tag)) {
|
|
1079
|
+
return svgTemplate(trimmed);
|
|
1080
|
+
}
|
|
1081
|
+
const tableInfo = TABLE_WRAPPERS[tag];
|
|
1082
|
+
if (tableInfo) {
|
|
1083
|
+
const t2 = document.createElement("template");
|
|
1084
|
+
t2.innerHTML = tableInfo.wrap + trimmed + tableInfo.unwrap;
|
|
1085
|
+
return () => {
|
|
1086
|
+
let node = t2.content.firstChild;
|
|
1087
|
+
for (let i = 0; i < tableInfo.depth; i++) {
|
|
1088
|
+
node = node.firstChild;
|
|
1089
|
+
}
|
|
1090
|
+
return node.cloneNode(true);
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
const t = document.createElement("template");
|
|
1094
|
+
t.innerHTML = trimmed;
|
|
1095
|
+
return () => t.content.firstChild.cloneNode(true);
|
|
1096
|
+
}
|
|
1097
|
+
var _templateWarned = false;
|
|
1098
|
+
function template(html) {
|
|
1099
|
+
if (__DEV__ && !_templateWarned) {
|
|
1100
|
+
_templateWarned = true;
|
|
1101
|
+
console.warn(
|
|
1102
|
+
"[what] template() is a compiler internal. Use JSX instead. Direct calls with user input can lead to XSS vulnerabilities."
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
return _$templateImpl(html);
|
|
1106
|
+
}
|
|
1107
|
+
function svgTemplate(html) {
|
|
1108
|
+
const trimmed = html.trim();
|
|
1109
|
+
const tag = getLeadingTag(trimmed);
|
|
1110
|
+
if (tag === "svg") {
|
|
1111
|
+
const t2 = document.createElement("template");
|
|
1112
|
+
t2.innerHTML = trimmed;
|
|
1113
|
+
return () => t2.content.firstChild.cloneNode(true);
|
|
1114
|
+
}
|
|
1115
|
+
const t = document.createElement("template");
|
|
1116
|
+
t.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg">${trimmed}</svg>`;
|
|
1117
|
+
return () => t.content.firstChild.firstChild.cloneNode(true);
|
|
1118
|
+
}
|
|
1119
|
+
function insert(parent, child, marker) {
|
|
1120
|
+
if (typeof child === "function") {
|
|
1121
|
+
let current = null;
|
|
1122
|
+
effect(() => {
|
|
1123
|
+
current = reconcileInsert(parent, child(), current, marker || null);
|
|
1124
|
+
});
|
|
1125
|
+
return current;
|
|
1126
|
+
}
|
|
1127
|
+
return reconcileInsert(parent, child, null, marker || null);
|
|
1128
|
+
}
|
|
1129
|
+
function isDomNode2(value) {
|
|
1130
|
+
if (!value || typeof value !== "object") return false;
|
|
1131
|
+
if (typeof Node !== "undefined" && value instanceof Node) return true;
|
|
1132
|
+
return typeof value.nodeType === "number" && typeof value.nodeName === "string";
|
|
1133
|
+
}
|
|
1134
|
+
function isVNode2(value) {
|
|
1135
|
+
return !!value && typeof value === "object" && (value._vnode === true || "tag" in value);
|
|
1136
|
+
}
|
|
1137
|
+
function isSvgParent(parent) {
|
|
1138
|
+
return typeof SVGElement !== "undefined" && parent instanceof SVGElement && parent.tagName.toLowerCase() !== "foreignobject";
|
|
1139
|
+
}
|
|
1140
|
+
function asNodeArray(value) {
|
|
1141
|
+
if (value == null) return [];
|
|
1142
|
+
return Array.isArray(value) ? value : [value];
|
|
1143
|
+
}
|
|
1144
|
+
function valuesToNodes(value, parent, out) {
|
|
1145
|
+
if (value == null || typeof value === "boolean") return out;
|
|
1146
|
+
if (Array.isArray(value)) {
|
|
1147
|
+
for (let i = 0; i < value.length; i++) {
|
|
1148
|
+
valuesToNodes(value[i], parent, out);
|
|
88
1149
|
}
|
|
1150
|
+
return out;
|
|
1151
|
+
}
|
|
1152
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
1153
|
+
out.push(document.createTextNode(String(value)));
|
|
1154
|
+
return out;
|
|
1155
|
+
}
|
|
1156
|
+
if (isDomNode2(value)) {
|
|
1157
|
+
out.push(value);
|
|
1158
|
+
return out;
|
|
1159
|
+
}
|
|
1160
|
+
if (isVNode2(value)) {
|
|
1161
|
+
out.push(createDOM(value, parent, isSvgParent(parent)));
|
|
1162
|
+
return out;
|
|
89
1163
|
}
|
|
90
|
-
|
|
1164
|
+
out.push(document.createTextNode(String(value)));
|
|
1165
|
+
return out;
|
|
91
1166
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
1167
|
+
function sameNodeArray(a, b) {
|
|
1168
|
+
if (a.length !== b.length) return false;
|
|
1169
|
+
for (let i = 0; i < a.length; i++) {
|
|
1170
|
+
if (a[i] !== b[i]) return false;
|
|
1171
|
+
}
|
|
1172
|
+
return true;
|
|
1173
|
+
}
|
|
1174
|
+
function reconcileInsert(parent, value, current, marker) {
|
|
1175
|
+
if (!parent || typeof parent.insertBefore !== "function") {
|
|
1176
|
+
if (__DEV__) {
|
|
1177
|
+
console.warn("[what] reconcileInsert called with invalid parent:", parent);
|
|
1178
|
+
}
|
|
1179
|
+
return current;
|
|
1180
|
+
}
|
|
1181
|
+
const targetMarker = marker || null;
|
|
1182
|
+
if (value == null || typeof value === "boolean") {
|
|
1183
|
+
const oldNodes2 = asNodeArray(current);
|
|
1184
|
+
for (let i = 0; i < oldNodes2.length; i++) {
|
|
1185
|
+
const oldNode = oldNodes2[i];
|
|
1186
|
+
if (oldNode.parentNode === parent) {
|
|
1187
|
+
disposeTree(oldNode);
|
|
1188
|
+
parent.removeChild(oldNode);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
return null;
|
|
1192
|
+
}
|
|
1193
|
+
if ((typeof value === "string" || typeof value === "number") && current && !Array.isArray(current) && current.nodeType === 3) {
|
|
1194
|
+
const text = String(value);
|
|
1195
|
+
if (current.textContent !== text) current.textContent = text;
|
|
1196
|
+
return current;
|
|
1197
|
+
}
|
|
1198
|
+
const newNodes = valuesToNodes(value, parent, []);
|
|
1199
|
+
const oldNodes = asNodeArray(current);
|
|
1200
|
+
if (sameNodeArray(oldNodes, newNodes)) {
|
|
1201
|
+
return current;
|
|
1202
|
+
}
|
|
1203
|
+
const keep = new Set(newNodes);
|
|
1204
|
+
for (let i = 0; i < oldNodes.length; i++) {
|
|
1205
|
+
const oldNode = oldNodes[i];
|
|
1206
|
+
if (!keep.has(oldNode) && oldNode.parentNode === parent) {
|
|
1207
|
+
disposeTree(oldNode);
|
|
1208
|
+
parent.removeChild(oldNode);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
let ref = targetMarker;
|
|
1212
|
+
for (let i = newNodes.length - 1; i >= 0; i--) {
|
|
1213
|
+
const node = newNodes[i];
|
|
1214
|
+
if (node.parentNode !== parent || node.nextSibling !== ref) {
|
|
1215
|
+
if (ref && ref.parentNode !== parent) ref = null;
|
|
1216
|
+
if (ref) parent.insertBefore(node, ref);
|
|
1217
|
+
else parent.appendChild(node);
|
|
1218
|
+
}
|
|
1219
|
+
ref = node;
|
|
1220
|
+
}
|
|
1221
|
+
if (newNodes.length === 0) return null;
|
|
1222
|
+
return newNodes.length === 1 ? newNodes[0] : newNodes;
|
|
1223
|
+
}
|
|
1224
|
+
function mapArray(source, mapFn, options) {
|
|
103
1225
|
const keyFn = options?.key;
|
|
104
1226
|
const raw = options?.raw || false;
|
|
105
|
-
|
|
106
1227
|
return (parent, marker) => {
|
|
107
1228
|
let items = [];
|
|
108
1229
|
let mappedNodes = [];
|
|
109
1230
|
let disposeFns = [];
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const endMarker = document.createComment('/list');
|
|
1231
|
+
let keyedState = keyFn && !raw ? /* @__PURE__ */ new Map() : null;
|
|
1232
|
+
const endMarker = document.createComment("/list");
|
|
114
1233
|
parent.insertBefore(endMarker, marker || null);
|
|
115
|
-
|
|
116
1234
|
effect(() => {
|
|
117
1235
|
const newItems = source() || [];
|
|
118
1236
|
if (keyFn) {
|
|
@@ -122,33 +1240,31 @@ export function mapArray(source, mapFn, options) {
|
|
|
122
1240
|
}
|
|
123
1241
|
items = newItems.slice();
|
|
124
1242
|
});
|
|
125
|
-
|
|
126
1243
|
return endMarker;
|
|
127
1244
|
};
|
|
128
1245
|
}
|
|
129
|
-
|
|
130
1246
|
function reconcileList(parent, endMarker, oldItems, newItems, mappedNodes, disposeFns, mapFn) {
|
|
131
1247
|
const newLen = newItems.length;
|
|
132
1248
|
const oldLen = oldItems.length;
|
|
133
|
-
|
|
134
1249
|
if (newLen === 0) {
|
|
135
|
-
// Fast path: clear all
|
|
136
1250
|
if (oldLen > 0) {
|
|
137
|
-
for (let i = 0; i < oldLen; i++)
|
|
138
|
-
|
|
139
|
-
|
|
1251
|
+
for (let i = 0; i < oldLen; i++) {
|
|
1252
|
+
disposeFns[i]?.();
|
|
1253
|
+
if (mappedNodes[i]?.parentNode === parent) {
|
|
1254
|
+
disposeTree(mappedNodes[i]);
|
|
1255
|
+
parent.removeChild(mappedNodes[i]);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
140
1258
|
mappedNodes.length = 0;
|
|
141
1259
|
disposeFns.length = 0;
|
|
142
1260
|
}
|
|
143
1261
|
return;
|
|
144
1262
|
}
|
|
145
|
-
|
|
146
1263
|
if (oldLen === 0) {
|
|
147
|
-
// Fast path: all new
|
|
148
1264
|
const frag = document.createDocumentFragment();
|
|
149
1265
|
for (let i = 0; i < newLen; i++) {
|
|
150
1266
|
const item = newItems[i];
|
|
151
|
-
const node = createRoot(dispose => {
|
|
1267
|
+
const node = createRoot((dispose) => {
|
|
152
1268
|
disposeFns[i] = dispose;
|
|
153
1269
|
return mapFn(item, i);
|
|
154
1270
|
});
|
|
@@ -158,23 +1274,16 @@ function reconcileList(parent, endMarker, oldItems, newItems, mappedNodes, dispo
|
|
|
158
1274
|
parent.insertBefore(frag, endMarker);
|
|
159
1275
|
return;
|
|
160
1276
|
}
|
|
161
|
-
|
|
162
|
-
// --- Common prefix/suffix skip ---
|
|
163
1277
|
let start = 0;
|
|
164
1278
|
const minLen = Math.min(oldLen, newLen);
|
|
165
1279
|
while (start < minLen && oldItems[start] === newItems[start]) start++;
|
|
166
|
-
|
|
167
|
-
// If everything matches and same length, nothing changed
|
|
168
1280
|
if (start === oldLen && start === newLen) return;
|
|
169
|
-
|
|
170
1281
|
let oldEnd = oldLen - 1;
|
|
171
1282
|
let newEnd = newLen - 1;
|
|
172
1283
|
while (oldEnd >= start && newEnd >= start && oldItems[oldEnd] === newItems[newEnd]) {
|
|
173
1284
|
oldEnd--;
|
|
174
1285
|
newEnd--;
|
|
175
1286
|
}
|
|
176
|
-
|
|
177
|
-
// Copy prefix/suffix into output arrays
|
|
178
1287
|
const newMapped = new Array(newLen);
|
|
179
1288
|
const newDispose = new Array(newLen);
|
|
180
1289
|
for (let i = 0; i < start; i++) {
|
|
@@ -182,30 +1291,24 @@ function reconcileList(parent, endMarker, oldItems, newItems, mappedNodes, dispo
|
|
|
182
1291
|
newDispose[i] = disposeFns[i];
|
|
183
1292
|
}
|
|
184
1293
|
for (let i = newEnd + 1; i < newLen; i++) {
|
|
185
|
-
// Suffix items: same item, possibly different index offset
|
|
186
1294
|
const oldI = oldEnd + 1 + (i - newEnd - 1);
|
|
187
1295
|
newMapped[i] = mappedNodes[oldI];
|
|
188
1296
|
newDispose[i] = disposeFns[oldI];
|
|
189
1297
|
}
|
|
190
|
-
|
|
191
|
-
// Only reconcile the middle section: start..newEnd (new) vs start..oldEnd (old)
|
|
192
1298
|
const midNewLen = newEnd - start + 1;
|
|
193
1299
|
const midOldLen = oldEnd - start + 1;
|
|
194
|
-
|
|
195
1300
|
if (midNewLen === 0) {
|
|
196
|
-
// Only removals in the middle
|
|
197
1301
|
for (let i = start; i <= oldEnd; i++) {
|
|
198
1302
|
disposeFns[i]?.();
|
|
199
1303
|
if (mappedNodes[i]?.parentNode) mappedNodes[i].parentNode.removeChild(mappedNodes[i]);
|
|
200
1304
|
}
|
|
201
1305
|
} else if (midOldLen === 0) {
|
|
202
|
-
// Only insertions in the middle
|
|
203
1306
|
const marker = start < newLen && newMapped[newEnd + 1] ? newMapped[newEnd + 1] : endMarker;
|
|
204
1307
|
const frag = document.createDocumentFragment();
|
|
205
1308
|
for (let i = start; i <= newEnd; i++) {
|
|
206
1309
|
const item = newItems[i];
|
|
207
1310
|
const idx = i;
|
|
208
|
-
newMapped[i] = createRoot(dispose => {
|
|
1311
|
+
newMapped[i] = createRoot((dispose) => {
|
|
209
1312
|
newDispose[idx] = dispose;
|
|
210
1313
|
return mapFn(item, idx);
|
|
211
1314
|
});
|
|
@@ -213,12 +1316,21 @@ function reconcileList(parent, endMarker, oldItems, newItems, mappedNodes, dispo
|
|
|
213
1316
|
}
|
|
214
1317
|
parent.insertBefore(frag, marker);
|
|
215
1318
|
} else {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
1319
|
+
_reconcileMiddle(
|
|
1320
|
+
parent,
|
|
1321
|
+
endMarker,
|
|
1322
|
+
oldItems,
|
|
1323
|
+
newItems,
|
|
1324
|
+
mappedNodes,
|
|
1325
|
+
disposeFns,
|
|
1326
|
+
mapFn,
|
|
1327
|
+
start,
|
|
1328
|
+
oldEnd,
|
|
1329
|
+
newEnd,
|
|
1330
|
+
newMapped,
|
|
1331
|
+
newDispose
|
|
1332
|
+
);
|
|
219
1333
|
}
|
|
220
|
-
|
|
221
|
-
// Update arrays in place
|
|
222
1334
|
mappedNodes.length = newLen;
|
|
223
1335
|
disposeFns.length = newLen;
|
|
224
1336
|
for (let i = 0; i < newLen; i++) {
|
|
@@ -226,46 +1338,32 @@ function reconcileList(parent, endMarker, oldItems, newItems, mappedNodes, dispo
|
|
|
226
1338
|
disposeFns[i] = newDispose[i];
|
|
227
1339
|
}
|
|
228
1340
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
mapFn, start, oldEnd, newEnd, newMapped, newDispose) {
|
|
232
|
-
// Build index map only for the middle section
|
|
233
|
-
const oldIdxMap = new Map();
|
|
1341
|
+
function _reconcileMiddle(parent, endMarker, oldItems, newItems, mappedNodes, disposeFns, mapFn, start, oldEnd, newEnd, newMapped, newDispose) {
|
|
1342
|
+
const oldIdxMap = /* @__PURE__ */ new Map();
|
|
234
1343
|
for (let i = start; i <= oldEnd; i++) {
|
|
235
1344
|
oldIdxMap.set(oldItems[i], i);
|
|
236
1345
|
}
|
|
237
|
-
|
|
238
|
-
// Match old items to new positions, collect old indices for LIS
|
|
239
1346
|
const midLen = newEnd - start + 1;
|
|
240
|
-
const oldIndices = new Int32Array(midLen);
|
|
1347
|
+
const oldIndices = new Int32Array(midLen);
|
|
241
1348
|
oldIndices.fill(-1);
|
|
242
|
-
|
|
243
1349
|
for (let i = start; i <= newEnd; i++) {
|
|
244
1350
|
const oldIdx = oldIdxMap.get(newItems[i]);
|
|
245
|
-
if (oldIdx !==
|
|
1351
|
+
if (oldIdx !== void 0) {
|
|
246
1352
|
oldIdxMap.delete(newItems[i]);
|
|
247
1353
|
newMapped[i] = mappedNodes[oldIdx];
|
|
248
1354
|
newDispose[i] = disposeFns[oldIdx];
|
|
249
1355
|
oldIndices[i - start] = oldIdx;
|
|
250
1356
|
}
|
|
251
1357
|
}
|
|
252
|
-
|
|
253
|
-
// Dispose removed items
|
|
254
1358
|
for (const [, oldIdx] of oldIdxMap) {
|
|
255
1359
|
disposeFns[oldIdx]?.();
|
|
256
1360
|
if (mappedNodes[oldIdx]?.parentNode) mappedNodes[oldIdx].parentNode.removeChild(mappedNodes[oldIdx]);
|
|
257
1361
|
}
|
|
258
|
-
|
|
259
|
-
// Compute LIS on old indices of reused items
|
|
260
|
-
// Build the sequence of old indices for reused items only
|
|
261
1362
|
const reusedCount = midLen - _countNeg1(oldIndices, midLen);
|
|
262
|
-
|
|
263
|
-
// Use a bitfield (via Uint8Array) to mark LIS positions — avoids Set overhead
|
|
264
1363
|
const inLIS = new Uint8Array(midLen);
|
|
265
|
-
|
|
266
1364
|
if (reusedCount > 1) {
|
|
267
1365
|
const seq = new Int32Array(reusedCount);
|
|
268
|
-
const seqToMid = new Int32Array(reusedCount);
|
|
1366
|
+
const seqToMid = new Int32Array(reusedCount);
|
|
269
1367
|
let k = 0;
|
|
270
1368
|
for (let i = 0; i < midLen; i++) {
|
|
271
1369
|
if (oldIndices[i] !== -1) {
|
|
@@ -279,56 +1377,46 @@ function _reconcileMiddle(parent, endMarker, oldItems, newItems, mappedNodes, di
|
|
|
279
1377
|
inLIS[seqToMid[lisResult[i]]] = 1;
|
|
280
1378
|
}
|
|
281
1379
|
} else if (reusedCount === 1) {
|
|
282
|
-
// Single reused item is trivially in LIS
|
|
283
1380
|
for (let i = 0; i < midLen; i++) {
|
|
284
|
-
if (oldIndices[i] !== -1) {
|
|
1381
|
+
if (oldIndices[i] !== -1) {
|
|
1382
|
+
inLIS[i] = 1;
|
|
1383
|
+
break;
|
|
1384
|
+
}
|
|
285
1385
|
}
|
|
286
1386
|
}
|
|
287
|
-
|
|
288
|
-
// Create new items
|
|
289
1387
|
for (let i = start; i <= newEnd; i++) {
|
|
290
1388
|
if (!newMapped[i]) {
|
|
291
1389
|
const item = newItems[i];
|
|
292
1390
|
const idx = i;
|
|
293
|
-
newMapped[i] = createRoot(dispose => {
|
|
1391
|
+
newMapped[i] = createRoot((dispose) => {
|
|
294
1392
|
newDispose[idx] = dispose;
|
|
295
1393
|
return mapFn(item, idx);
|
|
296
1394
|
});
|
|
297
1395
|
}
|
|
298
1396
|
}
|
|
299
|
-
|
|
300
|
-
// Position: work backwards from the item after newEnd (suffix start or endMarker)
|
|
301
|
-
let nextSibling = newEnd + 1 < newMapped.length && newMapped[newEnd + 1]
|
|
302
|
-
? newMapped[newEnd + 1] : endMarker;
|
|
303
|
-
|
|
1397
|
+
let nextSibling = newEnd + 1 < newMapped.length && newMapped[newEnd + 1] ? newMapped[newEnd + 1] : endMarker;
|
|
304
1398
|
for (let i = newEnd; i >= start; i--) {
|
|
305
1399
|
const mi = i - start;
|
|
306
1400
|
if (oldIndices[mi] === -1 || !inLIS[mi]) {
|
|
307
|
-
|
|
1401
|
+
if (nextSibling && nextSibling.parentNode !== parent) nextSibling = endMarker;
|
|
308
1402
|
parent.insertBefore(newMapped[i], nextSibling);
|
|
309
1403
|
}
|
|
310
1404
|
nextSibling = newMapped[i];
|
|
311
1405
|
}
|
|
312
1406
|
}
|
|
313
|
-
|
|
314
1407
|
function _countNeg1(arr, len) {
|
|
315
1408
|
let c = 0;
|
|
316
1409
|
for (let i = 0; i < len; i++) if (arr[i] === -1) c++;
|
|
317
1410
|
return c;
|
|
318
1411
|
}
|
|
319
|
-
|
|
320
|
-
// Longest Increasing Subsequence — returns indices into the input array.
|
|
321
|
-
// O(n log n) using patience sorting. Uses typed arrays for performance.
|
|
322
1412
|
function _lis(arr, len) {
|
|
323
1413
|
if (len === 0) return [];
|
|
324
1414
|
if (len === 1) return [0];
|
|
325
|
-
|
|
326
|
-
const tails = new Int32Array(len); // indices into arr
|
|
1415
|
+
const tails = new Int32Array(len);
|
|
327
1416
|
const predecessors = new Int32Array(len);
|
|
328
1417
|
let tailLen = 1;
|
|
329
1418
|
tails[0] = 0;
|
|
330
1419
|
predecessors[0] = -1;
|
|
331
|
-
|
|
332
1420
|
for (let i = 1; i < len; i++) {
|
|
333
1421
|
if (arr[i] > arr[tails[tailLen - 1]]) {
|
|
334
1422
|
predecessors[i] = tails[tailLen - 1];
|
|
@@ -336,7 +1424,7 @@ function _lis(arr, len) {
|
|
|
336
1424
|
} else {
|
|
337
1425
|
let lo = 0, hi = tailLen - 1;
|
|
338
1426
|
while (lo < hi) {
|
|
339
|
-
const mid =
|
|
1427
|
+
const mid = lo + hi >> 1;
|
|
340
1428
|
if (arr[tails[mid]] < arr[i]) lo = mid + 1;
|
|
341
1429
|
else hi = mid;
|
|
342
1430
|
}
|
|
@@ -344,7 +1432,6 @@ function _lis(arr, len) {
|
|
|
344
1432
|
predecessors[i] = lo > 0 ? tails[lo - 1] : -1;
|
|
345
1433
|
}
|
|
346
1434
|
}
|
|
347
|
-
|
|
348
1435
|
const result = new Array(tailLen);
|
|
349
1436
|
let k = tails[tailLen - 1];
|
|
350
1437
|
for (let i = tailLen - 1; i >= 0; i--) {
|
|
@@ -353,33 +1440,24 @@ function _lis(arr, len) {
|
|
|
353
1440
|
}
|
|
354
1441
|
return result;
|
|
355
1442
|
}
|
|
356
|
-
|
|
357
|
-
// --- reconcileKeyed ---
|
|
358
|
-
// Keyed reconciliation: tracks items by key function, not by reference.
|
|
359
|
-
// When a key persists but its item reference changes, the item signal updates
|
|
360
|
-
// in place — no DOM node destruction/creation. Only effects reading the
|
|
361
|
-
// item accessor re-run (e.g., textContent update for changed label).
|
|
362
|
-
|
|
363
1443
|
function reconcileKeyed(parent, endMarker, oldItems, newItems, mappedNodes, disposeFns, mapFn, keyFn, keyedState) {
|
|
364
1444
|
const newLen = newItems.length;
|
|
365
1445
|
const oldLen = oldItems.length;
|
|
366
|
-
|
|
367
|
-
// --- Fast path: clear all ---
|
|
368
1446
|
if (newLen === 0) {
|
|
369
1447
|
if (oldLen > 0) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
1448
|
+
for (let i = 0; i < oldLen; i++) {
|
|
1449
|
+
disposeFns[i]?.();
|
|
1450
|
+
if (mappedNodes[i]?.parentNode === parent) {
|
|
1451
|
+
disposeTree(mappedNodes[i]);
|
|
1452
|
+
parent.removeChild(mappedNodes[i]);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
375
1455
|
mappedNodes.length = 0;
|
|
376
1456
|
disposeFns.length = 0;
|
|
377
1457
|
if (keyedState) keyedState.clear();
|
|
378
1458
|
}
|
|
379
1459
|
return;
|
|
380
1460
|
}
|
|
381
|
-
|
|
382
|
-
// --- Fast path: all new ---
|
|
383
1461
|
if (oldLen === 0) {
|
|
384
1462
|
const frag = document.createDocumentFragment();
|
|
385
1463
|
for (let i = 0; i < newLen; i++) {
|
|
@@ -392,9 +1470,9 @@ function reconcileKeyed(parent, endMarker, oldItems, newItems, mappedNodes, disp
|
|
|
392
1470
|
accessor = itemSig;
|
|
393
1471
|
keyedState.set(key, { itemSig });
|
|
394
1472
|
} else {
|
|
395
|
-
accessor = item;
|
|
1473
|
+
accessor = item;
|
|
396
1474
|
}
|
|
397
|
-
const node = createRoot(dispose => {
|
|
1475
|
+
const node = createRoot((dispose) => {
|
|
398
1476
|
disposeFns[idx] = dispose;
|
|
399
1477
|
return mapFn(accessor, idx);
|
|
400
1478
|
});
|
|
@@ -404,26 +1482,27 @@ function reconcileKeyed(parent, endMarker, oldItems, newItems, mappedNodes, disp
|
|
|
404
1482
|
parent.insertBefore(frag, endMarker);
|
|
405
1483
|
return;
|
|
406
1484
|
}
|
|
407
|
-
|
|
408
|
-
// --- Common prefix: skip matching keys at the start ---
|
|
409
1485
|
let start = 0;
|
|
410
1486
|
const minLen = Math.min(oldLen, newLen);
|
|
411
1487
|
while (start < minLen) {
|
|
412
|
-
|
|
413
|
-
|
|
1488
|
+
if (oldItems[start] === newItems[start]) {
|
|
1489
|
+
start++;
|
|
1490
|
+
continue;
|
|
1491
|
+
}
|
|
414
1492
|
const oldKey = keyFn(oldItems[start]);
|
|
415
1493
|
const newKey = keyFn(newItems[start]);
|
|
416
1494
|
if (oldKey !== newKey) break;
|
|
417
|
-
// Key matches but reference changed — update signal (non-raw mode only)
|
|
418
1495
|
if (keyedState) keyedState.get(oldKey).itemSig.set(newItems[start]);
|
|
419
1496
|
start++;
|
|
420
1497
|
}
|
|
421
|
-
|
|
422
|
-
// --- Common suffix: skip matching keys at the end ---
|
|
423
1498
|
let oldEnd = oldLen - 1;
|
|
424
1499
|
let newEnd = newLen - 1;
|
|
425
1500
|
while (oldEnd >= start && newEnd >= start) {
|
|
426
|
-
if (oldItems[oldEnd] === newItems[newEnd]) {
|
|
1501
|
+
if (oldItems[oldEnd] === newItems[newEnd]) {
|
|
1502
|
+
oldEnd--;
|
|
1503
|
+
newEnd--;
|
|
1504
|
+
continue;
|
|
1505
|
+
}
|
|
427
1506
|
const oldKey = keyFn(oldItems[oldEnd]);
|
|
428
1507
|
const newKey = keyFn(newItems[newEnd]);
|
|
429
1508
|
if (oldKey !== newKey) break;
|
|
@@ -431,14 +1510,9 @@ function reconcileKeyed(parent, endMarker, oldItems, newItems, mappedNodes, disp
|
|
|
431
1510
|
oldEnd--;
|
|
432
1511
|
newEnd--;
|
|
433
1512
|
}
|
|
434
|
-
|
|
435
|
-
// If everything matched, nothing to do
|
|
436
1513
|
if (start > oldEnd && start > newEnd) {
|
|
437
|
-
// Just copy existing mappings to output
|
|
438
1514
|
return;
|
|
439
1515
|
}
|
|
440
|
-
|
|
441
|
-
// Copy prefix/suffix into output arrays
|
|
442
1516
|
const newMapped = new Array(newLen);
|
|
443
1517
|
const newDispose = new Array(newLen);
|
|
444
1518
|
for (let i = 0; i < start; i++) {
|
|
@@ -450,11 +1524,8 @@ function reconcileKeyed(parent, endMarker, oldItems, newItems, mappedNodes, disp
|
|
|
450
1524
|
newMapped[i] = mappedNodes[oldI];
|
|
451
1525
|
newDispose[i] = disposeFns[oldI];
|
|
452
1526
|
}
|
|
453
|
-
|
|
454
1527
|
const midNewLen = newEnd - start + 1;
|
|
455
1528
|
const midOldLen = oldEnd - start + 1;
|
|
456
|
-
|
|
457
|
-
// --- Only additions in middle ---
|
|
458
1529
|
if (midOldLen === 0) {
|
|
459
1530
|
const marker = newEnd + 1 < newLen && newMapped[newEnd + 1] ? newMapped[newEnd + 1] : endMarker;
|
|
460
1531
|
const frag = document.createDocumentFragment();
|
|
@@ -470,7 +1541,7 @@ function reconcileKeyed(parent, endMarker, oldItems, newItems, mappedNodes, disp
|
|
|
470
1541
|
} else {
|
|
471
1542
|
accessor = item;
|
|
472
1543
|
}
|
|
473
|
-
newMapped[i] = createRoot(dispose => {
|
|
1544
|
+
newMapped[i] = createRoot((dispose) => {
|
|
474
1545
|
newDispose[idx] = dispose;
|
|
475
1546
|
return mapFn(accessor, idx);
|
|
476
1547
|
});
|
|
@@ -480,8 +1551,6 @@ function reconcileKeyed(parent, endMarker, oldItems, newItems, mappedNodes, disp
|
|
|
480
1551
|
_copyBack(mappedNodes, disposeFns, newMapped, newDispose, newLen);
|
|
481
1552
|
return;
|
|
482
1553
|
}
|
|
483
|
-
|
|
484
|
-
// --- Only removals in middle ---
|
|
485
1554
|
if (midNewLen === 0) {
|
|
486
1555
|
for (let i = start; i <= oldEnd; i++) {
|
|
487
1556
|
disposeFns[i]?.();
|
|
@@ -491,41 +1560,30 @@ function reconcileKeyed(parent, endMarker, oldItems, newItems, mappedNodes, disp
|
|
|
491
1560
|
_copyBack(mappedNodes, disposeFns, newMapped, newDispose, newLen);
|
|
492
1561
|
return;
|
|
493
1562
|
}
|
|
494
|
-
|
|
495
|
-
// --- General case: reconcile middle section ---
|
|
496
|
-
// Build old key → old index map for middle section only
|
|
497
|
-
const oldKeyMap = new Map();
|
|
1563
|
+
const oldKeyMap = /* @__PURE__ */ new Map();
|
|
498
1564
|
for (let i = start; i <= oldEnd; i++) {
|
|
499
1565
|
oldKeyMap.set(keyFn(oldItems[i]), i);
|
|
500
1566
|
}
|
|
501
|
-
|
|
502
1567
|
const oldIndices = new Int32Array(midNewLen);
|
|
503
1568
|
oldIndices.fill(-1);
|
|
504
|
-
|
|
505
|
-
// Match by key
|
|
506
1569
|
for (let i = start; i <= newEnd; i++) {
|
|
507
1570
|
const key = keyFn(newItems[i]);
|
|
508
1571
|
const oldIdx = oldKeyMap.get(key);
|
|
509
|
-
if (oldIdx !==
|
|
1572
|
+
if (oldIdx !== void 0) {
|
|
510
1573
|
oldKeyMap.delete(key);
|
|
511
1574
|
newMapped[i] = mappedNodes[oldIdx];
|
|
512
1575
|
newDispose[i] = disposeFns[oldIdx];
|
|
513
1576
|
oldIndices[i - start] = oldIdx;
|
|
514
|
-
// Update item signal if reference changed (non-raw mode only)
|
|
515
1577
|
if (keyedState && newItems[i] !== oldItems[oldIdx]) {
|
|
516
1578
|
keyedState.get(key).itemSig.set(newItems[i]);
|
|
517
1579
|
}
|
|
518
1580
|
}
|
|
519
1581
|
}
|
|
520
|
-
|
|
521
|
-
// Dispose removed items
|
|
522
1582
|
for (const [key, oldIdx] of oldKeyMap) {
|
|
523
1583
|
disposeFns[oldIdx]?.();
|
|
524
1584
|
if (mappedNodes[oldIdx]?.parentNode) parent.removeChild(mappedNodes[oldIdx]);
|
|
525
1585
|
if (keyedState) keyedState.delete(key);
|
|
526
1586
|
}
|
|
527
|
-
|
|
528
|
-
// Create new items
|
|
529
1587
|
for (let i = start; i <= newEnd; i++) {
|
|
530
1588
|
if (!newMapped[i]) {
|
|
531
1589
|
const item = newItems[i];
|
|
@@ -539,15 +1597,12 @@ function reconcileKeyed(parent, endMarker, oldItems, newItems, mappedNodes, disp
|
|
|
539
1597
|
} else {
|
|
540
1598
|
accessor = item;
|
|
541
1599
|
}
|
|
542
|
-
newMapped[i] = createRoot(dispose => {
|
|
1600
|
+
newMapped[i] = createRoot((dispose) => {
|
|
543
1601
|
newDispose[idx] = dispose;
|
|
544
1602
|
return mapFn(accessor, idx);
|
|
545
1603
|
});
|
|
546
1604
|
}
|
|
547
1605
|
}
|
|
548
|
-
|
|
549
|
-
// Position using LIS
|
|
550
|
-
// First check: are reused items already in order? (common for update-in-place)
|
|
551
1606
|
let reusedCount = 0;
|
|
552
1607
|
let alreadySorted = true;
|
|
553
1608
|
let lastOldIdx = -1;
|
|
@@ -558,11 +1613,8 @@ function reconcileKeyed(parent, endMarker, oldItems, newItems, mappedNodes, disp
|
|
|
558
1613
|
lastOldIdx = oldIndices[i];
|
|
559
1614
|
}
|
|
560
1615
|
}
|
|
561
|
-
|
|
562
1616
|
const inLIS = new Uint8Array(midNewLen);
|
|
563
|
-
|
|
564
1617
|
if (alreadySorted) {
|
|
565
|
-
// All reused items are in order — mark all as in LIS (no moves needed)
|
|
566
1618
|
for (let i = 0; i < midNewLen; i++) {
|
|
567
1619
|
if (oldIndices[i] !== -1) inLIS[i] = 1;
|
|
568
1620
|
}
|
|
@@ -583,25 +1635,23 @@ function reconcileKeyed(parent, endMarker, oldItems, newItems, mappedNodes, disp
|
|
|
583
1635
|
}
|
|
584
1636
|
} else if (reusedCount === 1) {
|
|
585
1637
|
for (let i = 0; i < midNewLen; i++) {
|
|
586
|
-
if (oldIndices[i] !== -1) {
|
|
1638
|
+
if (oldIndices[i] !== -1) {
|
|
1639
|
+
inLIS[i] = 1;
|
|
1640
|
+
break;
|
|
1641
|
+
}
|
|
587
1642
|
}
|
|
588
1643
|
}
|
|
589
|
-
|
|
590
|
-
// Position: work backwards, insert items not in LIS
|
|
591
|
-
let nextSibling = newEnd + 1 < newMapped.length && newMapped[newEnd + 1]
|
|
592
|
-
? newMapped[newEnd + 1] : endMarker;
|
|
593
|
-
|
|
1644
|
+
let nextSibling = newEnd + 1 < newMapped.length && newMapped[newEnd + 1] ? newMapped[newEnd + 1] : endMarker;
|
|
594
1645
|
for (let i = newEnd; i >= start; i--) {
|
|
595
1646
|
const mi = i - start;
|
|
596
1647
|
if (oldIndices[mi] === -1 || !inLIS[mi]) {
|
|
1648
|
+
if (nextSibling && nextSibling.parentNode !== parent) nextSibling = endMarker;
|
|
597
1649
|
parent.insertBefore(newMapped[i], nextSibling);
|
|
598
1650
|
}
|
|
599
1651
|
nextSibling = newMapped[i];
|
|
600
1652
|
}
|
|
601
|
-
|
|
602
1653
|
_copyBack(mappedNodes, disposeFns, newMapped, newDispose, newLen);
|
|
603
1654
|
}
|
|
604
|
-
|
|
605
1655
|
function _copyBack(mappedNodes, disposeFns, newMapped, newDispose, newLen) {
|
|
606
1656
|
mappedNodes.length = newLen;
|
|
607
1657
|
disposeFns.length = newLen;
|
|
@@ -610,58 +1660,76 @@ function _copyBack(mappedNodes, disposeFns, newMapped, newDispose, newLen) {
|
|
|
610
1660
|
disposeFns[i] = newDispose[i];
|
|
611
1661
|
}
|
|
612
1662
|
}
|
|
613
|
-
|
|
614
|
-
// --- spread(el, props) ---
|
|
615
|
-
// Fine-grained prop effects. Function props create individual effects.
|
|
616
|
-
// Event props use direct assignment.
|
|
617
|
-
|
|
618
|
-
export function spread(el, props) {
|
|
1663
|
+
function spread(el, props) {
|
|
619
1664
|
for (const key in props) {
|
|
620
1665
|
const value = props[key];
|
|
621
|
-
|
|
622
|
-
if (key.startsWith('on') && key.length > 2) {
|
|
623
|
-
// Event handler — direct assignment. Use $$name for delegated events.
|
|
1666
|
+
if (key.startsWith("on") && key.length > 2) {
|
|
624
1667
|
const event = key.slice(2).toLowerCase();
|
|
625
1668
|
el.addEventListener(event, value);
|
|
626
1669
|
continue;
|
|
627
1670
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
} else if (key ===
|
|
1671
|
+
if (typeof value === "function" && !key.startsWith("on")) {
|
|
1672
|
+
if (key === "class" || key === "className") {
|
|
1673
|
+
effect(() => {
|
|
1674
|
+
el.className = value() || "";
|
|
1675
|
+
});
|
|
1676
|
+
} else if (key === "style" && typeof value() === "object") {
|
|
634
1677
|
effect(() => {
|
|
635
1678
|
const styles = value();
|
|
636
1679
|
for (const prop in styles) {
|
|
637
|
-
el.style[prop] = styles[prop] ??
|
|
1680
|
+
el.style[prop] = styles[prop] ?? "";
|
|
638
1681
|
}
|
|
639
1682
|
});
|
|
640
1683
|
} else {
|
|
641
|
-
effect(() => {
|
|
1684
|
+
effect(() => {
|
|
1685
|
+
setProp2(el, key, value());
|
|
1686
|
+
});
|
|
642
1687
|
}
|
|
643
1688
|
} else {
|
|
644
|
-
|
|
645
|
-
setPropDirect(el, key, value);
|
|
1689
|
+
setProp2(el, key, value);
|
|
646
1690
|
}
|
|
647
1691
|
}
|
|
648
1692
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
1693
|
+
function setProp2(el, key, value) {
|
|
1694
|
+
if (key === "ref") {
|
|
1695
|
+
if (typeof value === "function") value(el);
|
|
1696
|
+
else if (value && typeof value === "object") value.current = el;
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
if (URL_ATTRS.has(key) || URL_ATTRS.has(key.toLowerCase())) {
|
|
1700
|
+
if (!isSafeUrl(value)) {
|
|
1701
|
+
if (typeof console !== "undefined") {
|
|
1702
|
+
console.warn(`[what] Blocked unsafe URL in "${key}" attribute: ${value}`);
|
|
1703
|
+
}
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
if (key === "class" || key === "className") {
|
|
1708
|
+
el.className = value || "";
|
|
1709
|
+
} else if (key === "dangerouslySetInnerHTML") {
|
|
1710
|
+
el.innerHTML = value?.__html ?? "";
|
|
1711
|
+
} else if (key === "innerHTML") {
|
|
1712
|
+
if (value && typeof value === "object" && "__html" in value) {
|
|
1713
|
+
el.innerHTML = value.__html ?? "";
|
|
1714
|
+
} else {
|
|
1715
|
+
if (typeof console !== "undefined" && value != null && value !== "") {
|
|
1716
|
+
console.warn(
|
|
1717
|
+
'[what] Plain string innerHTML is not allowed. Use { __html: "..." } or dangerouslySetInnerHTML={{ __html: "..." }} instead.'
|
|
1718
|
+
);
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
} else if (key === "style") {
|
|
1722
|
+
if (typeof value === "string") {
|
|
655
1723
|
el.style.cssText = value;
|
|
656
|
-
} else if (typeof value ===
|
|
1724
|
+
} else if (typeof value === "object") {
|
|
657
1725
|
for (const prop in value) {
|
|
658
|
-
el.style[prop] = value[prop] ??
|
|
1726
|
+
el.style[prop] = value[prop] ?? "";
|
|
659
1727
|
}
|
|
660
1728
|
}
|
|
661
|
-
} else if (key.startsWith(
|
|
1729
|
+
} else if (key.startsWith("data-") || key.startsWith("aria-")) {
|
|
662
1730
|
el.setAttribute(key, value);
|
|
663
|
-
} else if (typeof value ===
|
|
664
|
-
if (value) el.setAttribute(key,
|
|
1731
|
+
} else if (typeof value === "boolean") {
|
|
1732
|
+
if (value) el.setAttribute(key, "");
|
|
665
1733
|
else el.removeAttribute(key);
|
|
666
1734
|
} else if (key in el) {
|
|
667
1735
|
el[key] = value;
|
|
@@ -669,24 +1737,14 @@ function setPropDirect(el, key, value) {
|
|
|
669
1737
|
el.setAttribute(key, value);
|
|
670
1738
|
}
|
|
671
1739
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
// Event delegation: common events handled at document level.
|
|
675
|
-
// Handlers stored as el.$$click, el.$$input, etc.
|
|
676
|
-
// Single listener per event type on document — reduces listener count from N to 1.
|
|
677
|
-
|
|
678
|
-
const delegatedEvents = new Set();
|
|
679
|
-
|
|
680
|
-
export function delegateEvents(eventNames) {
|
|
1740
|
+
var delegatedEvents = /* @__PURE__ */ new Set();
|
|
1741
|
+
function delegateEvents(eventNames) {
|
|
681
1742
|
for (const name of eventNames) {
|
|
682
1743
|
if (delegatedEvents.has(name)) continue;
|
|
683
1744
|
delegatedEvents.add(name);
|
|
684
|
-
|
|
685
1745
|
document.addEventListener(name, (e) => {
|
|
686
1746
|
let node = e.target;
|
|
687
|
-
const key =
|
|
688
|
-
|
|
689
|
-
// Walk up the DOM tree looking for handlers
|
|
1747
|
+
const key = "$$" + name;
|
|
690
1748
|
while (node) {
|
|
691
1749
|
const handler = node[key];
|
|
692
1750
|
if (handler) {
|
|
@@ -698,19 +1756,238 @@ export function delegateEvents(eventNames) {
|
|
|
698
1756
|
});
|
|
699
1757
|
}
|
|
700
1758
|
}
|
|
701
|
-
|
|
702
|
-
// --- addEventListener helper for non-delegated events ---
|
|
703
|
-
export function on(el, event, handler) {
|
|
1759
|
+
function on(el, event, handler) {
|
|
704
1760
|
el.addEventListener(event, handler);
|
|
705
1761
|
return () => el.removeEventListener(event, handler);
|
|
706
1762
|
}
|
|
707
|
-
|
|
708
|
-
// --- className helper for conditional classes ---
|
|
709
|
-
export function classList(el, classes) {
|
|
1763
|
+
function classList(el, classes) {
|
|
710
1764
|
effect(() => {
|
|
711
1765
|
for (const name in classes) {
|
|
712
|
-
const value = typeof classes[name] ===
|
|
1766
|
+
const value = typeof classes[name] === "function" ? classes[name]() : classes[name];
|
|
713
1767
|
el.classList.toggle(name, !!value);
|
|
714
1768
|
}
|
|
715
1769
|
});
|
|
716
1770
|
}
|
|
1771
|
+
var _isHydrating = false;
|
|
1772
|
+
var _hydrationCursor = null;
|
|
1773
|
+
function isHydrating() {
|
|
1774
|
+
return _isHydrating;
|
|
1775
|
+
}
|
|
1776
|
+
function hydrate(vnode, container) {
|
|
1777
|
+
_isHydrating = true;
|
|
1778
|
+
_hydrationCursor = { parent: container, index: 0 };
|
|
1779
|
+
try {
|
|
1780
|
+
const result = hydrateNode(vnode, container);
|
|
1781
|
+
return result;
|
|
1782
|
+
} finally {
|
|
1783
|
+
_isHydrating = false;
|
|
1784
|
+
_hydrationCursor = null;
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
function claimNode(parent) {
|
|
1788
|
+
const children = parent.childNodes;
|
|
1789
|
+
while (_hydrationCursor.index < children.length) {
|
|
1790
|
+
const node = children[_hydrationCursor.index];
|
|
1791
|
+
if (node.nodeType === 8) {
|
|
1792
|
+
const text = node.textContent;
|
|
1793
|
+
if (text === "$" || text === "/$" || text === "[]" || text === "/[]") {
|
|
1794
|
+
_hydrationCursor.index++;
|
|
1795
|
+
continue;
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
_hydrationCursor.index++;
|
|
1799
|
+
return node;
|
|
1800
|
+
}
|
|
1801
|
+
return null;
|
|
1802
|
+
}
|
|
1803
|
+
function isDevMode() {
|
|
1804
|
+
return typeof process !== "undefined" && true;
|
|
1805
|
+
}
|
|
1806
|
+
function hydrateNode(vnode, parent) {
|
|
1807
|
+
if (vnode == null || typeof vnode === "boolean") {
|
|
1808
|
+
return null;
|
|
1809
|
+
}
|
|
1810
|
+
if (typeof vnode === "string" || typeof vnode === "number") {
|
|
1811
|
+
const existing = claimNode(parent);
|
|
1812
|
+
const text = String(vnode);
|
|
1813
|
+
if (existing && existing.nodeType === 3) {
|
|
1814
|
+
if (isDevMode() && existing.textContent !== text) {
|
|
1815
|
+
console.warn(
|
|
1816
|
+
`[what] Hydration mismatch: expected text "${text}", got "${existing.textContent}"`
|
|
1817
|
+
);
|
|
1818
|
+
existing.textContent = text;
|
|
1819
|
+
}
|
|
1820
|
+
return existing;
|
|
1821
|
+
}
|
|
1822
|
+
if (isDevMode()) {
|
|
1823
|
+
console.warn(
|
|
1824
|
+
`[what] Hydration mismatch: expected text node "${text}", got ${existing ? existing.nodeName : "nothing"}. Falling back to client render.`
|
|
1825
|
+
);
|
|
1826
|
+
}
|
|
1827
|
+
const textNode2 = document.createTextNode(text);
|
|
1828
|
+
if (existing) {
|
|
1829
|
+
parent.replaceChild(textNode2, existing);
|
|
1830
|
+
} else {
|
|
1831
|
+
parent.appendChild(textNode2);
|
|
1832
|
+
}
|
|
1833
|
+
return textNode2;
|
|
1834
|
+
}
|
|
1835
|
+
if (typeof vnode === "function") {
|
|
1836
|
+
const initialValue = vnode();
|
|
1837
|
+
let current = hydrateNode(initialValue, parent);
|
|
1838
|
+
effect(() => {
|
|
1839
|
+
const value = vnode();
|
|
1840
|
+
if (!_isHydrating) {
|
|
1841
|
+
current = reconcileInsert(parent, value, current, null);
|
|
1842
|
+
}
|
|
1843
|
+
});
|
|
1844
|
+
return current;
|
|
1845
|
+
}
|
|
1846
|
+
if (Array.isArray(vnode)) {
|
|
1847
|
+
const nodes = [];
|
|
1848
|
+
for (const child of vnode) {
|
|
1849
|
+
const node = hydrateNode(child, parent);
|
|
1850
|
+
if (node) nodes.push(node);
|
|
1851
|
+
}
|
|
1852
|
+
return nodes.length === 1 ? nodes[0] : nodes;
|
|
1853
|
+
}
|
|
1854
|
+
if (typeof vnode === "object" && vnode._vnode) {
|
|
1855
|
+
if (typeof vnode.tag === "function") {
|
|
1856
|
+
const componentStack2 = getComponentStack();
|
|
1857
|
+
const Component = vnode.tag;
|
|
1858
|
+
const props = vnode.props || {};
|
|
1859
|
+
const children = vnode.children || [];
|
|
1860
|
+
const ctx = {
|
|
1861
|
+
hooks: [],
|
|
1862
|
+
hookIndex: 0,
|
|
1863
|
+
effects: [],
|
|
1864
|
+
cleanups: [],
|
|
1865
|
+
mounted: false,
|
|
1866
|
+
disposed: false,
|
|
1867
|
+
Component,
|
|
1868
|
+
_parentCtx: componentStack2[componentStack2.length - 1] || null,
|
|
1869
|
+
_errorBoundary: null
|
|
1870
|
+
};
|
|
1871
|
+
componentStack2.push(ctx);
|
|
1872
|
+
let result;
|
|
1873
|
+
try {
|
|
1874
|
+
const propsChildren = children.length === 0 ? void 0 : children.length === 1 ? children[0] : children;
|
|
1875
|
+
result = Component({ ...props, children: propsChildren });
|
|
1876
|
+
} catch (error) {
|
|
1877
|
+
componentStack2.pop();
|
|
1878
|
+
console.error("[what] Error in component during hydration:", Component.name || "Anonymous", error);
|
|
1879
|
+
return null;
|
|
1880
|
+
}
|
|
1881
|
+
componentStack2.pop();
|
|
1882
|
+
ctx.mounted = true;
|
|
1883
|
+
if (ctx._mountCallbacks) {
|
|
1884
|
+
queueMicrotask(() => {
|
|
1885
|
+
if (ctx.disposed) return;
|
|
1886
|
+
for (const fn of ctx._mountCallbacks) {
|
|
1887
|
+
try {
|
|
1888
|
+
fn();
|
|
1889
|
+
} catch (e) {
|
|
1890
|
+
console.error("[what] onMount error:", e);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1895
|
+
return hydrateNode(result, parent);
|
|
1896
|
+
}
|
|
1897
|
+
const existing = claimNode(parent);
|
|
1898
|
+
const expectedTag = vnode.tag.toUpperCase();
|
|
1899
|
+
if (existing && existing.nodeType === 1 && existing.nodeName === expectedTag) {
|
|
1900
|
+
hydrateElementProps(existing, vnode.props || {});
|
|
1901
|
+
const savedCursor = _hydrationCursor;
|
|
1902
|
+
_hydrationCursor = { parent: existing, index: 0 };
|
|
1903
|
+
const rawInner = vnode.props?.dangerouslySetInnerHTML?.__html;
|
|
1904
|
+
if (rawInner == null) {
|
|
1905
|
+
for (const child of vnode.children) {
|
|
1906
|
+
hydrateNode(child, existing);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
_hydrationCursor = savedCursor;
|
|
1910
|
+
return existing;
|
|
1911
|
+
}
|
|
1912
|
+
if (isDevMode()) {
|
|
1913
|
+
console.warn(
|
|
1914
|
+
`[what] Hydration mismatch: expected <${vnode.tag}>, got ${existing ? existing.nodeName : "nothing"}. Falling back to client render.`
|
|
1915
|
+
);
|
|
1916
|
+
}
|
|
1917
|
+
const newEl = document.createElement(vnode.tag);
|
|
1918
|
+
for (const key in vnode.props || {}) {
|
|
1919
|
+
if (key === "children" || key === "key") continue;
|
|
1920
|
+
setProp2(newEl, key, vnode.props[key]);
|
|
1921
|
+
}
|
|
1922
|
+
for (const child of vnode.children) {
|
|
1923
|
+
reconcileInsert(newEl, child, null, null);
|
|
1924
|
+
}
|
|
1925
|
+
if (existing) {
|
|
1926
|
+
parent.replaceChild(newEl, existing);
|
|
1927
|
+
} else {
|
|
1928
|
+
parent.appendChild(newEl);
|
|
1929
|
+
}
|
|
1930
|
+
return newEl;
|
|
1931
|
+
}
|
|
1932
|
+
if (isDomNode2(vnode)) {
|
|
1933
|
+
return vnode;
|
|
1934
|
+
}
|
|
1935
|
+
const textNode = document.createTextNode(String(vnode));
|
|
1936
|
+
parent.appendChild(textNode);
|
|
1937
|
+
return textNode;
|
|
1938
|
+
}
|
|
1939
|
+
function hydrateElementProps(el, props) {
|
|
1940
|
+
for (const key in props) {
|
|
1941
|
+
if (key === "children" || key === "key" || key === "ref") continue;
|
|
1942
|
+
if (key === "dangerouslySetInnerHTML" || key === "innerHTML") continue;
|
|
1943
|
+
const value = props[key];
|
|
1944
|
+
if (key.startsWith("on") && key.length > 2) {
|
|
1945
|
+
const event = key.slice(2).toLowerCase();
|
|
1946
|
+
el.addEventListener(event, value);
|
|
1947
|
+
continue;
|
|
1948
|
+
}
|
|
1949
|
+
if (key.startsWith("$$")) {
|
|
1950
|
+
el[key] = value;
|
|
1951
|
+
continue;
|
|
1952
|
+
}
|
|
1953
|
+
if (typeof value === "function" && !key.startsWith("on")) {
|
|
1954
|
+
if (key === "class" || key === "className") {
|
|
1955
|
+
effect(() => {
|
|
1956
|
+
el.className = value() || "";
|
|
1957
|
+
});
|
|
1958
|
+
} else if (key === "style" && typeof value() === "object") {
|
|
1959
|
+
effect(() => {
|
|
1960
|
+
const styles = value();
|
|
1961
|
+
for (const prop in styles) {
|
|
1962
|
+
el.style[prop] = styles[prop] ?? "";
|
|
1963
|
+
}
|
|
1964
|
+
});
|
|
1965
|
+
} else {
|
|
1966
|
+
effect(() => {
|
|
1967
|
+
setProp2(el, key, value());
|
|
1968
|
+
});
|
|
1969
|
+
}
|
|
1970
|
+
continue;
|
|
1971
|
+
}
|
|
1972
|
+
if (key === "data-hk") continue;
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
export {
|
|
1976
|
+
_$createComponent,
|
|
1977
|
+
_$templateImpl as _$template,
|
|
1978
|
+
template as _template,
|
|
1979
|
+
classList,
|
|
1980
|
+
delegateEvents,
|
|
1981
|
+
effect,
|
|
1982
|
+
hydrate,
|
|
1983
|
+
insert,
|
|
1984
|
+
isHydrating,
|
|
1985
|
+
mapArray,
|
|
1986
|
+
on,
|
|
1987
|
+
setProp2 as setProp,
|
|
1988
|
+
spread,
|
|
1989
|
+
svgTemplate,
|
|
1990
|
+
template,
|
|
1991
|
+
untrack
|
|
1992
|
+
};
|
|
1993
|
+
//# sourceMappingURL=render.js.map
|