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