@estjs/template 0.0.15 → 0.0.16-beta.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.
@@ -1,688 +1,461 @@
1
- import { error, isHTMLElement, isPrimitive, isFalsy, isNull, isObject, isArray, isHtmlInputElement, isHtmlSelectElement, isHtmlTextAreaElement, isFunction, hasChanged, isPromise, coerceArray, startsWith, isString, isBrowser, normalizeClassName, camelCase, capitalize, warn, isSpecialBooleanAttr, isBooleanAttr, includeBooleanAttr, isSymbol, isUndefined, isNumber, isTextNode } from '@estjs/shared';
2
- import { effect, shallowReactive, isSignal, isComputed, signal, memoEffect, untrack } from '@estjs/signals';
1
+ import { getActiveScope, runWithScope, onCleanup, __objRest, createScope, disposeScope, __async } from './chunk-3E4EK64L.dev.esm.js';
2
+ import { normalizeClassName, isObject, warn, isSpecialBooleanAttr, isBooleanAttr, includeBooleanAttr, isSymbol, isString, isArray, camelCase, capitalize, isBrowser, error, isPromise, isFunction, isOn, isNull, isUndefined, isNumber, isBoolean, coerceArray, isHTMLElement, isPrimitive, isFalsy } from '@estjs/shared';
3
+ import { effect, shallowReactive, isSignal, isComputed, signal } from '@estjs/signals';
3
4
 
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
- var __spreadValues = (a, b) => {
10
- for (var prop in b || (b = {}))
11
- if (__hasOwnProp.call(b, prop))
12
- __defNormalProp(a, prop, b[prop]);
13
- if (__getOwnPropSymbols)
14
- for (var prop of __getOwnPropSymbols(b)) {
15
- if (__propIsEnum.call(b, prop))
16
- __defNormalProp(a, prop, b[prop]);
17
- }
18
- return a;
19
- };
20
- var __async = (__this, __arguments, generator) => {
21
- return new Promise((resolve, reject) => {
22
- var fulfilled = (value) => {
23
- try {
24
- step(generator.next(value));
25
- } catch (e) {
26
- reject(e);
27
- }
28
- };
29
- var rejected = (value) => {
30
- try {
31
- step(generator.throw(value));
32
- } catch (e) {
33
- reject(e);
34
- }
35
- };
36
- var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
37
- step((generator = generator.apply(__this, __arguments)).next());
38
- });
39
- };
40
- var LIFECYCLE = {
41
- mount: "mount",
42
- destroy: "destroy",
43
- update: "update"
44
- };
45
- function registerScopedHook(scope, listKey, hook) {
46
- let hookList = scope[listKey];
47
- if (!hookList) {
48
- hookList = scope[listKey] = [];
49
- }
50
- if (!hookList.includes(hook)) {
51
- hookList.push(hook);
5
+ // src/constants.ts
6
+ var SPREAD_NAME = "_$spread$";
7
+ var REF_KEY = "ref";
8
+ var KEY_PROP = "key";
9
+ var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
10
+ var XLINK_NAMESPACE = "http://www.w3.org/2000/xlink";
11
+ var XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/";
12
+ var FRAGMENT_COMPONENT = /* @__PURE__ */ Symbol("Fragment Component" );
13
+ var PORTAL_COMPONENT = /* @__PURE__ */ Symbol("Portal Component" );
14
+ var SUSPENSE_COMPONENT = /* @__PURE__ */ Symbol("Suspense Component" );
15
+ var FOR_COMPONENT = /* @__PURE__ */ Symbol("For Component" );
16
+ function patchAttr(el, key, prev, next2) {
17
+ if (key === KEY_PROP) {
18
+ if (next2 == null) {
19
+ el.removeAttribute(key);
20
+ } else {
21
+ el.setAttribute(key, String(next2));
22
+ }
23
+ return;
52
24
  }
53
- }
54
- function executeHooks(hooks, scopeId2, phase) {
55
- const len = hooks.length;
56
- if (len === 0) return;
57
- let pending;
58
- for (let i = 0; i < len; i++) {
59
- try {
60
- const result = hooks[i]();
61
- if (isPromise(result)) {
62
- const safePromise = result.catch((error_) => {
63
- if (true) {
64
- error(`Scope(${scopeId2}): Async ${phase} hook rejected:`, error_);
65
- }
66
- });
67
- (pending != null ? pending : pending = []).push(safePromise);
25
+ if (key === SPREAD_NAME) {
26
+ const prevObj = isObject(prev) ? prev : null;
27
+ const nextObj = isObject(next2) ? next2 : null;
28
+ {
29
+ if (next2 != null && !nextObj) {
30
+ warn("spread attribute must be an object");
68
31
  }
69
- } catch (error_) {
70
- {
71
- error(`Scope(${scopeId2}): Error in ${phase} hook:`, error_);
32
+ }
33
+ if (prevObj) {
34
+ for (const attrKey in prevObj) {
35
+ if (attrKey === SPREAD_NAME) {
36
+ {
37
+ warn("nested spread attributes are ignored");
38
+ }
39
+ continue;
40
+ }
41
+ if (!nextObj || !(attrKey in nextObj)) {
42
+ patchAttr(el, attrKey, prevObj[attrKey], null);
43
+ }
72
44
  }
73
45
  }
74
- }
75
- if (!pending) return;
76
- return Promise.all(pending).then(() => void 0);
77
- }
78
- function onMount(hook) {
79
- const scope = getActiveScope();
80
- if (!scope) {
81
- error("onMount() must be called within a scope");
82
- return;
83
- }
84
- if (scope.isMounted) {
85
- try {
86
- const result = hook();
87
- if (isPromise(result)) {
88
- result.catch((error_) => {
89
- if (true) error(`Scope(${scope.id}): Async ${LIFECYCLE.mount} hook rejected:`, error_);
90
- });
46
+ if (nextObj) {
47
+ for (const attrKey in nextObj) {
48
+ if (attrKey === SPREAD_NAME) {
49
+ {
50
+ warn("nested spread attributes are ignored");
51
+ }
52
+ continue;
53
+ }
54
+ patchAttr(el, attrKey, prevObj == null ? void 0 : prevObj[attrKey], nextObj[attrKey]);
91
55
  }
92
- } catch (error_) {
93
- error(`Scope(${scope.id}): Error in ${LIFECYCLE.mount} hook:`, error_);
94
56
  }
95
57
  return;
96
58
  }
97
- registerScopedHook(scope, "onMount", hook);
98
- }
99
- function onUpdate(hook) {
100
- const scope = getActiveScope();
101
- if (!scope) {
102
- error("onUpdate() must be called within a scope");
59
+ const elementIsSVG = (el == null ? void 0 : el.namespaceURI) === SVG_NAMESPACE;
60
+ const isXlink = elementIsSVG && key.startsWith("xlink:");
61
+ const isXmlns = elementIsSVG && key.startsWith("xmlns:");
62
+ const isBoolean2 = isSpecialBooleanAttr(key) || isBooleanAttr(key);
63
+ if (prev === next2) {
103
64
  return;
104
65
  }
105
- registerScopedHook(scope, "onUpdate", hook);
106
- }
107
- function onDestroy(hook) {
108
- const scope = getActiveScope();
109
- if (!scope) {
110
- error("onDestroy() must be called within a scope");
66
+ if (key.length > 2 && key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110) {
111
67
  return;
112
68
  }
113
- registerScopedHook(scope, "onDestroy", hook);
114
- }
115
- function triggerMountHooks(scope) {
116
- var _a2;
117
- if (scope.isDestroyed || !((_a2 = scope.onMount) == null ? void 0 : _a2.length)) {
118
- scope.isMounted = true;
69
+ const lowerKey = key.toLowerCase();
70
+ if (lowerKey === "innerhtml" || lowerKey === "srcdoc") {
71
+ {
72
+ warn(`${key} updates are ignored by patchAttr`);
73
+ }
119
74
  return;
120
75
  }
121
- const mountHooks = scope.onMount;
122
- const result = runWithScope(scope, () => executeHooks(mountHooks, scope.id, LIFECYCLE.mount));
123
- mountHooks.length = 0;
124
- scope.isMounted = true;
125
- return result;
126
- }
127
- function triggerUpdateHooks(scope) {
128
- var _a2;
129
- if (scope.isDestroyed || !((_a2 = scope.onUpdate) == null ? void 0 : _a2.length)) return;
130
- const updateHooks = scope.onUpdate;
131
- const result = runWithScope(scope, () => executeHooks(updateHooks, scope.id, "update"));
132
- updateHooks.length = 0;
133
- return result;
134
- }
135
- function triggerDestroyHooks(scope) {
136
- var _a2;
137
- if (scope.isDestroyed || !((_a2 = scope.onDestroy) == null ? void 0 : _a2.length)) return;
138
- return runWithScope(scope, () => executeHooks(scope.onDestroy, scope.id, "destroy"));
139
- }
140
-
141
- // src/scope.ts
142
- var activeScope = null;
143
- var scopeId = 0;
144
- function getActiveScope() {
145
- return activeScope;
146
- }
147
- function setActiveScope(scope) {
148
- activeScope = scope;
149
- }
150
- function createScope(parent = activeScope) {
151
- const scope = {
152
- id: ++scopeId,
153
- parent,
154
- children: null,
155
- provides: null,
156
- cleanup: null,
157
- onMount: null,
158
- onUpdate: null,
159
- onDestroy: null,
160
- isMounted: false,
161
- isDestroyed: false
162
- };
163
- if (parent) {
164
- if (!parent.children) {
165
- parent.children = [];
76
+ if (next2 == null) {
77
+ if (isXlink) {
78
+ el.removeAttributeNS(XLINK_NAMESPACE, key.slice(6));
79
+ } else if (isXmlns) {
80
+ const localName = key.slice(6);
81
+ el.removeAttributeNS(XMLNS_NAMESPACE, localName);
82
+ } else {
83
+ el.removeAttribute(key);
166
84
  }
167
- parent.children.push(scope);
168
- }
169
- return scope;
170
- }
171
- function runWithScope(scope, fn) {
172
- const prevScope = activeScope;
173
- activeScope = scope;
174
- try {
175
- return fn();
176
- } finally {
177
- activeScope = prevScope;
85
+ return;
178
86
  }
179
- }
180
- function disposeScope(scope) {
181
- var _a2, _b, _c;
182
- if (!scope || scope.isDestroyed) {
87
+ if (isBoolean2) {
88
+ if (includeBooleanAttr(next2)) {
89
+ el.setAttribute(key, "");
90
+ } else {
91
+ el.removeAttribute(key);
92
+ }
183
93
  return;
184
94
  }
185
- const parentScope = scope.parent;
186
- if (scope.children) {
187
- for (const child of scope.children) {
188
- disposeScope(child);
95
+ const attrValue = isSymbol(next2) ? String(next2) : next2;
96
+ const isUrlAttr = lowerKey === "href" || lowerKey === "src" || lowerKey === "xlink:href" || lowerKey === "action" || lowerKey === "formaction" || lowerKey === "poster";
97
+ if (isUrlAttr && isString(attrValue)) {
98
+ const v = attrValue.trim().toLowerCase();
99
+ if (v.startsWith("javascript:") || v.startsWith("data:")) {
100
+ return;
189
101
  }
190
- scope.children.length = 0;
191
102
  }
192
- if ((_a2 = scope.onDestroy) == null ? void 0 : _a2.length) {
193
- triggerDestroyHooks(scope);
194
- scope.onDestroy.length = 0;
103
+ if (isXlink) {
104
+ el.setAttributeNS(XLINK_NAMESPACE, key, String(attrValue));
105
+ return;
195
106
  }
196
- if ((_b = scope.cleanup) == null ? void 0 : _b.length) {
197
- for (const fn of scope.cleanup) {
107
+ if (isXmlns) {
108
+ el.setAttributeNS(XMLNS_NAMESPACE, key, String(attrValue));
109
+ return;
110
+ }
111
+ if (elementIsSVG) {
112
+ el.setAttribute(key, String(attrValue));
113
+ } else {
114
+ if (key in el) {
198
115
  try {
199
- fn();
200
- } catch (error_) {
201
- {
202
- error(`Scope(${scope.id}): Error in cleanup:`, error_);
203
- }
116
+ el[key] = attrValue;
117
+ } catch (e) {
118
+ el.setAttribute(key, String(attrValue));
204
119
  }
120
+ } else {
121
+ el.setAttribute(key, String(attrValue));
205
122
  }
206
- scope.cleanup.length = 0;
207
- }
208
- if (parentScope == null ? void 0 : parentScope.children) {
209
- const idx = parentScope.children.indexOf(scope);
210
- if (idx !== -1) {
211
- parentScope.children.splice(idx, 1);
212
- }
213
- }
214
- (_c = scope.provides) == null ? void 0 : _c.clear();
215
- if (scope.onMount) scope.onMount.length = 0;
216
- if (scope.onUpdate) scope.onUpdate.length = 0;
217
- scope.parent = null;
218
- scope.isDestroyed = true;
219
- if (activeScope === scope) {
220
- activeScope = parentScope;
221
123
  }
222
124
  }
223
- function onCleanup(fn) {
224
- const scope = activeScope;
225
- if (!scope) {
226
- {
227
- error("onCleanup() must be called within a scope");
228
- }
125
+ function patchClass(el, prev, next2, isSVG = false) {
126
+ if (prev === next2) {
229
127
  return;
230
128
  }
231
- if (!scope.cleanup) {
232
- scope.cleanup = [];
129
+ const normalizedNext = normalizeClass(next2);
130
+ if (!normalizedNext) {
131
+ el.removeAttribute("class");
132
+ return;
233
133
  }
234
- scope.cleanup.push(fn);
235
- }
236
-
237
- // src/constants.ts
238
- var EVENT_PREFIX = "on";
239
- var SPREAD_NAME = "_$spread$";
240
- var REF_KEY = "ref";
241
- var KEY_PROP = "key";
242
- var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
243
- var XLINK_NAMESPACE = "http://www.w3.org/2000/xlink";
244
- var XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/";
245
- var NORMAL_COMPONENT = /* @__PURE__ */ Symbol("Normal Component" );
246
- var FRAGMENT_COMPONENT = /* @__PURE__ */ Symbol("Fragment Component" );
247
- var PORTAL_COMPONENT = /* @__PURE__ */ Symbol("Portal Component" );
248
- var SUSPENSE_COMPONENT = /* @__PURE__ */ Symbol("Suspense Component" );
249
- var FOR_COMPONENT = /* @__PURE__ */ Symbol("For Component" );
250
- var MAX_KEY_LENGTH = 1e3;
251
- var componentKeyPrefixCache = /* @__PURE__ */ new WeakMap();
252
- function getComponentKey(type) {
253
- let prefix = componentKeyPrefixCache.get(type);
254
- if (!prefix) {
255
- const name = type.name || "anonymous";
256
- const hash = simpleHash(type.toString()).toString(36);
257
- prefix = `${name}_${hash}`;
258
- componentKeyPrefixCache.set(type, prefix);
259
- }
260
- return prefix;
261
- }
262
- function simpleHash(str) {
263
- let hash = 0;
264
- const len = str.length < 100 ? str.length : 100;
265
- for (let i = 0; i < len; i++) {
266
- hash = Math.trunc((hash << 5) - hash + str.charCodeAt(i));
134
+ const normalizedPrev = isString(prev) ? prev : normalizeClass(prev);
135
+ if (normalizedPrev === normalizedNext) {
136
+ return;
267
137
  }
268
- return hash < 0 ? -hash : hash;
269
- }
270
- var symbolIdCounter = 0;
271
- function normalizeKey(key) {
272
- if (isFalsy(key)) {
273
- return void 0;
138
+ if (isSVG) {
139
+ el.setAttribute("class", normalizedNext);
140
+ } else {
141
+ el.className = normalizedNext;
274
142
  }
275
- if (isString(key)) {
276
- if (key.length <= MAX_KEY_LENGTH) {
277
- return key;
143
+ }
144
+ var normalizeClass = normalizeClassName;
145
+ var importantRE = /\s*!important$/;
146
+ var prefixes = ["Webkit", "Moz", "ms"];
147
+ var prefixCache = {};
148
+ function patchStyle(el, prev, next2) {
149
+ const style = el.style;
150
+ if (next2 && isString(next2)) {
151
+ if (prev !== next2) {
152
+ style.cssText = next2;
278
153
  }
279
- {
280
- warn(
281
- `[Key System] Key length exceeds ${MAX_KEY_LENGTH} characters. Consider using a shorter identifier.`
282
- );
154
+ return;
155
+ }
156
+ if (!next2) {
157
+ if (prev) {
158
+ el.removeAttribute("style");
283
159
  }
284
- return `${key.slice(0, MAX_KEY_LENGTH - 10)}_${simpleHash(key).toString(36)}`;
160
+ return;
285
161
  }
286
- if (isNumber(key)) {
287
- {
288
- if (key !== key) {
289
- warn("[Key System] NaN cannot be used as a key");
290
- return void 0;
291
- }
292
- if (!Number.isFinite(key)) {
293
- warn("[Key System] Infinity cannot be used as a key");
294
- return void 0;
162
+ if (prev && !isString(prev)) {
163
+ const prevObj = prev;
164
+ for (const key in prevObj) {
165
+ if (!next2 || next2[key] == null) {
166
+ setStyle(style, key, "");
295
167
  }
296
168
  }
297
- return String(key);
298
- }
299
- if (isSymbol(key)) {
300
- const globalKey = Symbol.keyFor(key);
301
- if (globalKey) {
302
- return `_s.${globalKey}`;
169
+ } else if (prev && isString(prev)) {
170
+ const declRE = /(?:^|;)\s*([a-z][a-z\d-]*)\s*:/gi;
171
+ let match;
172
+ while ((match = declRE.exec(prev)) !== null) {
173
+ const key = match[1].trim();
174
+ if (key && next2 && isObject(next2) && next2[key] == null) {
175
+ setStyle(style, key, "");
176
+ }
303
177
  }
304
- const desc = key.description;
305
- return desc ? `_s.${desc}` : `${symbolIdCounter++}`;
306
178
  }
307
- return String(key);
308
- }
309
- var NODE_KEY_SYMBOL = /* @__PURE__ */ Symbol("essor.key");
310
- function setNodeKey(node, key) {
311
- if (isComponent(node)) {
312
- return;
313
- }
314
- if (!node || node.nodeType === Node.DOCUMENT_NODE) {
315
- {
316
- warn("[Key System] Cannot set key on invalid node");
179
+ if (next2 && !isString(next2)) {
180
+ const nextObj = next2;
181
+ for (const key in nextObj) {
182
+ const value = nextObj[key];
183
+ if ((!prev || isString(prev) || prev[key] !== value) && value != null) {
184
+ setStyle(style, key, value);
185
+ }
317
186
  }
318
- return;
319
- }
320
- const normalizedKey = normalizeKey(key);
321
- if (isFalsy(normalizedKey)) {
322
- delete node[NODE_KEY_SYMBOL];
323
- } else {
324
- node[NODE_KEY_SYMBOL] = normalizedKey;
325
187
  }
326
188
  }
327
- function getNodeKey(node) {
328
- if (!node) return void 0;
329
- return isComponent(node) ? node.key : node[NODE_KEY_SYMBOL];
330
- }
331
-
332
- // src/utils/node.ts
333
- function normalizeNode(node) {
334
- if (isHTMLElement(node)) {
335
- return node;
336
- }
337
- if (isPrimitive(node)) {
338
- const textContent = isFalsy(node) ? "" : String(node);
339
- return document.createTextNode(textContent);
340
- }
341
- return node;
342
- }
343
- function isSameNode(a, b) {
344
- const keyA = getNodeKey(a);
345
- const keyB = getNodeKey(b);
346
- if (keyA !== keyB) {
347
- return false;
348
- }
349
- const aIsComponent = isComponent(a);
350
- const bIsComponent = isComponent(b);
351
- if (aIsComponent && bIsComponent) {
352
- return a.component === b.component;
353
- }
354
- if (aIsComponent !== bIsComponent) {
355
- return false;
356
- }
357
- if (isPrimitive(a) || isPrimitive(b)) {
358
- return a === b;
359
- }
360
- const aNode = a;
361
- const bNode = b;
362
- if (aNode.nodeType !== bNode.nodeType) {
363
- return false;
364
- }
365
- if (aNode.nodeType === Node.ELEMENT_NODE) {
366
- return aNode.tagName === bNode.tagName;
367
- }
368
- return true;
369
- }
370
- function shallowCompare(a, b) {
371
- if (a === b) return true;
372
- if (isNull(a) || isNull(b)) return false;
373
- if (!isObject(a) || !isObject(b)) return false;
374
- if (isArray(a) !== isArray(b)) return false;
375
- const aRecord = a;
376
- const bRecord = b;
377
- const aKeys = Object.keys(aRecord);
378
- const bKeys = Object.keys(bRecord);
379
- if (aKeys.length !== bKeys.length) return false;
380
- for (const key of aKeys) {
381
- if (!(key in bRecord) || aRecord[key] !== bRecord[key]) {
382
- return false;
189
+ function setStyle(style, name, val) {
190
+ if (isArray(val)) {
191
+ for (const element of val) {
192
+ setStyle(style, name, element);
383
193
  }
384
- }
385
- return true;
386
- }
387
- function removeNode(node) {
388
- if (!node) return;
389
- if (isComponent(node)) {
390
- node.destroy();
391
194
  return;
392
195
  }
393
- const element = node;
394
- if (element.parentElement) {
395
- element.remove();
196
+ if (val == null || val === "") {
197
+ val = "";
396
198
  }
397
- }
398
- function insertNode(parent, child, before) {
399
- if (!parent || !child) return;
400
- if (isComponent(child)) {
401
- const beforeNode2 = isComponent(before) ? before.firstChild : before;
402
- child.mount(parent, beforeNode2);
199
+ if (name.startsWith("--")) {
200
+ style.setProperty(name, val);
403
201
  return;
404
202
  }
405
- const beforeNode = isComponent(before) ? before.firstChild : before;
406
- if (beforeNode) {
407
- parent.insertBefore(child, beforeNode);
203
+ const prefixed = autoPrefix(style, name);
204
+ if (isString(val) && importantRE.test(val)) {
205
+ style.setProperty(camelCase(prefixed), val.replace(importantRE, ""), "important");
408
206
  } else {
409
- parent.appendChild(child);
207
+ style[prefixed] = val;
410
208
  }
411
209
  }
412
- function replaceNode(parent, newNode, oldNode) {
413
- if (!parent || !newNode || !oldNode || newNode === oldNode) return;
414
- const beforeNode = isComponent(oldNode) ? oldNode.beforeNode : oldNode.nextSibling;
415
- removeNode(oldNode);
416
- insertNode(parent, newNode, beforeNode);
417
- }
418
- function getFirstDOMNode(node) {
419
- if (!node) {
420
- return;
210
+ function autoPrefix(style, rawName) {
211
+ const cached = prefixCache[rawName];
212
+ if (cached) {
213
+ return cached;
421
214
  }
422
- if (isComponent(node)) {
423
- return node.firstChild;
215
+ let name = camelCase(rawName);
216
+ if (name !== "filter" && name in style) {
217
+ return prefixCache[rawName] = name;
424
218
  }
425
- if (isPrimitive(node)) {
426
- return void 0;
219
+ name = capitalize(name);
220
+ for (const prefix of prefixes) {
221
+ const prefixed = prefix + name;
222
+ if (prefixed in style) {
223
+ return prefixCache[rawName] = prefixed;
224
+ }
427
225
  }
428
- return node;
226
+ return rawName;
429
227
  }
430
- function transferKey(oldNode, newNode) {
431
- if (isComponent(oldNode) || isComponent(newNode)) {
432
- return;
433
- }
434
- const oldKey = getNodeKey(oldNode);
435
- if (oldKey && !getNodeKey(newNode)) {
436
- setNodeKey(newNode, oldKey);
437
- }
228
+
229
+ // src/hydration.ts
230
+ var _hydrationKey = 0;
231
+ function getHydrationKey() {
232
+ return String(_hydrationKey++);
438
233
  }
439
- function patch(parent, oldNode, newNode) {
440
- if (newNode === oldNode) {
441
- return oldNode;
442
- }
443
- const oldIsElement = isHTMLElement(oldNode);
444
- const newIsElement = isHTMLElement(newNode);
445
- if (newIsElement && oldIsElement) {
446
- if (newNode.isEqualNode(oldNode)) {
447
- return oldNode;
234
+ function resetHydrationKey() {
235
+ _hydrationKey = 0;
236
+ }
237
+ var _isHydrating = false;
238
+ function isHydrating() {
239
+ return _isHydrating;
240
+ }
241
+ var _registry = /* @__PURE__ */ new Map();
242
+ function gatherHydratable(root) {
243
+ const nodes = root.querySelectorAll("[data-hk]");
244
+ for (const node of nodes) {
245
+ const key = node.dataset.hk;
246
+ if (key != null && !_registry.has(key)) {
247
+ _registry.set(key, node);
448
248
  }
449
- if (oldNode.tagName === newNode.tagName) {
450
- const oldAttrs = oldNode.attributes;
451
- const newAttrs = newNode.attributes;
452
- for (let i = oldAttrs.length - 1; i >= 0; i--) {
453
- const attrName = oldAttrs[i].name;
454
- if (!newNode.hasAttribute(attrName)) {
455
- oldNode.removeAttribute(attrName);
456
- }
457
- }
458
- for (let i = 0, len = newAttrs.length; i < len; i++) {
459
- const attr = newAttrs[i];
460
- if (oldNode.getAttribute(attr.name) !== attr.value) {
461
- oldNode.setAttribute(attr.name, attr.value);
462
- }
249
+ }
250
+ }
251
+ var _teleportCallsiteAnchors = [];
252
+ var _teleportTargetStarts = /* @__PURE__ */ new Map();
253
+ function gatherTeleportAnchors() {
254
+ if (typeof document === "undefined") return;
255
+ const walker = document.createNodeIterator(document.body, NodeFilter.SHOW_COMMENT);
256
+ let node;
257
+ while (node = walker.nextNode()) {
258
+ const data = node.data;
259
+ if (data === "teleport-anchor") {
260
+ _teleportCallsiteAnchors.push(node);
261
+ } else if (data === "teleport-start") {
262
+ const parent = node.parentElement;
263
+ if (!parent) continue;
264
+ let bucket = _teleportTargetStarts.get(parent);
265
+ if (!bucket) {
266
+ bucket = [];
267
+ _teleportTargetStarts.set(parent, bucket);
463
268
  }
464
- transferKey(oldNode, newNode);
465
- return oldNode;
269
+ bucket.push(node);
466
270
  }
467
271
  }
468
- if (isTextNode(oldNode) && isTextNode(newNode)) {
469
- if (oldNode.textContent !== newNode.textContent) {
470
- oldNode.textContent = newNode.textContent;
272
+ }
273
+ function consumeTeleportAnchor() {
274
+ var _a2;
275
+ return (_a2 = _teleportCallsiteAnchors.shift()) != null ? _a2 : null;
276
+ }
277
+ function consumeTeleportBlock(target) {
278
+ const bucket = _teleportTargetStarts.get(target);
279
+ const start = bucket == null ? void 0 : bucket.shift();
280
+ if (!start) return null;
281
+ const nodes = [];
282
+ let cursor = start.nextSibling;
283
+ while (cursor) {
284
+ if (cursor.nodeType === Node.COMMENT_NODE && cursor.data === "teleport-end") {
285
+ return { start, end: cursor, nodes };
471
286
  }
472
- transferKey(oldNode, newNode);
473
- return oldNode;
287
+ nodes.push(cursor);
288
+ cursor = cursor.nextSibling;
474
289
  }
475
- const oldIsComponent = isComponent(oldNode);
476
- const newIsComponent = isComponent(newNode);
477
- if (oldIsComponent && newIsComponent) {
478
- if (oldNode.component === newNode.component) {
479
- return newNode.update(oldNode);
480
- }
290
+ {
291
+ warn("[Portal] hydration: orphaned <!--teleport-start--> without matching <!--teleport-end-->");
481
292
  }
482
- replaceNode(parent, newNode, oldNode);
483
- return newNode;
293
+ return null;
484
294
  }
485
- function patchChildren(parent, oldChildren, newChildren, anchor) {
486
- const oldLength = oldChildren.length;
487
- const newLength = newChildren.length;
488
- if (oldLength === 0 && newLength === 0) {
489
- return [];
295
+ function beginHydration(root) {
296
+ _isHydrating = true;
297
+ _hydrationKey = 0;
298
+ _registry.clear();
299
+ _teleportCallsiteAnchors.length = 0;
300
+ _teleportTargetStarts.clear();
301
+ gatherHydratable(root);
302
+ gatherTeleportAnchors();
303
+ }
304
+ function endHydration() {
305
+ _isHydrating = false;
306
+ _registry.clear();
307
+ _teleportCallsiteAnchors.length = 0;
308
+ _teleportTargetStarts.clear();
309
+ }
310
+ function getRenderedElement(html) {
311
+ if (!isBrowser()) {
312
+ return () => {
313
+ throw new Error("[essor] getRenderedElement called in non-browser environment");
314
+ };
490
315
  }
316
+ let _csrFactory = null;
317
+ return () => {
318
+ if (!_isHydrating) {
319
+ if (!_csrFactory) _csrFactory = template(html);
320
+ return _csrFactory();
321
+ }
322
+ const key = getHydrationKey();
323
+ const node = _registry.get(key);
324
+ if (node) {
325
+ _registry.delete(key);
326
+ return node;
327
+ }
328
+ warn(`[essor] hydration mismatch: no SSR element for key "${key}"`);
329
+ if (!_csrFactory) _csrFactory = template(html);
330
+ return _csrFactory();
331
+ };
332
+ }
333
+ function patchClassHydrate(el, prev, next2, isSVG) {
334
+ if (_isHydrating) return;
335
+ patchClass(el, prev, next2, isSVG);
336
+ }
337
+ function patchAttrHydrate(el, key, prev, next2) {
338
+ if (_isHydrating) return;
339
+ patchAttr(el, key, prev, next2);
340
+ }
341
+ function patchStyleHydrate(el, prev, next2) {
342
+ if (_isHydrating) return;
343
+ patchStyle(el, prev, next2);
344
+ }
345
+
346
+ // src/reconcile.ts
347
+ function resolveInsertAnchor(parent, candidate) {
348
+ return candidate && candidate.parentNode === parent ? candidate : null;
349
+ }
350
+ function reconcileArrays(parent, oldNodes, newNodes, anchor) {
351
+ const fallbackAnchor = resolveInsertAnchor(parent, anchor);
352
+ const oldLength = oldNodes.length;
353
+ const newLength = newNodes.length;
354
+ if (oldLength === 0 && newLength === 0) return newNodes;
491
355
  if (oldLength === 0) {
492
356
  for (let i = 0; i < newLength; i++) {
493
- insertNode(parent, newChildren[i], anchor);
357
+ insertNode(parent, newNodes[i], fallbackAnchor);
494
358
  }
495
- return newChildren;
359
+ return newNodes;
496
360
  }
497
361
  if (newLength === 0) {
498
362
  for (let i = 0; i < oldLength; i++) {
499
- removeNode(oldChildren[i]);
363
+ removeNode(oldNodes[i]);
500
364
  }
501
365
  return [];
502
366
  }
503
- if (oldLength === 1 && newLength === 1) {
504
- const oldNode = oldChildren[0];
505
- const newNode = newChildren[0];
506
- if (isSameNode(oldNode, newNode)) {
507
- patch(parent, oldNode, newNode);
508
- newChildren[0] = oldNode;
509
- } else {
510
- replaceNode(parent, newNode, oldNode);
511
- }
512
- return newChildren;
513
- }
514
- if (oldLength === 2 && newLength === 2) {
515
- const o0 = oldChildren[0];
516
- const o1 = oldChildren[1];
517
- const n0 = newChildren[0];
518
- const n1 = newChildren[1];
519
- if (isSameNode(o0, n0) && isSameNode(o1, n1)) {
520
- patch(parent, o0, n0);
521
- patch(parent, o1, n1);
522
- newChildren[0] = o0;
523
- newChildren[1] = o1;
524
- return newChildren;
525
- }
526
- if (isSameNode(o0, n1) && isSameNode(o1, n0)) {
527
- patch(parent, o0, n1);
528
- patch(parent, o1, n0);
529
- const dom1 = getFirstDOMNode(o1);
530
- const dom0 = getFirstDOMNode(o0);
531
- if (dom1 && dom0 && dom1.parentNode === parent) {
532
- parent.insertBefore(dom1, dom0);
533
- }
534
- newChildren[0] = o1;
535
- newChildren[1] = o0;
536
- return newChildren;
537
- }
538
- }
539
- return patchKeyedChildren(parent, oldChildren, newChildren, anchor);
540
- }
541
- function patchKeyedChildren(parent, oldChildren, newChildren, anchor) {
542
- let oldStartIdx = 0;
543
- let newStartIdx = 0;
544
- let oldEndIdx = oldChildren.length - 1;
545
- let newEndIdx = newChildren.length - 1;
546
- let oldStartNode = oldChildren[0];
547
- let oldEndNode = oldChildren[oldEndIdx];
548
- let newStartNode = newChildren[0];
549
- let newEndNode = newChildren[newEndIdx];
550
- while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
551
- if (!oldStartNode) {
552
- oldStartNode = oldChildren[++oldStartIdx];
553
- } else if (!oldEndNode) {
554
- oldEndNode = oldChildren[--oldEndIdx];
555
- } else if (isSameNode(oldStartNode, newStartNode)) {
556
- patch(parent, oldStartNode, newStartNode);
557
- newChildren[newStartIdx] = oldStartNode;
558
- oldStartNode = oldChildren[++oldStartIdx];
559
- newStartNode = newChildren[++newStartIdx];
367
+ let start = 0;
368
+ let oldEnd = oldLength - 1;
369
+ let newEnd = newLength - 1;
370
+ while (start <= oldEnd && start <= newEnd) {
371
+ if (oldNodes[start] === newNodes[start]) {
372
+ start++;
560
373
  } else {
561
374
  break;
562
375
  }
563
376
  }
564
- while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
565
- if (!oldStartNode) {
566
- oldStartNode = oldChildren[++oldStartIdx];
567
- } else if (!oldEndNode) {
568
- oldEndNode = oldChildren[--oldEndIdx];
569
- } else if (isSameNode(oldEndNode, newEndNode)) {
570
- patch(parent, oldEndNode, newEndNode);
571
- newChildren[newEndIdx] = oldEndNode;
572
- oldEndNode = oldChildren[--oldEndIdx];
573
- newEndNode = newChildren[--newEndIdx];
377
+ while (oldEnd >= start && newEnd >= start) {
378
+ if (oldNodes[oldEnd] === newNodes[newEnd]) {
379
+ oldEnd--;
380
+ newEnd--;
574
381
  } else {
575
382
  break;
576
383
  }
577
384
  }
578
- if (oldStartIdx > oldEndIdx) {
579
- if (newStartIdx <= newEndIdx) {
580
- const anchorNode = newEndIdx + 1 < newChildren.length ? getFirstDOMNode(newChildren[newEndIdx + 1]) : anchor;
581
- for (let i = newStartIdx; i <= newEndIdx; i++) {
582
- insertNode(parent, newChildren[i], anchorNode);
385
+ if (start > oldEnd) {
386
+ if (start <= newEnd) {
387
+ const nextPos = newEnd + 1;
388
+ const nextNode = resolveInsertAnchor(
389
+ parent,
390
+ nextPos < newLength ? newNodes[nextPos] : fallbackAnchor
391
+ );
392
+ for (let i = start; i <= newEnd; i++) {
393
+ insertNode(parent, newNodes[i], nextNode);
583
394
  }
584
395
  }
585
- } else if (newStartIdx > newEndIdx) {
586
- for (let i = oldStartIdx; i <= oldEndIdx; i++) {
587
- const node = oldChildren[i];
588
- if (node) {
589
- removeNode(node);
590
- }
396
+ } else if (start > newEnd) {
397
+ for (let i = start; i <= oldEnd; i++) {
398
+ removeNode(oldNodes[i]);
591
399
  }
592
400
  } else {
593
- patchUnknownSequence(
594
- parent,
595
- oldChildren,
596
- newChildren,
597
- oldStartIdx,
598
- oldEndIdx,
599
- newStartIdx,
600
- newEndIdx,
601
- anchor
602
- );
401
+ reconcileUnknownSequence(parent, oldNodes, newNodes, start, oldEnd, newEnd, fallbackAnchor);
603
402
  }
604
- return newChildren;
403
+ return newNodes;
605
404
  }
606
- function patchUnknownSequence(parent, oldChildren, newChildren, oldStartIdx, oldEndIdx, newStartIdx, newEndIdx, anchor) {
607
- const newLength = newEndIdx - newStartIdx + 1;
608
- let keyToNewIndexMap;
609
- for (let i = newStartIdx; i <= newEndIdx; i++) {
610
- const key = getNodeKey(newChildren[i]);
611
- if (key !== void 0) {
612
- if (!keyToNewIndexMap) {
613
- keyToNewIndexMap = /* @__PURE__ */ Object.create(null);
614
- }
615
- keyToNewIndexMap[key] = i;
616
- }
405
+ function reconcileUnknownSequence(parent, oldNodes, newNodes, start, oldEnd, newEnd, anchor) {
406
+ const newLength = newEnd - start + 1;
407
+ const newIndexMap = /* @__PURE__ */ new Map();
408
+ for (let i = start; i <= newEnd; i++) {
409
+ newIndexMap.set(newNodes[i], i);
617
410
  }
618
411
  const newIndexToOldIndexMap = new Int32Array(newLength);
412
+ newIndexToOldIndexMap.fill(0);
413
+ let patched = 0;
619
414
  let moved = false;
620
415
  let maxNewIndexSoFar = 0;
621
- let patched = 0;
622
- for (let i = oldStartIdx; i <= oldEndIdx; i++) {
623
- const oldNode = oldChildren[i];
624
- if (!oldNode) continue;
416
+ for (let i = start; i <= oldEnd; i++) {
417
+ const oldNode = oldNodes[i];
625
418
  if (patched >= newLength) {
626
419
  removeNode(oldNode);
627
420
  continue;
628
421
  }
629
- let newIndex;
630
- const oldKey = getNodeKey(oldNode);
631
- if (oldKey !== void 0 && keyToNewIndexMap && oldKey in keyToNewIndexMap) {
632
- newIndex = keyToNewIndexMap[oldKey];
633
- } else {
634
- for (let j2 = newStartIdx; j2 <= newEndIdx; j2++) {
635
- if (newIndexToOldIndexMap[j2 - newStartIdx] === 0 && oldKey === void 0 && getNodeKey(newChildren[j2]) === void 0 && isSameNode(oldNode, newChildren[j2])) {
636
- newIndex = j2;
637
- break;
638
- }
639
- }
640
- }
422
+ const newIndex = newIndexMap.get(oldNode);
641
423
  if (newIndex === void 0) {
642
424
  removeNode(oldNode);
643
425
  } else {
644
- newIndexToOldIndexMap[newIndex - newStartIdx] = i + 1;
426
+ newIndexToOldIndexMap[newIndex - start] = i + 1;
645
427
  if (newIndex >= maxNewIndexSoFar) {
646
428
  maxNewIndexSoFar = newIndex;
647
429
  } else {
648
430
  moved = true;
649
431
  }
650
- patch(parent, oldNode, newChildren[newIndex]);
651
- newChildren[newIndex] = oldNode;
652
432
  patched++;
653
433
  }
654
434
  }
655
435
  const increasingNewIndexSequence = moved ? getSequence(newIndexToOldIndexMap) : [];
656
436
  let j = increasingNewIndexSequence.length - 1;
657
- let cachedAnchor = anchor;
658
437
  for (let i = newLength - 1; i >= 0; i--) {
659
- const nextIndex = newStartIdx + i;
660
- const nextNode = newChildren[nextIndex];
438
+ const nextIndex = start + i;
439
+ const nextNode = newNodes[nextIndex];
440
+ const anchorNode = resolveInsertAnchor(
441
+ parent,
442
+ nextIndex + 1 < newNodes.length ? newNodes[nextIndex + 1] : anchor
443
+ );
661
444
  if (newIndexToOldIndexMap[i] === 0) {
662
- insertNode(parent, nextNode, cachedAnchor);
663
- cachedAnchor = getFirstDOMNode(nextNode) || cachedAnchor;
445
+ insertNode(parent, nextNode, anchorNode);
664
446
  } else if (moved) {
665
447
  if (j < 0 || i !== increasingNewIndexSequence[j]) {
666
- const domNode = getFirstDOMNode(nextNode);
667
- if (domNode && domNode.parentNode === parent) {
668
- insertNode(parent, domNode, cachedAnchor);
669
- }
670
- cachedAnchor = domNode || cachedAnchor;
448
+ insertNode(parent, nextNode, anchorNode);
671
449
  } else {
672
- cachedAnchor = getFirstDOMNode(nextNode) || cachedAnchor;
673
450
  j--;
674
451
  }
675
- } else {
676
- cachedAnchor = getFirstDOMNode(nextNode) || cachedAnchor;
677
452
  }
678
453
  }
679
454
  }
680
455
  function getSequence(arr) {
456
+ const p = new Int32Array(arr.length);
457
+ const result = [0];
681
458
  const len = arr.length;
682
- if (len === 0) return [];
683
- if (len === 1) return arr[0] !== 0 ? [0] : [];
684
- const result = [];
685
- const p = new Int32Array(len);
686
459
  let i;
687
460
  let j;
688
461
  let u;
@@ -692,7 +465,7 @@ function getSequence(arr) {
692
465
  const arrI = arr[i];
693
466
  if (arrI !== 0) {
694
467
  j = result[result.length - 1];
695
- if (result.length === 0 || arr[j] < arrI) {
468
+ if (arr[j] < arrI) {
696
469
  p[i] = j;
697
470
  result.push(i);
698
471
  continue;
@@ -724,348 +497,385 @@ function getSequence(arr) {
724
497
  return result;
725
498
  }
726
499
 
727
- // src/binding.ts
728
- function addEventListener(element, event, handler, options) {
729
- element.addEventListener(event, handler, options);
730
- onCleanup(() => {
731
- element.removeEventListener(event, handler, options);
732
- });
733
- }
734
- function bindElement(node, key, defaultValue, setter) {
735
- if (isHtmlInputElement(node)) {
736
- bindInputElement(node, setter);
737
- } else if (isHtmlSelectElement(node)) {
738
- bindSelectElement(node, setter);
739
- } else if (isHtmlTextAreaElement(node)) {
740
- addEventListener(node, "input", () => {
741
- setter(node.value);
742
- });
500
+ // src/dom.ts
501
+ function removeNode(node) {
502
+ if (!node) return;
503
+ if (isComponent(node)) {
504
+ node.destroy();
505
+ } else {
506
+ const element = node;
507
+ if (element.parentNode) {
508
+ element.remove();
509
+ }
743
510
  }
744
511
  }
745
- function bindInputElement(node, setter) {
746
- switch (node.type) {
747
- case "checkbox":
748
- addEventListener(node, "change", () => {
749
- setter(Boolean(node.checked));
750
- });
751
- break;
752
- case "radio":
753
- addEventListener(node, "change", () => {
754
- setter(node.checked ? node.value : "");
755
- });
756
- break;
757
- case "file":
758
- addEventListener(node, "change", () => {
759
- setter(node.files);
760
- });
761
- break;
762
- case "number":
763
- case "range":
764
- addEventListener(node, "input", () => {
765
- setter(node.value || "");
766
- });
767
- break;
768
- case "date":
769
- case "datetime-local":
770
- case "month":
771
- case "time":
772
- case "week":
773
- addEventListener(node, "change", () => {
774
- setter(node.value || "");
775
- });
776
- break;
777
- default:
778
- addEventListener(node, "input", () => {
779
- setter(node.value);
780
- });
781
- break;
512
+ function insertNode(parent, child2, before) {
513
+ if (!parent || !child2) return;
514
+ const beforeNode = isComponent(before) ? before.firstChild : before;
515
+ if (isComponent(child2)) {
516
+ child2.mount(parent, beforeNode);
517
+ return;
782
518
  }
783
- }
784
- function bindSelectElement(node, setter) {
785
- addEventListener(node, "change", () => {
786
- if (node.multiple) {
787
- const values = Array.from(node.options).filter((option) => option.selected).map((option) => option.value);
788
- setter(values);
789
- } else {
790
- setter(node.value);
519
+ if (beforeNode) {
520
+ parent.insertBefore(child2, beforeNode);
521
+ } else {
522
+ {
523
+ if (!child2) {
524
+ error("insertNode: child is not a Node", child2);
525
+ }
791
526
  }
792
- });
527
+ parent.appendChild(child2);
528
+ }
529
+ }
530
+ function normalizeNode(node) {
531
+ if (isHTMLElement(node)) {
532
+ return node;
533
+ }
534
+ if (isPrimitive(node)) {
535
+ return document.createTextNode(isFalsy(node) ? "" : String(node));
536
+ }
537
+ return node;
793
538
  }
794
539
  function insert(parent, nodeFactory, before) {
795
540
  if (!parent) return;
541
+ const ownerScope = getActiveScope();
796
542
  let renderedNodes = [];
797
- const currentScope = getActiveScope();
798
- const cleanup = effect(() => {
799
- const run = () => {
543
+ let isFirstRun = true;
544
+ const resolveNodes = (raw) => {
545
+ if (raw instanceof Node) return [raw];
546
+ if (isNull(raw) || isUndefined(raw) || isString(raw) || isNumber(raw) || isBoolean(raw)) {
547
+ return [normalizeNode(raw)];
548
+ }
549
+ return coerceArray(raw).map((item) => isFunction(item) ? item() : item).flatMap((i) => i).map(normalizeNode);
550
+ };
551
+ const effectRunner = effect(() => {
552
+ const executeUpdate = () => {
800
553
  const rawNodes = isFunction(nodeFactory) ? nodeFactory() : nodeFactory;
801
- const nodes = coerceArray(rawNodes).map((item) => isFunction(item) ? item() : item).flatMap(normalizeNode);
802
- renderedNodes = patchChildren(parent, renderedNodes, nodes, before);
554
+ const nodes = resolveNodes(rawNodes);
555
+ if (isFirstRun && isHydrating() && nodes.every((node) => node instanceof Node && node.parentNode === parent)) {
556
+ renderedNodes = nodes;
557
+ isFirstRun = false;
558
+ return;
559
+ }
560
+ renderedNodes = reconcileArrays(parent, renderedNodes, nodes, before);
561
+ isFirstRun = false;
803
562
  };
804
- if (currentScope) {
805
- runWithScope(currentScope, run);
563
+ if (ownerScope && !ownerScope.isDestroyed) {
564
+ runWithScope(ownerScope, executeUpdate);
806
565
  } else {
807
- run();
566
+ executeUpdate();
808
567
  }
809
568
  });
810
569
  onCleanup(() => {
811
- cleanup();
812
- renderedNodes.forEach((node) => removeNode(node));
813
- renderedNodes.length = 0;
570
+ effectRunner.stop();
571
+ for (const node of renderedNodes) removeNode(node);
572
+ renderedNodes = [];
814
573
  });
815
574
  return renderedNodes;
816
575
  }
817
- function mapNodes(template2, indexes) {
818
- const len = indexes.length;
819
- const tree = new Array(len);
820
- const indexSet = new Set(indexes);
821
- let index = 1;
822
- let found = 0;
823
- const walk = (node) => {
824
- if (node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
825
- if (indexSet.has(index)) {
826
- tree[found++] = node;
827
- if (found === len) return true;
828
- }
829
- index++;
830
- }
831
- let child = node.firstChild;
832
- while (child) {
833
- if (walk(child)) return true;
834
- child = child.nextSibling;
835
- }
836
- return false;
837
- };
838
- walk(template2);
839
- return tree;
576
+ function child(node) {
577
+ return (node == null ? void 0 : node.firstChild) || null;
840
578
  }
841
-
842
- // src/component.ts
843
- var _a;
844
- _a = NORMAL_COMPONENT;
845
- var Component = class {
846
- constructor(component, props = {}) {
847
- this.component = component;
848
- this.props = props;
849
- // Component rendered nodes (supports arrays and fragments)
850
- this.renderedNodes = [];
851
- // Component scope (unified context management)
852
- this.scope = null;
853
- // Component parent node
854
- this.parentNode = void 0;
855
- // Component before node
856
- this.beforeNode = void 0;
857
- // Component props (reactive and snapshot)
858
- this.reactiveProps = {};
859
- this._propSnapshots = {};
860
- // Component lifecycle state
861
- this.state = 0 /* INITIAL */;
862
- // Parent scope captured at construction time for correct hierarchy
863
- this.parentScope = null;
864
- // @ts-ignore
865
- this[_a] = true;
866
- this.key = props.key ? normalizeKey(props.key) : getComponentKey(component);
867
- this.reactiveProps = shallowReactive(__spreadValues({}, props));
868
- this.parentScope = getActiveScope();
869
- for (const key in props) {
870
- const val = props[key];
871
- if (isObject(val)) {
872
- this._propSnapshots[key] = isArray(val) ? [...val] : __spreadValues({}, val);
873
- }
874
- }
579
+ function next(node, step = 1) {
580
+ while (node && step > 0) {
581
+ node = node.nextSibling;
582
+ step--;
875
583
  }
876
- get isConnected() {
877
- return this.state === 2 /* MOUNTED */;
584
+ return node || null;
585
+ }
586
+ function nthChild(node, index) {
587
+ if (!node || index < 0) return null;
588
+ let current = node.firstChild;
589
+ while (current && index > 0) {
590
+ current = current.nextSibling;
591
+ index--;
878
592
  }
879
- get firstChild() {
880
- for (const node of this.renderedNodes) {
881
- const dom = getFirstDOMNode(node);
882
- if (dom) return dom;
883
- }
884
- return void 0;
593
+ return current || null;
594
+ }
595
+
596
+ // src/operations/event.ts
597
+ function addEvent(el, event, handler, options) {
598
+ if (!(options == null ? void 0 : options.delegate)) {
599
+ el.addEventListener(event, handler, options);
600
+ return () => el.removeEventListener(event, handler, options);
885
601
  }
886
- mount(parentNode, beforeNode) {
887
- var _a2, _b;
888
- this.parentNode = parentNode;
889
- this.beforeNode = beforeNode;
890
- this.state = 1 /* MOUNTING */;
891
- if (this.renderedNodes.length > 0) {
892
- for (const node of this.renderedNodes) {
893
- insertNode(parentNode, node, beforeNode);
894
- }
895
- this.state = 2 /* MOUNTED */;
896
- return this.renderedNodes;
897
- }
898
- const parentScope = (_a2 = this.parentScope) != null ? _a2 : getActiveScope();
899
- this.scope = createScope(parentScope);
900
- setActiveScope(this.scope);
901
- let result = this.component(this.reactiveProps);
902
- if (isFunction(result)) {
903
- result = result(this.reactiveProps);
904
- }
905
- if (isSignal(result) || isComputed(result)) {
906
- result = result.value;
602
+ const selector = options.delegate;
603
+ const wrappedHandler = (e) => {
604
+ const target = e.target;
605
+ if (target.matches(selector) || target.closest(selector)) {
606
+ handler.call(el, e);
907
607
  }
908
- const renderedNodes = (_b = insert(parentNode, result, beforeNode)) != null ? _b : [];
909
- this.renderedNodes = renderedNodes;
910
- this.applyProps(this.props);
911
- this.state = 2 /* MOUNTED */;
912
- triggerMountHooks(this.scope);
913
- return this.renderedNodes;
914
- }
915
- update(prevNode) {
916
- if (this.key !== prevNode.key) {
917
- this.mount(prevNode.parentNode, prevNode.beforeNode);
918
- return this;
919
- }
920
- this.parentNode = prevNode.parentNode;
921
- this.beforeNode = prevNode.beforeNode;
922
- this.scope = prevNode.scope;
923
- this.parentScope = prevNode.parentScope;
924
- this.renderedNodes = prevNode.renderedNodes;
925
- this.state = prevNode.state;
926
- this.reactiveProps = prevNode.reactiveProps;
927
- this._propSnapshots = prevNode._propSnapshots;
928
- this._updateReactiveProps(this.props);
929
- if (!this.isConnected && this.parentNode) {
930
- this.mount(this.parentNode, this.beforeNode);
931
- return this;
932
- }
933
- if (this.scope) {
934
- setActiveScope(this.scope);
935
- this.applyProps(this.props);
936
- triggerUpdateHooks(this.scope);
937
- }
938
- return this;
608
+ };
609
+ const _a2 = options, { delegate: _ } = _a2, nativeOptions = __objRest(_a2, ["delegate"]);
610
+ el.addEventListener(event, wrappedHandler, nativeOptions);
611
+ return () => {
612
+ el.removeEventListener(event, wrappedHandler, nativeOptions);
613
+ };
614
+ }
615
+ function registerScopedHook(scope, listKey, hook) {
616
+ let hookList = scope[listKey];
617
+ if (!hookList) {
618
+ hookList = [];
619
+ scope[listKey] = hookList;
939
620
  }
940
- /**
941
- * Update reactive props by comparing with current values
942
- */
943
- _updateReactiveProps(props) {
944
- for (const key in props) {
945
- if (key === "key") continue;
946
- const newValue = props[key];
947
- const oldValue = this.reactiveProps[key];
948
- if (newValue === oldValue && !this._propSnapshots[key]) continue;
949
- const isNewValueObject = isObject(newValue);
950
- if (isNewValueObject) {
951
- const snapshot = this._propSnapshots[key];
952
- if (snapshot && shallowCompare(newValue, snapshot)) continue;
953
- const newSnapshot = isArray(newValue) ? [...newValue] : __spreadValues({}, newValue);
954
- this.reactiveProps[key] = newSnapshot;
955
- this._propSnapshots[key] = newSnapshot;
956
- } else {
957
- if (hasChanged(newValue, oldValue)) {
958
- this.reactiveProps[key] = newValue;
959
- if (this._propSnapshots[key]) {
960
- delete this._propSnapshots[key];
621
+ hookList.push(hook);
622
+ }
623
+ function executeHooks(hooks, scopeId, phase) {
624
+ const len = hooks.length;
625
+ if (len === 0) return;
626
+ let pending;
627
+ for (let i = 0; i < len; i++) {
628
+ try {
629
+ const result = hooks[i]();
630
+ if (isPromise(result)) {
631
+ const safePromise = result.catch((error_) => {
632
+ if (true) {
633
+ error(`Scope(${scopeId}): Async ${phase} hook rejected:`, error_);
961
634
  }
962
- }
635
+ });
636
+ (pending || (pending = [])).push(safePromise);
637
+ }
638
+ } catch (error_) {
639
+ {
640
+ error(`Scope(${scopeId}): Error in ${phase} hook:`, error_);
963
641
  }
964
642
  }
965
643
  }
966
- unwrapRenderResult(result) {
967
- if (isFunction(result)) {
968
- result = result(this.reactiveProps);
969
- }
970
- if (isSignal(result) || isComputed(result)) {
971
- result = result.value;
972
- }
973
- if (isComponent(result)) {
974
- result = result.mount(this.parentNode, this.beforeNode);
975
- }
976
- if (isPromise(result)) {
977
- result = result.then((r) => this.unwrapRenderResult(r));
978
- }
979
- return result;
644
+ if (!pending) return;
645
+ return Promise.all(pending).then(() => {
646
+ });
647
+ }
648
+ function onMount(hook) {
649
+ const scope = getActiveScope();
650
+ if (!scope) {
651
+ error("onMount() must be called within a scope");
652
+ return;
980
653
  }
981
- forceUpdate() {
982
- if (this.state === 5 /* DESTROYED */ || !this.parentNode || !this.scope) {
983
- return;
984
- }
985
- const originalNodes = [...this.renderedNodes];
654
+ if (scope.isMounted) {
986
655
  try {
987
- runWithScope(this.scope, () => {
988
- let result = this.component(this.reactiveProps);
989
- if (isFunction(result)) {
990
- result = result(this.reactiveProps);
991
- }
992
- if (isSignal(result) || isComputed(result)) {
993
- result = result.value;
994
- }
995
- const newNodes = coerceArray(result);
996
- const anchor = this._getAnchorNode();
997
- if (!this.parentNode) return;
998
- for (const node of this.renderedNodes) {
999
- removeNode(node);
1000
- }
1001
- for (const node of newNodes) {
1002
- insertNode(this.parentNode, node, anchor);
1003
- }
1004
- this.renderedNodes = newNodes;
1005
- });
1006
- if (this.scope) {
1007
- triggerUpdateHooks(this.scope);
656
+ const result = hook();
657
+ if (isPromise(result)) {
658
+ result.catch((error_) => {
659
+ if (true) error(`Scope(${scope.id}): Async mount hook rejected:`, error_);
660
+ });
1008
661
  }
1009
- } catch (error8) {
1010
- this.renderedNodes = originalNodes;
1011
- throw error8;
662
+ } catch (error_) {
663
+ error(`Scope(${scope.id}): Error in mount hook:`, error_);
664
+ }
665
+ return;
666
+ }
667
+ registerScopedHook(scope, "onMount", hook);
668
+ }
669
+ function onUpdate(hook) {
670
+ const scope = getActiveScope();
671
+ if (!scope) {
672
+ error("onUpdate() must be called within a scope");
673
+ return;
674
+ }
675
+ registerScopedHook(scope, "onUpdate", hook);
676
+ }
677
+ function onDestroy(hook) {
678
+ const scope = getActiveScope();
679
+ if (!scope) {
680
+ error("onDestroy() must be called within a scope");
681
+ return;
682
+ }
683
+ registerScopedHook(scope, "onDestroy", hook);
684
+ }
685
+ function triggerMountHooks(scope) {
686
+ if (scope.isDestroyed) {
687
+ return;
688
+ }
689
+ if (!scope.onMount || scope.onMount.length === 0) {
690
+ scope.isMounted = true;
691
+ return;
692
+ }
693
+ const mountHooks = scope.onMount;
694
+ const result = runWithScope(scope, () => executeHooks(mountHooks, scope.id, "mount"));
695
+ mountHooks.length = 0;
696
+ scope.isMounted = true;
697
+ return result;
698
+ }
699
+ function triggerUpdateHooks(scope) {
700
+ if (scope.isDestroyed || !scope.onUpdate || scope.onUpdate.length === 0) return;
701
+ return runWithScope(scope, () => executeHooks(scope.onUpdate, scope.id, "update"));
702
+ }
703
+
704
+ // src/component.ts
705
+ function syncDescriptors(target, source, pruneMissing = false) {
706
+ const seen = pruneMissing ? /* @__PURE__ */ new Set() : null;
707
+ for (const key of Object.getOwnPropertyNames(source)) {
708
+ seen == null ? void 0 : seen.add(key);
709
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
710
+ }
711
+ if (seen) {
712
+ for (const key of Object.getOwnPropertyNames(target)) {
713
+ if (!seen.has(key)) delete target[key];
1012
714
  }
1013
715
  }
716
+ }
717
+ function readProp(source, key) {
718
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
719
+ return descriptor.get ? descriptor.get.call(source) : descriptor.value;
720
+ }
721
+ var _a;
722
+ _a = "normal" /* NORMAL */;
723
+ var Component = class {
724
+ constructor(component, props = {}) {
725
+ this.component = component;
726
+ this.props = props;
727
+ this[_a] = true;
728
+ this.scope = null;
729
+ this.state = 0 /* INITIAL */;
730
+ this.beforeNode = void 0;
731
+ this.renderedNodes = [];
732
+ this.firstChild = void 0;
733
+ this.parentNode = void 0;
734
+ this.rootEventCleanups = [];
735
+ this.parentScope = getActiveScope();
736
+ const container = {};
737
+ syncDescriptors(container, props);
738
+ this.reactiveProps = shallowReactive(container);
739
+ }
1014
740
  /**
1015
- * Get anchor node for insertion
741
+ * Mount the component into `parentNode` (optionally before `beforeNode`).
742
+ * If already rendered, the existing DOM is re-inserted without re-running
743
+ * the component function.
1016
744
  */
1017
- _getAnchorNode() {
1018
- if (this.beforeNode) return this.beforeNode;
745
+ mount(parentNode, beforeNode) {
746
+ var _a2;
747
+ this.parentNode = parentNode;
748
+ this.beforeNode = beforeNode;
749
+ this.state = 1 /* MOUNTING */;
1019
750
  if (this.renderedNodes.length > 0) {
1020
- const lastNode = this.renderedNodes[this.renderedNodes.length - 1];
1021
- const lastDom = getFirstDOMNode(lastNode);
1022
- if (lastDom) {
1023
- return lastDom.nextSibling;
751
+ for (const node of this.renderedNodes) {
752
+ insertNode(parentNode, node, beforeNode);
1024
753
  }
754
+ this.state = 2 /* MOUNTED */;
755
+ return this.renderedNodes;
1025
756
  }
1026
- return void 0;
757
+ const scope = createScope((_a2 = this.parentScope) != null ? _a2 : getActiveScope());
758
+ this.scope = scope;
759
+ const renderedNodes = runWithScope(scope, () => {
760
+ var _a3;
761
+ let result = this.component(this.reactiveProps);
762
+ if (isFunction(result)) {
763
+ result = result(this.reactiveProps);
764
+ }
765
+ if (isSignal(result) || isComputed(result)) {
766
+ result = result.value;
767
+ }
768
+ return (_a3 = insert(parentNode, result, beforeNode)) != null ? _a3 : [];
769
+ });
770
+ this.renderedNodes = renderedNodes;
771
+ this.firstChild = renderedNodes[0];
772
+ this.syncSpecialProps(this.props);
773
+ this.state = 2 /* MOUNTED */;
774
+ triggerMountHooks(scope);
775
+ return this.renderedNodes;
776
+ }
777
+ /**
778
+ * Re-install props into the same `reactiveProps` container (preserving
779
+ * any closures already holding a reference to it) and re-apply
780
+ * refs/events against the current root element.
781
+ */
782
+ update(props) {
783
+ this.props = props;
784
+ const scope = this.scope;
785
+ if (!scope || scope.isDestroyed) return;
786
+ syncDescriptors(
787
+ this.reactiveProps,
788
+ props != null ? props : {},
789
+ /* pruneMissing */
790
+ true
791
+ );
792
+ this.syncSpecialProps(props);
793
+ triggerUpdateHooks(scope);
1027
794
  }
1028
795
  /**
1029
- * Destroy component
796
+ * Tear down and re-mount the component at its current insertion point.
797
+ * No-op if the component has never been mounted.
798
+ */
799
+ forceUpdate() {
800
+ if (!this.parentNode) return;
801
+ const parent = this.parentNode;
802
+ const before = this.beforeNode;
803
+ this.destroy();
804
+ this.mount(parent, before);
805
+ }
806
+ /**
807
+ * Dispose the scope, remove all rendered nodes, and clear bookkeeping.
808
+ * Idempotent: subsequent calls are no-ops.
1030
809
  */
1031
810
  destroy() {
1032
- if (this.state === 4 /* DESTROYING */ || this.state === 5 /* DESTROYED */) {
1033
- return;
1034
- }
1035
- this.state = 4 /* DESTROYING */;
1036
- for (const node of this.renderedNodes) {
1037
- removeNode(node);
1038
- }
1039
811
  const scope = this.scope;
1040
- if (scope) {
1041
- disposeScope(scope);
1042
- this.scope = null;
1043
- }
812
+ if (!scope || scope.isDestroyed) return;
813
+ this.scope = null;
814
+ this.releaseSpecialProps();
815
+ disposeScope(scope);
816
+ for (const node of this.renderedNodes) removeNode(node);
1044
817
  this.renderedNodes = [];
818
+ this.firstChild = void 0;
1045
819
  this.parentNode = void 0;
1046
- this.beforeNode = void 0;
1047
- this.parentScope = null;
1048
- this.reactiveProps = {};
1049
- this.props = {};
1050
- this.state = 5 /* DESTROYED */;
1051
820
  }
1052
- applyProps(props) {
821
+ /**
822
+ * Apply props that bind to the root DOM element rather than flowing into
823
+ * the component body: `ref` (signal/function) and `onXxx` event handlers.
824
+ * The render-facing `reactiveProps` already has those keys; here we just
825
+ * wire them to the actual DOM node.
826
+ */
827
+ syncSpecialProps(props) {
1053
828
  if (!props) return;
1054
- const firstElement = this.firstChild;
1055
- for (const [propName, propValue] of Object.entries(props)) {
1056
- if (startsWith(propName, EVENT_PREFIX)) {
1057
- if (!firstElement || !isHTMLElement(firstElement)) return;
1058
- const eventName = propName.slice(EVENT_PREFIX.length).toLowerCase();
1059
- addEventListener(firstElement, eventName, propValue);
1060
- } else if (propName === REF_KEY && isSignal(propValue)) {
1061
- propValue.value = firstElement;
829
+ const root = this.firstChild;
830
+ if (!root) return;
831
+ this.releaseSpecialProps();
832
+ for (const key of Object.getOwnPropertyNames(props)) {
833
+ const value = readProp(props, key);
834
+ if (key === REF_KEY) {
835
+ this.rootRefCleanup = this.bindRootRef(value, root);
836
+ continue;
837
+ }
838
+ if (isOn(key) && isFunction(value)) {
839
+ const eventName = key.slice(2).toLowerCase();
840
+ this.rootEventCleanups.push(addEvent(root, eventName, value));
1062
841
  }
1063
842
  }
1064
- this.props = props;
843
+ }
844
+ /**
845
+ * Remove all listeners/ref bindings currently attached to the root element.
846
+ */
847
+ releaseSpecialProps() {
848
+ for (const cleanup of this.rootEventCleanups) {
849
+ cleanup();
850
+ }
851
+ this.rootEventCleanups.length = 0;
852
+ if (this.rootRefCleanup) {
853
+ this.rootRefCleanup();
854
+ this.rootRefCleanup = void 0;
855
+ }
856
+ }
857
+ /**
858
+ * Bind the root ref prop and return a cleanup that restores the previous ref state.
859
+ */
860
+ bindRootRef(value, root) {
861
+ if (isFunction(value)) {
862
+ value(root);
863
+ return () => value(null);
864
+ }
865
+ if (isSignal(value)) {
866
+ const previousValue = value.value;
867
+ value.value = root;
868
+ return () => {
869
+ if (value.value === root) {
870
+ value.value = previousValue;
871
+ }
872
+ };
873
+ }
874
+ return void 0;
1065
875
  }
1066
876
  };
1067
877
  function isComponent(node) {
1068
- return !!node && !!node[NORMAL_COMPONENT];
878
+ return !!node && !!node["normal" /* NORMAL */];
1069
879
  }
1070
880
  function createComponent(componentFn, props) {
1071
881
  if (isComponent(componentFn)) {
@@ -1091,17 +901,72 @@ function template(html) {
1091
901
  function createApp(component, target) {
1092
902
  const container = isString(target) ? document.querySelector(target) : target;
1093
903
  if (!container) {
1094
- error(`Target element not found: ${target}`);
904
+ {
905
+ warn(`Target element not found: ${target}`);
906
+ }
1095
907
  return;
1096
908
  }
1097
- const existingContext = container.innerHTML;
1098
- if (existingContext) {
1099
- error(`Target element is not empty, it will be delete: ${target}`);
909
+ const existingContent = container.innerHTML;
910
+ if (existingContent) {
911
+ {
912
+ warn(`Target element is not empty, it will be cleared: ${target}`);
913
+ }
1100
914
  container.innerHTML = "";
1101
915
  }
1102
- const rootComponent = isComponent(component) ? component : createComponent(component);
1103
- rootComponent.mount(container);
1104
- return rootComponent;
916
+ const scope = createScope();
917
+ let rootNode;
918
+ try {
919
+ runWithScope(scope, () => {
920
+ const mountedRoot = createComponent(component);
921
+ if (isComponent(mountedRoot)) {
922
+ rootNode = mountedRoot;
923
+ insertNode(container, mountedRoot);
924
+ }
925
+ });
926
+ } catch (error_) {
927
+ disposeScope(scope);
928
+ throw error_;
929
+ }
930
+ return {
931
+ root: rootNode,
932
+ unmount: () => {
933
+ disposeScope(scope);
934
+ rootNode == null ? void 0 : rootNode.destroy();
935
+ }
936
+ };
937
+ }
938
+ function hydrate(component, target) {
939
+ const container = isString(target) ? document.querySelector(target) : target;
940
+ if (!container) {
941
+ {
942
+ warn(`[essor] hydrate: target element not found: ${target}`);
943
+ }
944
+ return;
945
+ }
946
+ beginHydration(container);
947
+ const scope = createScope();
948
+ let rootNode;
949
+ try {
950
+ runWithScope(scope, () => {
951
+ const mountedRoot = createComponent(component);
952
+ if (isComponent(mountedRoot)) {
953
+ rootNode = mountedRoot;
954
+ insert(container, mountedRoot);
955
+ }
956
+ });
957
+ } catch (error_) {
958
+ disposeScope(scope);
959
+ throw error_;
960
+ } finally {
961
+ endHydration();
962
+ }
963
+ return {
964
+ root: rootNode,
965
+ unmount: () => {
966
+ disposeScope(scope);
967
+ rootNode == null ? void 0 : rootNode.destroy();
968
+ }
969
+ };
1105
970
  }
1106
971
  function provide(key, value) {
1107
972
  const scope = getActiveScope();
@@ -1127,68 +992,66 @@ function inject(key, defaultValue) {
1127
992
  let current = scope;
1128
993
  while (current) {
1129
994
  if (current.provides) {
1130
- const value = current.provides.get(key);
1131
- if (value) {
1132
- return value;
995
+ if (current.provides.has(key)) {
996
+ return current.provides.get(key);
1133
997
  }
1134
998
  }
1135
999
  current = current.parent;
1136
1000
  }
1137
1001
  return defaultValue;
1138
1002
  }
1139
- function reTarget(event, value) {
1140
- Object.defineProperty(event, "target", {
1003
+ function reTargetEvent(e, value) {
1004
+ Object.defineProperty(e, "target", {
1141
1005
  configurable: true,
1142
1006
  value
1143
1007
  });
1144
1008
  }
1145
- function handleNodeEvent(node, event, key) {
1146
- const handler = node[`_$${key}`];
1147
- if (handler && isFunction(handler) && !node.disabled) {
1148
- const data = node[`${key}Data`];
1149
- data ? handler.call(node, data, event) : handler.call(node, event);
1150
- if (event.cancelBubble) return false;
1151
- }
1152
- if (node.host && !isString(node.host) && !node.host._$host && isFunction(node.contains) && node.contains(event.target)) {
1153
- reTarget(event, node.host);
1154
- }
1155
- return true;
1156
- }
1157
- function walkUpTree(startNode, event, key) {
1158
- let node = startNode;
1159
- while (handleNodeEvent(node, event, key) && (node = node._$host || node.parentNode || node.host)) ;
1160
- return node;
1161
- }
1162
- function eventHandler(event) {
1163
- let node = event.target;
1164
- const key = `${event.type}`;
1165
- const oriTarget = event.target;
1166
- const oriCurrentTarget = event.currentTarget;
1167
- Object.defineProperty(event, "currentTarget", {
1009
+ function eventHandler(e) {
1010
+ let node = e.target;
1011
+ const key = e.type;
1012
+ const oriTarget = e.target;
1013
+ const oriCurrentTarget = e.currentTarget;
1014
+ const handleNode = () => {
1015
+ const handler = node[`_$${key}`];
1016
+ if (handler && isFunction(handler) && !node.disabled) {
1017
+ const data = node[`${key}Data`];
1018
+ data ? handler.call(node, data, e) : handler.call(node, e);
1019
+ if (e.cancelBubble) return false;
1020
+ }
1021
+ if (node.host && !isString(node.host) && !node.host._$host && isFunction(node.contains) && node.contains(e.target)) {
1022
+ reTargetEvent(e, node.host);
1023
+ }
1024
+ return true;
1025
+ };
1026
+ const walkUpTree = () => {
1027
+ while (handleNode() && (node = node._$host || node.parentNode || node.host)) ;
1028
+ };
1029
+ Object.defineProperty(e, "currentTarget", {
1168
1030
  configurable: true,
1031
+ /**
1032
+ * Returns the current delegated target for the event.
1033
+ */
1169
1034
  get() {
1170
1035
  return node || document;
1171
1036
  }
1172
1037
  });
1173
- if (event.composedPath) {
1174
- const path = event.composedPath();
1175
- reTarget(event, path[0]);
1038
+ if (e.composedPath) {
1039
+ const path = e.composedPath();
1040
+ reTargetEvent(e, path[0]);
1176
1041
  for (let i = 0; i < path.length - 2; i++) {
1177
1042
  node = path[i];
1178
- if (!handleNodeEvent(node, event, key)) break;
1043
+ if (!handleNode()) break;
1179
1044
  if (node._$host) {
1180
1045
  node = node._$host;
1181
- node = walkUpTree(node, event, key);
1046
+ walkUpTree();
1182
1047
  break;
1183
1048
  }
1184
1049
  if (node.parentNode === oriCurrentTarget) {
1185
1050
  break;
1186
1051
  }
1187
1052
  }
1188
- } else {
1189
- node = walkUpTree(node, event, key);
1190
- }
1191
- reTarget(event, oriTarget);
1053
+ } else walkUpTree();
1054
+ reTargetEvent(e, oriTarget);
1192
1055
  }
1193
1056
  var $EVENTS = /* @__PURE__ */ Symbol("_$EVENTS");
1194
1057
  function delegateEvents(eventNames, document2 = window.document) {
@@ -1201,424 +1064,354 @@ function delegateEvents(eventNames, document2 = window.document) {
1201
1064
  }
1202
1065
  }
1203
1066
  }
1204
-
1205
- // src/utils/props.ts
1206
- function omitProps(target, keys) {
1207
- const excludeSet = new Set(keys);
1208
- return new Proxy(target, {
1209
- // Intercept property reads
1210
- get(obj, prop) {
1211
- if (excludeSet.has(prop)) {
1212
- return void 0;
1213
- }
1214
- return Reflect.get(obj, prop);
1215
- },
1216
- // Intercept property enumeration (for...in, Object.keys, etc.)
1217
- ownKeys(obj) {
1218
- return Reflect.ownKeys(obj).filter((key) => !excludeSet.has(key));
1219
- },
1220
- // Intercept property descriptor retrieval
1221
- getOwnPropertyDescriptor(obj, prop) {
1222
- if (excludeSet.has(prop)) {
1223
- return void 0;
1224
- }
1225
- return Reflect.getOwnPropertyDescriptor(obj, prop);
1226
- },
1227
- // Intercept the 'in' operator
1228
- has(obj, prop) {
1229
- if (excludeSet.has(prop)) {
1230
- return false;
1231
- }
1232
- return Reflect.has(obj, prop);
1233
- }
1234
- });
1235
- }
1236
-
1237
- // src/hydration/shared.ts
1238
- var isHydrationActive = false;
1239
- function startHydration() {
1240
- isHydrationActive = true;
1241
- }
1242
- function endHydration() {
1243
- isHydrationActive = false;
1244
- }
1245
- function isHydrating() {
1246
- return isHydrationActive;
1247
- }
1248
- var hydrationCounter = 0;
1249
- function getHydrationKey() {
1250
- return `${hydrationCounter++}`;
1251
- }
1252
- function resetHydrationKey() {
1253
- hydrationCounter = 0;
1254
- }
1255
-
1256
- // src/hydration/hydration.ts
1257
- var DATA_IDX_REGEX = /^\d+-\d+$/;
1258
- function getRenderedElement(temp) {
1259
- return () => {
1260
- if (!isBrowser()) {
1261
- return null;
1262
- }
1263
- const key = getHydrationKey();
1264
- const node = document.querySelector(`[data-hk="${key}"]`);
1265
- if (node) {
1266
- return node;
1267
- }
1268
- return template(temp)();
1269
- };
1270
- }
1271
- function mapSSRNodes(templateEl, idx) {
1272
- const hk = templateEl.dataset.hk;
1273
- if (!hk) {
1274
- return mapNodes(templateEl, idx);
1275
- }
1276
- const nodesList = [];
1277
- const elements = templateEl.querySelectorAll(`[data-idx^="${hk}"]`);
1278
- if (elements.length > 0) {
1279
- nodesList.push(
1280
- ...Array.from(elements).filter((item) => {
1281
- const idxAttr = item.dataset.idx;
1282
- return idxAttr !== null && DATA_IDX_REGEX.test(idxAttr);
1283
- }).map((item) => {
1284
- const idxAttr = item.dataset.idx || "";
1285
- const [hkPart, idxPart] = idxAttr.split("-");
1286
- return {
1287
- hk: hkPart,
1288
- idx: idxPart,
1289
- node: item
1290
- };
1291
- })
1292
- );
1293
- }
1294
- const commentNodes = [];
1295
- const walkNodes = (node) => {
1296
- if (node.nodeType === Node.COMMENT_NODE && node.textContent && DATA_IDX_REGEX.test(node.textContent)) {
1297
- const [hkPart, idxPart] = node.textContent.split("-");
1298
- commentNodes.push({
1299
- hk: hkPart,
1300
- idx: idxPart,
1301
- node
1302
- });
1303
- }
1304
- let child = node.firstChild;
1305
- while (child) {
1306
- walkNodes(child);
1307
- child = child.nextSibling;
1308
- }
1309
- };
1310
- walkNodes(templateEl);
1311
- nodesList.push(...commentNodes);
1312
- const nodes = [templateEl];
1313
- idx.forEach((indexValue) => {
1314
- const node = nodesList.find((item) => item.idx === String(indexValue));
1315
- if (node) {
1316
- nodes.push(node.node);
1317
- }
1318
- });
1319
- return nodes;
1320
- }
1321
- function hydrate(component, container) {
1322
- startHydration();
1323
- resetHydrationKey();
1324
- try {
1325
- const rootElement = isString(container) ? document.querySelector(container) : container;
1326
- if (!rootElement) {
1327
- error("Hydration error: Root element not found");
1328
- return void 0;
1329
- }
1330
- const rootComponent = createComponent(component);
1331
- rootComponent.mount(rootElement);
1332
- endHydration();
1333
- return rootComponent;
1334
- } catch (error_) {
1335
- error("Hydration error:", error_);
1336
- endHydration();
1337
- return void 0;
1338
- }
1339
- }
1340
- function patchClass(el, prev, next, isSVG = false) {
1341
- if (prev === next) {
1342
- return;
1343
- }
1344
- if (isHydrating()) {
1345
- return;
1346
- }
1347
- const normalizedNext = normalizeClass(next);
1348
- const normalizedPrev = normalizeClass(prev);
1349
- if (normalizedNext && normalizedPrev === normalizedNext) {
1350
- return;
1351
- }
1352
- if (!normalizedNext) {
1353
- el.removeAttribute("class");
1354
- } else if (isSVG) {
1355
- el.setAttribute("class", normalizedNext);
1356
- } else {
1357
- el.className = normalizedNext;
1358
- }
1359
- }
1360
- function normalizeClass(value) {
1361
- return normalizeClassName(value);
1362
- }
1363
- var importantRE = /\s*!important$/;
1364
- var prefixes = ["Webkit", "Moz", "ms"];
1365
- var prefixCache = {};
1366
- function patchStyle(el, prev, next) {
1367
- const style = el.style;
1368
- const isCssString = isString(next);
1369
- if (isHydrating()) {
1370
- return;
1371
- }
1372
- if (next && isCssString) {
1373
- if (prev !== next) {
1374
- style.cssText = next;
1375
- }
1376
- return;
1377
- }
1378
- if (!next) {
1379
- if (prev) {
1380
- el.removeAttribute("style");
1381
- }
1382
- return;
1383
- }
1384
- if (prev && !isString(prev)) {
1385
- for (const key in prev) {
1386
- if (!next || next[key] == null) {
1387
- setStyle(style, key, "");
1388
- }
1389
- }
1390
- } else if (prev && isString(prev)) {
1391
- const prevStyles = prev.split(";");
1392
- for (const stylePart of prevStyles) {
1393
- const colonIndex = stylePart.indexOf(":");
1394
- if (colonIndex > 0) {
1395
- const key = stylePart.slice(0, colonIndex).trim();
1396
- if (next && isObject(next) && next[key] == null) {
1397
- setStyle(style, key, "");
1398
- }
1399
- }
1400
- }
1401
- }
1402
- if (next && !isString(next)) {
1403
- for (const key in next) {
1404
- const value = next[key];
1405
- if ((!prev || isString(prev) || prev[key] !== value) && value != null) {
1406
- setStyle(style, key, value);
1407
- }
1408
- }
1409
- }
1410
- }
1411
- function setStyle(style, name, val) {
1412
- if (isArray(val)) {
1413
- for (const element of val) {
1414
- setStyle(style, name, element);
1067
+ function clearDelegatedEvents(document2 = window.document) {
1068
+ const docWithEvents = document2;
1069
+ const eventSet = docWithEvents[$EVENTS];
1070
+ if (eventSet) {
1071
+ for (const name of eventSet.keys()) {
1072
+ document2.removeEventListener(name, eventHandler);
1415
1073
  }
1416
- return;
1417
- }
1418
- if (val == null || val === "") {
1419
- val = "";
1420
- }
1421
- if (name.startsWith("--")) {
1422
- style.setProperty(name, val);
1423
- return;
1424
- }
1425
- const prefixed = autoPrefix(style, name);
1426
- if (isString(val) && importantRE.test(val)) {
1427
- style.setProperty(camelCase(prefixed), val.replace(importantRE, ""), "important");
1428
- } else {
1429
- style[prefixed] = val;
1074
+ delete docWithEvents[$EVENTS];
1430
1075
  }
1431
1076
  }
1432
- function autoPrefix(style, rawName) {
1433
- const cached = prefixCache[rawName];
1434
- if (cached) {
1435
- return cached;
1436
- }
1437
- let name = camelCase(rawName);
1438
- if (name !== "filter" && name in style) {
1439
- return prefixCache[rawName] = name;
1440
- }
1441
- name = capitalize(name);
1442
- for (const prefix of prefixes) {
1443
- const prefixed = prefix + name;
1444
- if (prefixed in style) {
1445
- return prefixCache[rawName] = prefixed;
1446
- }
1077
+ function addEventListener(element, event, handler, options) {
1078
+ const cleanup = addEvent(element, event, handler, options);
1079
+ if (getActiveScope()) {
1080
+ onCleanup(cleanup);
1447
1081
  }
1448
- return rawName;
1449
1082
  }
1450
- function patchAttr(el, key, prev, next) {
1451
- if (key === KEY_PROP) {
1452
- if (next == null) {
1453
- setNodeKey(el, void 0);
1454
- } else {
1455
- setNodeKey(el, String(next));
1456
- }
1457
- return;
1458
- }
1459
- if (key === SPREAD_NAME) {
1460
- {
1461
- if (!isObject(next)) {
1462
- warn("spread attribute must be an object");
1463
- }
1464
- }
1465
- Object.keys(next).forEach((k) => {
1466
- patchAttr(el, k, prev == null ? void 0 : prev[k], next == null ? void 0 : next[k]);
1467
- });
1468
- return;
1469
- }
1470
- if (isHydrating()) {
1471
- return;
1472
- }
1473
- const elementIsSVG = (el == null ? void 0 : el.namespaceURI) === SVG_NAMESPACE;
1474
- const isXlink = elementIsSVG && key.startsWith("xlink:");
1475
- const isXmlns = elementIsSVG && key.startsWith("xmlns:");
1476
- const isBoolean = isSpecialBooleanAttr(key) || isBooleanAttr(key);
1477
- if (prev === next) {
1478
- return;
1083
+
1084
+ // src/binding.ts
1085
+ var INPUT_CHECKBOX_CHECKED = {
1086
+ event: "change",
1087
+ forceChangeEvent: true,
1088
+ read: (n) => n.checked,
1089
+ write: (n, v) => {
1090
+ const el = n;
1091
+ const next2 = Boolean(v);
1092
+ if (el.checked !== next2) el.checked = next2;
1479
1093
  }
1480
- const lowerKey = key.toLowerCase();
1481
- if (lowerKey.length > 2 && lowerKey.charCodeAt(0) === 111 && lowerKey.charCodeAt(1) === 110) {
1482
- return;
1094
+ };
1095
+ var INPUT_RADIO_CHECKED = {
1096
+ event: "change",
1097
+ forceChangeEvent: true,
1098
+ read: (n) => {
1099
+ const el = n;
1100
+ return el.checked ? el.value : "";
1101
+ },
1102
+ write: (n, v) => {
1103
+ const el = n;
1104
+ const next2 = String(v) === el.value;
1105
+ if (el.checked !== next2) el.checked = next2;
1483
1106
  }
1484
- if (lowerKey === "innerhtml") {
1485
- return;
1107
+ };
1108
+ var INPUT_FILE_FILES = {
1109
+ event: "change",
1110
+ forceChangeEvent: true,
1111
+ read: (n) => n.files,
1112
+ // Browsers do not allow programmatic writes to <input type="file">.
1113
+ write: () => {
1486
1114
  }
1487
- if (next == null) {
1488
- if (isXlink) {
1489
- el.removeAttributeNS(XLINK_NAMESPACE, key.slice(6));
1490
- } else if (isXmlns) {
1491
- const localName = key.slice(6);
1492
- el.removeAttributeNS(XMLNS_NAMESPACE, localName);
1115
+ };
1116
+ var INPUT_VALUE = {
1117
+ event: "input",
1118
+ needsComposition: true,
1119
+ read: (n) => n.value,
1120
+ write: (n, v) => {
1121
+ const el = n;
1122
+ const next2 = v == null ? "" : String(v);
1123
+ if (el.value !== next2) el.value = next2;
1124
+ }
1125
+ };
1126
+ var SELECT_VALUE = {
1127
+ event: "change",
1128
+ forceChangeEvent: true,
1129
+ read: (n) => {
1130
+ const s = n;
1131
+ return s.multiple ? Array.from(s.selectedOptions, (o) => o.value) : s.value;
1132
+ },
1133
+ write: (n, v) => {
1134
+ const s = n;
1135
+ if (s.multiple) {
1136
+ const set = new Set((Array.isArray(v) ? v : []).map(String));
1137
+ for (const opt of Array.from(s.options)) opt.selected = set.has(opt.value);
1493
1138
  } else {
1494
- el.removeAttribute(key);
1139
+ const next2 = v == null ? "" : String(v);
1140
+ if (s.value !== next2) s.value = next2;
1495
1141
  }
1496
- return;
1497
1142
  }
1498
- if (isXlink) {
1499
- el.setAttributeNS(XLINK_NAMESPACE, key, String(next));
1500
- return;
1501
- }
1502
- if (isXmlns) {
1503
- el.setAttributeNS(XMLNS_NAMESPACE, key, String(next));
1504
- return;
1143
+ };
1144
+ var TEXTAREA_VALUE = {
1145
+ event: "input",
1146
+ needsComposition: true,
1147
+ read: (n) => n.value,
1148
+ write: (n, v) => {
1149
+ const el = n;
1150
+ const next2 = v == null ? "" : String(v);
1151
+ if (el.value !== next2) el.value = next2;
1505
1152
  }
1506
- if (isBoolean) {
1507
- if (includeBooleanAttr(next)) {
1508
- el.setAttribute(key, "");
1509
- } else {
1510
- el.removeAttribute(key);
1153
+ };
1154
+ function resolveStrategy(node, prop) {
1155
+ const tag = node.nodeName;
1156
+ if (tag === "INPUT") {
1157
+ const type = node.type;
1158
+ if (prop === "checked") {
1159
+ return type === "radio" ? INPUT_RADIO_CHECKED : INPUT_CHECKBOX_CHECKED;
1160
+ }
1161
+ if (prop === "files") return INPUT_FILE_FILES;
1162
+ return INPUT_VALUE;
1163
+ }
1164
+ if (tag === "SELECT") return SELECT_VALUE;
1165
+ if (tag === "TEXTAREA") return TEXTAREA_VALUE;
1166
+ return {
1167
+ event: "input",
1168
+ read: (n) => n[prop],
1169
+ write: (n, v) => {
1170
+ n[prop] = v;
1511
1171
  }
1512
- return;
1172
+ };
1173
+ }
1174
+ function castValue(val, trim, number) {
1175
+ if (!isString(val)) return val;
1176
+ if (trim) val = val.trim();
1177
+ if (number && val !== "") {
1178
+ const parsed = Number(val);
1179
+ if (!Number.isNaN(parsed)) return parsed;
1513
1180
  }
1514
- const attrValue = isSymbol(next) ? String(next) : next;
1515
- const isUrlAttr = lowerKey === "href" || lowerKey === "src" || lowerKey === "xlink:href";
1516
- if (isUrlAttr && isString(attrValue)) {
1517
- const v = attrValue.trim().toLowerCase();
1518
- if (v.startsWith("javascript:") || v.startsWith("data:")) {
1181
+ return val;
1182
+ }
1183
+ function isFocused(node) {
1184
+ const root = node.getRootNode();
1185
+ return (root instanceof Document || root instanceof ShadowRoot) && root.activeElement === node;
1186
+ }
1187
+ function bindElement(node, prop, getter, setter, modifiers = {}) {
1188
+ if (!node) return;
1189
+ const strategy = resolveStrategy(node, prop);
1190
+ const { trim, number, lazy } = modifiers;
1191
+ const isFiles = prop === "files";
1192
+ const readModel = () => isFunction(getter) ? getter() : getter;
1193
+ const transform = (v) => isFiles ? v : castValue(v, trim, number);
1194
+ const eventName = lazy || strategy.forceChangeEvent ? "change" : strategy.event;
1195
+ let composing = false;
1196
+ const syncFromDom = () => {
1197
+ if (composing) return;
1198
+ const raw = strategy.read(node);
1199
+ if (raw === void 0) return;
1200
+ if (isFiles) {
1201
+ setter(raw);
1519
1202
  return;
1520
1203
  }
1204
+ const next2 = transform(raw);
1205
+ if (!Object.is(readModel(), next2)) {
1206
+ setter(next2);
1207
+ }
1208
+ };
1209
+ addEventListener(node, eventName, syncFromDom);
1210
+ if (!lazy && !isFiles && (trim || number) && eventName !== "change") {
1211
+ addEventListener(node, "change", () => {
1212
+ strategy.write(node, transform(strategy.read(node)));
1213
+ });
1521
1214
  }
1522
- if (elementIsSVG) {
1523
- el.setAttribute(key, String(attrValue));
1524
- } else {
1525
- if (key in el) {
1526
- try {
1527
- el[key] = attrValue;
1528
- } catch (e) {
1529
- el.setAttribute(key, String(attrValue));
1530
- }
1531
- } else {
1532
- el.setAttribute(key, String(attrValue));
1215
+ if (strategy.needsComposition && !lazy) {
1216
+ addEventListener(node, "compositionstart", () => {
1217
+ composing = true;
1218
+ });
1219
+ addEventListener(node, "compositionend", () => {
1220
+ if (!composing) return;
1221
+ composing = false;
1222
+ syncFromDom();
1223
+ });
1224
+ }
1225
+ const runner = effect(() => {
1226
+ const value = readModel();
1227
+ if (strategy.needsComposition && !lazy && isFocused(node)) {
1228
+ if (composing) return;
1229
+ const current = transform(strategy.read(node));
1230
+ if (Object.is(current, value)) return;
1533
1231
  }
1232
+ strategy.write(node, value);
1233
+ });
1234
+ if (getActiveScope()) {
1235
+ onCleanup(() => runner.stop());
1534
1236
  }
1535
1237
  }
1536
1238
 
1537
- // src/operations/event.ts
1538
- function addEvent(el, event, handler, options) {
1539
- if (!(options == null ? void 0 : options.delegate)) {
1540
- el.addEventListener(event, handler, options);
1541
- return () => el.removeEventListener(event, handler, options);
1542
- }
1543
- const selector = options.delegate;
1544
- const wrappedHandler = (e) => {
1545
- const target = e.target;
1546
- if (target.matches(selector) || target.closest(selector)) {
1547
- handler.call(el, e);
1239
+ // src/utils.ts
1240
+ function omitProps(target, keys) {
1241
+ const excludeSet = new Set(keys);
1242
+ return new Proxy(target, {
1243
+ /**
1244
+ * Returns a property unless it is excluded by the proxy.
1245
+ */
1246
+ get(obj, prop) {
1247
+ if (excludeSet.has(prop)) {
1248
+ return void 0;
1249
+ }
1250
+ return Reflect.get(obj, prop);
1251
+ },
1252
+ /**
1253
+ * Returns the enumerable keys that are not excluded from the proxy.
1254
+ */
1255
+ ownKeys(obj) {
1256
+ return Reflect.ownKeys(obj).filter((key) => !excludeSet.has(key));
1257
+ },
1258
+ /**
1259
+ * Returns the property descriptor unless the key is excluded.
1260
+ */
1261
+ getOwnPropertyDescriptor(obj, prop) {
1262
+ if (excludeSet.has(prop)) {
1263
+ return void 0;
1264
+ }
1265
+ return Reflect.getOwnPropertyDescriptor(obj, prop);
1266
+ },
1267
+ /**
1268
+ * Returns whether the requested value exists.
1269
+ */
1270
+ has(obj, prop) {
1271
+ if (excludeSet.has(prop)) {
1272
+ return false;
1273
+ }
1274
+ return Reflect.has(obj, prop);
1548
1275
  }
1549
- };
1550
- const cleanOptions = __spreadValues({}, options);
1551
- cleanOptions.delegate = void 0;
1552
- el.addEventListener(event, wrappedHandler, cleanOptions);
1553
- return () => {
1554
- el.removeEventListener(event, wrappedHandler, cleanOptions);
1555
- };
1276
+ });
1556
1277
  }
1278
+
1279
+ // src/components/Fragment.ts
1557
1280
  function Fragment(props) {
1558
- {
1559
- if (!props) {
1560
- error("Fragment component requires props");
1561
- return null;
1562
- }
1563
- }
1564
- if (!(props == null ? void 0 : props.children)) {
1565
- error("Fragment component requires children");
1566
- return null;
1567
- }
1568
- const { children } = props;
1569
- return children;
1281
+ if (!props || props.children == null) return null;
1282
+ return props.children;
1570
1283
  }
1571
1284
  Fragment[FRAGMENT_COMPONENT] = true;
1572
1285
  function isFragment(node) {
1573
1286
  return !!node && !!node[FRAGMENT_COMPONENT];
1574
1287
  }
1288
+ function resolveTarget(props) {
1289
+ const raw = isFunction(props.target) ? props.target() : props.target;
1290
+ if (raw == null) return null;
1291
+ if (isString(raw)) return document.querySelector(raw);
1292
+ return raw;
1293
+ }
1294
+ function evalDisabled(props) {
1295
+ return isFunction(props.disabled) ? !!props.disabled() : !!props.disabled;
1296
+ }
1575
1297
  function Portal(props) {
1298
+ if (isHydrating()) {
1299
+ const adopted = tryHydratePortal(props);
1300
+ if (adopted) return adopted;
1301
+ }
1576
1302
  const placeholder = document.createComment("portal");
1577
1303
  placeholder[PORTAL_COMPONENT] = true;
1578
- const children = props.children;
1579
- if (children) {
1580
- const childArray = isArray(children) ? children : [children];
1581
- const nodes = [];
1582
- onMount(() => {
1583
- const targetElement = isString(props.target) ? document.querySelector(props.target) : props.target;
1584
- if (!targetElement) {
1585
- if (true) {
1586
- warn(`[Portal] Target element not found: ${props.target}`);
1587
- }
1588
- return;
1304
+ const { children } = props;
1305
+ if (children == null) return placeholder;
1306
+ const ownerScope = getActiveScope();
1307
+ let innerScope = null;
1308
+ const mountAt = (parent, before) => {
1309
+ innerScope = createScope(ownerScope);
1310
+ runWithScope(innerScope, () => {
1311
+ insert(parent, () => children, before);
1312
+ });
1313
+ };
1314
+ const teardown = () => {
1315
+ if (innerScope) {
1316
+ disposeScope(innerScope);
1317
+ innerScope = null;
1318
+ }
1319
+ };
1320
+ const apply = (disabled, target) => {
1321
+ teardown();
1322
+ if (disabled) {
1323
+ const parent = placeholder.parentNode;
1324
+ if (!parent) return;
1325
+ mountAt(parent, placeholder);
1326
+ return;
1327
+ }
1328
+ if (!target) {
1329
+ {
1330
+ warn(`[Portal] Target element not found: ${String(props.target)}`);
1589
1331
  }
1590
- childArray.forEach((child) => {
1591
- if (child != null) {
1592
- const normalized = normalizeNode(child);
1593
- if (normalized) {
1594
- insertNode(targetElement, normalized);
1595
- nodes.push(normalized);
1596
- }
1597
- }
1598
- });
1599
- onCleanup(() => {
1600
- nodes.forEach((node) => {
1601
- if (!isString(node) && node.parentNode === targetElement) {
1602
- targetElement.removeChild(node);
1603
- }
1604
- });
1605
- });
1332
+ return;
1333
+ }
1334
+ mountAt(target);
1335
+ };
1336
+ let mounted = false;
1337
+ const effectRunner = effect(() => {
1338
+ const disabled = evalDisabled(props);
1339
+ const target = disabled ? null : resolveTarget(props);
1340
+ if (mounted) {
1341
+ apply(disabled, target);
1342
+ }
1343
+ });
1344
+ onMount(() => {
1345
+ mounted = true;
1346
+ const disabled = evalDisabled(props);
1347
+ const target = disabled ? null : resolveTarget(props);
1348
+ if (disabled || target) {
1349
+ apply(disabled, target);
1350
+ return;
1351
+ }
1352
+ queueMicrotask(() => {
1353
+ if (!placeholder.parentNode) return;
1354
+ apply(evalDisabled(props), resolveTarget(props));
1606
1355
  });
1607
- }
1356
+ });
1357
+ onCleanup(() => {
1358
+ effectRunner.stop();
1359
+ teardown();
1360
+ });
1608
1361
  return placeholder;
1609
1362
  }
1610
1363
  Portal[PORTAL_COMPONENT] = true;
1364
+ function tryHydratePortal(props) {
1365
+ if (evalDisabled(props)) return null;
1366
+ const anchor = consumeTeleportAnchor();
1367
+ if (!anchor) {
1368
+ {
1369
+ warn("[Portal] hydration mismatch: no <!--teleport-anchor--> at call site.");
1370
+ }
1371
+ return null;
1372
+ }
1373
+ const target = resolveTarget(props);
1374
+ if (!target) {
1375
+ {
1376
+ warn(`[Portal] hydration mismatch: target not found: ${String(props.target)}`);
1377
+ }
1378
+ return null;
1379
+ }
1380
+ const block = consumeTeleportBlock(target);
1381
+ if (!block) {
1382
+ {
1383
+ warn(
1384
+ `[Portal] hydration mismatch: no <!--teleport-start--> in target ${String(props.target)}`
1385
+ );
1386
+ }
1387
+ return null;
1388
+ }
1389
+ anchor[PORTAL_COMPONENT] = true;
1390
+ return anchor;
1391
+ }
1611
1392
  function isPortal(node) {
1612
1393
  return !!node && !!node[PORTAL_COMPONENT];
1613
1394
  }
1395
+ function clearContainer(el) {
1396
+ while (el.firstChild) {
1397
+ el.removeChild(el.firstChild);
1398
+ }
1399
+ }
1400
+ function resolveNodeValue(value) {
1401
+ let current = value;
1402
+ while (isFunction(current)) {
1403
+ current = current();
1404
+ }
1405
+ if (isSignal(current) || isComputed(current)) {
1406
+ return resolveNodeValue(current.value);
1407
+ }
1408
+ return current;
1409
+ }
1614
1410
  var SuspenseContext = /* @__PURE__ */ Symbol("SuspenseContext");
1615
1411
  function Suspense(props) {
1616
- if (isUndefined(document)) {
1617
- const fallback = props.fallback;
1618
- if (fallback) {
1619
- return String(fallback || "");
1620
- }
1621
- return "";
1412
+ var _a2;
1413
+ if (!isBrowser()) {
1414
+ return (_a2 = props.fallback) != null ? _a2 : "";
1622
1415
  }
1623
1416
  const container = document.createElement("div");
1624
1417
  container.style.display = "contents";
@@ -1626,19 +1419,42 @@ function Suspense(props) {
1626
1419
  let pendingCount = 0;
1627
1420
  let isShowingFallback = false;
1628
1421
  let resolvedChildren = null;
1629
- const showFallback = () => {
1630
- if (isShowingFallback) return;
1631
- isShowingFallback = true;
1632
- while (container.firstChild) {
1633
- container.removeChild(container.firstChild);
1422
+ const materializeChild = (value) => {
1423
+ const current = resolveNodeValue(value);
1424
+ if (isArray(current)) {
1425
+ const nodes = [];
1426
+ for (const item of current) {
1427
+ const materialized = materializeChild(item);
1428
+ if (isArray(materialized)) {
1429
+ nodes.push(...materialized);
1430
+ } else {
1431
+ nodes.push(materialized);
1432
+ }
1433
+ }
1434
+ return nodes;
1634
1435
  }
1635
- if (props.fallback != null) {
1636
- const normalized = normalizeNode(props.fallback);
1637
- if (normalized) {
1638
- insertNode(container, normalized);
1436
+ return normalizeNode(current);
1437
+ };
1438
+ const insertMaterializedChild = (value) => {
1439
+ const normalized = materializeChild(value);
1440
+ const nodes = isArray(normalized) ? normalized : [normalized];
1441
+ for (const node of nodes) {
1442
+ if (node != null) {
1443
+ insertNode(container, node);
1639
1444
  }
1640
1445
  }
1641
1446
  };
1447
+ const renderFallbackContent = () => {
1448
+ clearContainer(container);
1449
+ if (props.fallback != null) {
1450
+ insertMaterializedChild(props.fallback);
1451
+ }
1452
+ };
1453
+ const showFallback = () => {
1454
+ if (isShowingFallback) return;
1455
+ isShowingFallback = true;
1456
+ renderFallbackContent();
1457
+ };
1642
1458
  const showChildren = () => {
1643
1459
  if (!isShowingFallback) return;
1644
1460
  const hasContent = resolvedChildren || props.children != null && !isPromise(props.children);
@@ -1646,9 +1462,7 @@ function Suspense(props) {
1646
1462
  return;
1647
1463
  }
1648
1464
  isShowingFallback = false;
1649
- while (container.firstChild) {
1650
- container.removeChild(container.firstChild);
1651
- }
1465
+ clearContainer(container);
1652
1466
  if (resolvedChildren) {
1653
1467
  renderChildren(resolvedChildren);
1654
1468
  } else if (props.children != null && !isPromise(props.children)) {
@@ -1656,33 +1470,17 @@ function Suspense(props) {
1656
1470
  }
1657
1471
  };
1658
1472
  const renderChildren = (children2) => {
1659
- while (container.firstChild) {
1660
- container.removeChild(container.firstChild);
1661
- }
1473
+ if (isShowingFallback) return;
1474
+ clearContainer(container);
1662
1475
  if (children2 == null) return;
1663
- const currentScope = getActiveScope();
1664
1476
  const childArray = isArray(children2) ? children2 : [children2];
1665
- childArray.forEach((child) => {
1666
- if (child != null) {
1667
- if (isComponent(child)) {
1668
- child.parentContext = currentScope;
1669
- }
1670
- const normalized = normalizeNode(child);
1671
- if (normalized) {
1672
- insertNode(container, normalized);
1673
- }
1477
+ for (const child2 of childArray) {
1478
+ if (child2 != null) {
1479
+ insertMaterializedChild(child2);
1674
1480
  }
1675
- });
1481
+ }
1676
1482
  if (isShowingFallback) {
1677
- while (container.firstChild) {
1678
- container.removeChild(container.firstChild);
1679
- }
1680
- if (props.fallback != null) {
1681
- const normalized = normalizeNode(props.fallback);
1682
- if (normalized) {
1683
- insertNode(container, normalized);
1684
- }
1685
- }
1483
+ renderFallbackContent();
1686
1484
  }
1687
1485
  };
1688
1486
  const suspenseContext = {
@@ -1695,9 +1493,9 @@ function Suspense(props) {
1695
1493
  if (pendingCount === 0) {
1696
1494
  showChildren();
1697
1495
  }
1698
- }).catch((error8) => {
1496
+ }).catch((error4) => {
1699
1497
  {
1700
- warn("[Suspense] Resource failed:", error8);
1498
+ warn("[Suspense] Resource failed:", error4);
1701
1499
  }
1702
1500
  if (!isMounted) return;
1703
1501
  pendingCount--;
@@ -1711,7 +1509,7 @@ function Suspense(props) {
1711
1509
  showFallback();
1712
1510
  },
1713
1511
  decrement: () => {
1714
- pendingCount--;
1512
+ pendingCount = Math.max(0, pendingCount - 1);
1715
1513
  if (pendingCount === 0) {
1716
1514
  showChildren();
1717
1515
  }
@@ -1732,9 +1530,10 @@ function Suspense(props) {
1732
1530
  }
1733
1531
  onDestroy(() => {
1734
1532
  isMounted = false;
1735
- while (container.firstChild) {
1736
- container.removeChild(container.firstChild);
1737
- }
1533
+ pendingCount = 0;
1534
+ resolvedChildren = null;
1535
+ clearContainer(container);
1536
+ container.remove();
1738
1537
  });
1739
1538
  return container;
1740
1539
  }
@@ -1745,19 +1544,25 @@ function isSuspense(node) {
1745
1544
  function createResource(fetcher, options) {
1746
1545
  const value = signal(options == null ? void 0 : options.initialValue);
1747
1546
  const loading = signal(true);
1748
- const error8 = signal(null);
1547
+ const error4 = signal(null);
1749
1548
  const state = signal("pending");
1750
1549
  let fetchId = 0;
1751
1550
  let currentPromise = null;
1551
+ let suspenseRegistered = false;
1552
+ const suspenseContext = inject(SuspenseContext, null);
1752
1553
  const fetch = () => __async(null, null, function* () {
1753
1554
  const currentFetchId = ++fetchId;
1754
1555
  loading.value = true;
1755
1556
  state.value = "pending";
1756
- error8.value = null;
1557
+ error4.value = null;
1558
+ suspenseRegistered = false;
1559
+ if (suspenseContext) {
1560
+ suspenseContext.increment();
1561
+ }
1757
1562
  try {
1758
1563
  const promise = fetcher();
1759
- currentPromise = promise.then(() => {
1760
- }).catch(() => {
1564
+ currentPromise = promise;
1565
+ promise.catch(() => {
1761
1566
  });
1762
1567
  const result = yield promise;
1763
1568
  if (currentFetchId === fetchId) {
@@ -1767,31 +1572,33 @@ function createResource(fetcher, options) {
1767
1572
  }
1768
1573
  } catch (error_) {
1769
1574
  if (currentFetchId === fetchId) {
1770
- error8.value = error_ instanceof Error ? error_ : new Error(String(error_));
1575
+ error4.value = error_ instanceof Error ? error_ : new Error(String(error_));
1771
1576
  state.value = "errored";
1772
1577
  loading.value = false;
1773
1578
  }
1579
+ } finally {
1580
+ if (suspenseContext) {
1581
+ suspenseContext.decrement();
1582
+ }
1774
1583
  }
1775
1584
  });
1776
1585
  fetch();
1777
1586
  const resource = (() => {
1778
- if (loading.value && currentPromise) {
1779
- const suspenseContext = inject(SuspenseContext, null);
1780
- if (suspenseContext) {
1781
- suspenseContext.register(currentPromise);
1782
- }
1587
+ if (!suspenseRegistered && loading.value && currentPromise && suspenseContext) {
1588
+ suspenseRegistered = true;
1589
+ suspenseContext.register(currentPromise);
1783
1590
  }
1784
1591
  return value.value;
1785
1592
  });
1786
1593
  resource.loading = loading;
1787
- resource.error = error8;
1594
+ resource.error = error4;
1788
1595
  resource.state = state;
1789
1596
  const actions = {
1790
1597
  mutate: (newValue) => {
1791
1598
  value.value = newValue;
1792
1599
  state.value = "ready";
1793
1600
  loading.value = false;
1794
- error8.value = null;
1601
+ error4.value = null;
1795
1602
  },
1796
1603
  refetch: () => __async(null, null, function* () {
1797
1604
  yield fetch();
@@ -1799,89 +1606,274 @@ function createResource(fetcher, options) {
1799
1606
  };
1800
1607
  return [resource, actions];
1801
1608
  }
1609
+ function resolveModule(mod) {
1610
+ return isFunction(mod) ? mod : mod.default;
1611
+ }
1612
+ function renderInto(el, fn, props) {
1613
+ const comp = new Component(fn, props);
1614
+ comp.mount(el);
1615
+ return comp;
1616
+ }
1617
+ function defineAsyncComponent(loader, options = {}) {
1618
+ const { delay = 200, timeout, ssr = "blocking", onError } = options;
1619
+ if (typeof window === "undefined") {
1620
+ if (ssr === "client-only") {
1621
+ const placeholder = () => "";
1622
+ placeholder.__asyncLoader = loader;
1623
+ placeholder.__asyncResolved = () => null;
1624
+ return placeholder;
1625
+ }
1626
+ let ssrResolved = null;
1627
+ let ssrPromise = null;
1628
+ const ssrLoad = () => {
1629
+ if (ssrPromise) return ssrPromise;
1630
+ ssrPromise = loader().then((mod) => {
1631
+ ssrResolved = resolveModule(mod);
1632
+ }).catch(() => {
1633
+ });
1634
+ return ssrPromise;
1635
+ };
1636
+ ssrLoad();
1637
+ const ssrWrapper = (props) => {
1638
+ if (ssrResolved) {
1639
+ return ssrResolved(props);
1640
+ }
1641
+ return "";
1642
+ };
1643
+ ssrWrapper.__asyncLoader = ssrLoad;
1644
+ ssrWrapper.__asyncResolved = () => ssrResolved;
1645
+ return ssrWrapper;
1646
+ }
1647
+ let cachedComponent = null;
1648
+ let cachedError = null;
1649
+ let cachedStatus = "pending";
1650
+ let loadPromise = null;
1651
+ function load() {
1652
+ if (loadPromise) return loadPromise;
1653
+ loadPromise = loader().then((mod) => {
1654
+ cachedComponent = resolveModule(mod);
1655
+ cachedStatus = "resolved";
1656
+ }).catch((error4) => {
1657
+ cachedError = error4 instanceof Error ? error4 : new Error(String(error4));
1658
+ cachedStatus = "errored";
1659
+ loadPromise = null;
1660
+ });
1661
+ return loadPromise;
1662
+ }
1663
+ load();
1664
+ function AsyncWrapper(props) {
1665
+ var _a2;
1666
+ if (cachedStatus === "resolved" && cachedComponent) {
1667
+ const el = document.createElement("div");
1668
+ el.style.display = "contents";
1669
+ const comp = renderInto(el, cachedComponent, props);
1670
+ onCleanup(() => comp.destroy());
1671
+ return el;
1672
+ }
1673
+ if (cachedStatus === "errored" && cachedError) {
1674
+ const el = document.createElement("div");
1675
+ el.style.display = "contents";
1676
+ if (options.error) {
1677
+ let alive2 = true;
1678
+ let currentComp2 = null;
1679
+ const swap2 = (fn, swapProps) => {
1680
+ if (!alive2) return;
1681
+ currentComp2 == null ? void 0 : currentComp2.destroy();
1682
+ currentComp2 = renderInto(el, fn, swapProps);
1683
+ };
1684
+ const retry = () => {
1685
+ loadPromise = null;
1686
+ cachedStatus = "pending";
1687
+ cachedError = null;
1688
+ if (options.loading) swap2(options.loading);
1689
+ load().then(() => {
1690
+ if (!alive2) return;
1691
+ if (cachedStatus === "resolved" && cachedComponent) {
1692
+ swap2(cachedComponent, props);
1693
+ } else if (cachedStatus === "errored" && cachedError) {
1694
+ if (options.error) swap2(options.error, { error: cachedError, retry });
1695
+ }
1696
+ });
1697
+ };
1698
+ swap2(options.error, { error: cachedError, retry });
1699
+ onDestroy(() => {
1700
+ alive2 = false;
1701
+ currentComp2 == null ? void 0 : currentComp2.destroy();
1702
+ currentComp2 = null;
1703
+ });
1704
+ }
1705
+ return el;
1706
+ }
1707
+ const container = document.createElement("div");
1708
+ container.style.display = "contents";
1709
+ let alive = true;
1710
+ let currentComp = null;
1711
+ let delayTimer = null;
1712
+ let timeoutTimer = null;
1713
+ const swap = (fn, swapProps) => {
1714
+ if (!alive) return;
1715
+ currentComp == null ? void 0 : currentComp.destroy();
1716
+ currentComp = renderInto(container, fn, swapProps);
1717
+ };
1718
+ const retryWith = (retryProps) => () => {
1719
+ loadPromise = null;
1720
+ cachedStatus = "pending";
1721
+ cachedError = null;
1722
+ if (options.loading) swap(options.loading);
1723
+ load().then(() => {
1724
+ if (cachedStatus === "resolved" && cachedComponent) {
1725
+ swap(cachedComponent, retryProps);
1726
+ } else if (cachedStatus === "errored" && cachedError) {
1727
+ if (options.error) {
1728
+ swap(options.error, {
1729
+ error: cachedError,
1730
+ retry: retryWith(retryProps)
1731
+ });
1732
+ }
1733
+ }
1734
+ });
1735
+ };
1736
+ onDestroy(() => {
1737
+ alive = false;
1738
+ currentComp == null ? void 0 : currentComp.destroy();
1739
+ currentComp = null;
1740
+ if (delayTimer != null) clearTimeout(delayTimer);
1741
+ if (timeoutTimer != null) clearTimeout(timeoutTimer);
1742
+ });
1743
+ const suspenseCtx = (_a2 = inject(SuspenseContext)) != null ? _a2 : null;
1744
+ const showResolved = (compFn) => swap(compFn, props);
1745
+ const showError = (err) => {
1746
+ if (options.error) swap(options.error, { error: err, retry: retryWith(props) });
1747
+ };
1748
+ const showLoading = () => {
1749
+ if (options.loading) swap(options.loading);
1750
+ };
1751
+ const instancePromise = load().then(() => {
1752
+ if (!alive) return;
1753
+ if (cachedStatus === "resolved" && cachedComponent) {
1754
+ showResolved(cachedComponent);
1755
+ } else if (cachedStatus === "errored" && cachedError) {
1756
+ showError(cachedError);
1757
+ if (onError) onError(cachedError, retryWith(props));
1758
+ }
1759
+ if (delayTimer != null) clearTimeout(delayTimer);
1760
+ if (timeoutTimer != null) clearTimeout(timeoutTimer);
1761
+ });
1762
+ if (suspenseCtx) {
1763
+ suspenseCtx.register(instancePromise);
1764
+ }
1765
+ if (delay > 0) {
1766
+ delayTimer = setTimeout(() => {
1767
+ if (alive && cachedStatus === "pending") {
1768
+ showLoading();
1769
+ }
1770
+ }, delay);
1771
+ } else if (options.loading) {
1772
+ showLoading();
1773
+ }
1774
+ if (timeout != null) {
1775
+ timeoutTimer = setTimeout(() => {
1776
+ if (alive && cachedStatus === "pending") {
1777
+ const err = new Error(`[defineAsyncComponent] Timeout after ${timeout}ms`);
1778
+ cachedError = err;
1779
+ cachedStatus = "errored";
1780
+ showError(err);
1781
+ if (onError) onError(err, retryWith(props));
1782
+ }
1783
+ }, timeout);
1784
+ }
1785
+ return container;
1786
+ }
1787
+ AsyncWrapper.__asyncLoader = load;
1788
+ AsyncWrapper.__asyncResolved = () => cachedComponent;
1789
+ return AsyncWrapper;
1790
+ }
1802
1791
  function For(props) {
1803
1792
  const fragment = document.createDocumentFragment();
1804
1793
  const marker = document.createComment("");
1805
1794
  fragment.appendChild(marker);
1806
1795
  let entries = [];
1807
- let fallbackNode = null;
1808
- const keyFn = props.keyFn;
1796
+ let fallbackNodes = [];
1797
+ const keyFn = props.key;
1809
1798
  const renderFn = props.children;
1810
1799
  const getList = () => {
1811
1800
  var _a2, _b;
1812
1801
  const input = props.each;
1813
1802
  if (isSignal(input)) return (_a2 = input.value) != null ? _a2 : [];
1814
- if (typeof input === "function") return (_b = input()) != null ? _b : [];
1803
+ if (isFunction(input)) return (_b = input()) != null ? _b : [];
1815
1804
  return input != null ? input : [];
1816
1805
  };
1817
1806
  const getKey = (item) => keyFn ? keyFn(item) : item;
1818
- const renderItem = (item, index, parent, before) => {
1819
- var _a2;
1820
- const prevScope = getActiveScope();
1821
- const scope = createScope(prevScope);
1822
- setActiveScope(scope);
1823
- let node;
1824
- try {
1825
- const result = renderFn(item, index);
1826
- if (isComponent(result)) {
1827
- result.mount(parent, before);
1828
- node = (_a2 = result.firstChild) != null ? _a2 : document.createComment("empty");
1807
+ const normalizeNodes = (value) => {
1808
+ if (Array.isArray(value)) {
1809
+ const nodes = [];
1810
+ for (const item of value) {
1811
+ nodes.push(...normalizeNodes(item));
1812
+ }
1813
+ return nodes;
1814
+ }
1815
+ return [normalizeNode(value)];
1816
+ };
1817
+ const mountValue = (value, parent, before) => {
1818
+ const nodes = normalizeNodes(value);
1819
+ for (const node of nodes) {
1820
+ if (before) {
1821
+ parent.insertBefore(node, before);
1829
1822
  } else {
1830
- node = result;
1831
- if (!node.parentNode) {
1832
- if (before) {
1833
- parent.insertBefore(node, before);
1834
- } else {
1835
- parent.appendChild(node);
1836
- }
1837
- }
1823
+ parent.appendChild(node);
1824
+ }
1825
+ }
1826
+ return nodes;
1827
+ };
1828
+ const mountFallback = (parent, before) => {
1829
+ if (!props.fallback) return;
1830
+ const nodes = mountValue(props.fallback(), parent, before);
1831
+ fallbackNodes = nodes;
1832
+ };
1833
+ const clearFallback = () => {
1834
+ for (const node of fallbackNodes) {
1835
+ if (node.parentNode) {
1836
+ node.parentNode.removeChild(node);
1838
1837
  }
1839
- } finally {
1840
- setActiveScope(prevScope);
1841
1838
  }
1842
- return { key: getKey(item), node, scope };
1839
+ fallbackNodes = [];
1840
+ };
1841
+ const renderItem = (item, index, parent, before) => {
1842
+ const parentScope = getActiveScope();
1843
+ const scope = createScope(parentScope);
1844
+ let mountedNodes = [];
1845
+ runWithScope(scope, () => {
1846
+ mountedNodes = mountValue(renderFn(item, index), parent, before);
1847
+ });
1848
+ return { key: getKey(item), item, nodes: mountedNodes, scope };
1843
1849
  };
1844
1850
  const disposeItem = (entry) => {
1845
1851
  disposeScope(entry.scope);
1846
- if (entry.node.parentNode) {
1847
- entry.node.parentNode.removeChild(entry.node);
1852
+ for (const node of entry.nodes) {
1853
+ if (node.parentNode) {
1854
+ node.parentNode.removeChild(node);
1855
+ }
1848
1856
  }
1849
1857
  };
1850
- memoEffect(
1851
- ({ prev }) => {
1852
- var _a2;
1853
- const newItems = getList();
1854
- if (prev === newItems) return { prev: newItems };
1855
- const parent = marker.parentNode;
1856
- if (!parent) {
1857
- if (newItems.length === 0) {
1858
- if (props.fallback) {
1859
- const fb = props.fallback();
1860
- if (isComponent(fb)) {
1861
- fb.mount(fragment, marker);
1862
- fallbackNode = (_a2 = fb.firstChild) != null ? _a2 : document.createComment("empty");
1863
- } else {
1864
- fallbackNode = fb;
1865
- fragment.insertBefore(fallbackNode, marker);
1866
- }
1867
- }
1868
- return { prev: newItems };
1869
- }
1858
+ const effectRunner = effect(() => {
1859
+ const newItems = getList();
1860
+ const parent = marker.parentNode;
1861
+ if (!parent) {
1862
+ if (newItems.length === 0) {
1863
+ mountFallback(fragment, marker);
1864
+ } else {
1870
1865
  entries = new Array(newItems.length);
1871
- for (const [i, newItem] of newItems.entries()) {
1872
- entries[i] = renderItem(newItem, i, fragment, marker);
1866
+ let idx = 0;
1867
+ for (const newItem of newItems) {
1868
+ entries[idx] = renderItem(newItem, idx, fragment, marker);
1869
+ idx++;
1873
1870
  }
1874
- return { prev: newItems };
1875
1871
  }
1876
- untrack(() => reconcile(parent, newItems));
1877
- return { prev: newItems };
1878
- },
1879
- {
1880
- prev: []
1872
+ return;
1881
1873
  }
1882
- );
1874
+ reconcile(parent, newItems);
1875
+ });
1883
1876
  function reconcile(parent, newItems) {
1884
- var _a2;
1885
1877
  const oldLen = entries.length;
1886
1878
  const newLen = newItems.length;
1887
1879
  if (newLen === 0) {
@@ -1889,22 +1881,14 @@ function For(props) {
1889
1881
  disposeItem(entries[i]);
1890
1882
  }
1891
1883
  entries = [];
1892
- if (props.fallback && !fallbackNode) {
1893
- const fb = props.fallback();
1894
- if (isComponent(fb)) {
1895
- fb.mount(parent, marker);
1896
- fallbackNode = (_a2 = fb.firstChild) != null ? _a2 : document.createComment("empty");
1897
- } else {
1898
- fallbackNode = fb;
1899
- parent.insertBefore(fallbackNode, marker);
1900
- }
1884
+ if (props.fallback && fallbackNodes.length === 0) {
1885
+ mountFallback(parent, marker);
1901
1886
  }
1902
1887
  return;
1903
1888
  }
1904
- if (oldLen === 0 || fallbackNode) {
1905
- if (fallbackNode) {
1906
- if (fallbackNode.parentNode) fallbackNode.parentNode.removeChild(fallbackNode);
1907
- fallbackNode = null;
1889
+ if (oldLen === 0 || fallbackNodes.length > 0) {
1890
+ if (fallbackNodes.length > 0) {
1891
+ clearFallback();
1908
1892
  }
1909
1893
  entries = new Array(newLen);
1910
1894
  const batchFragment2 = document.createDocumentFragment();
@@ -1918,46 +1902,76 @@ function For(props) {
1918
1902
  for (let i = 0; i < oldLen; i++) {
1919
1903
  const entry = entries[i];
1920
1904
  const list = oldKeyMap.get(entry.key);
1921
- if (list) {
1922
- list.push(entry);
1923
- } else {
1924
- oldKeyMap.set(entry.key, [entry]);
1925
- }
1905
+ const pair = [entry, i];
1906
+ if (list) list.push(pair);
1907
+ else oldKeyMap.set(entry.key, [pair]);
1926
1908
  }
1927
1909
  const newEntries = new Array(newLen);
1928
1910
  const toRemove = [];
1929
- const batchFragment = document.createDocumentFragment();
1911
+ let batchFragment = null;
1912
+ const newIndexToOldIndex = new Int32Array(newLen);
1913
+ let moved = false;
1914
+ let maxOldSeen = 0;
1915
+ const newKeys = new Array(newLen);
1916
+ for (let i = 0; i < newLen; i++) {
1917
+ newKeys[i] = getKey(newItems[i]);
1918
+ }
1930
1919
  for (let i = 0; i < newLen; i++) {
1931
1920
  const item = newItems[i];
1932
- const key = getKey(item);
1921
+ const key = newKeys[i];
1933
1922
  const oldList = oldKeyMap.get(key);
1934
1923
  if (oldList && oldList.length > 0) {
1935
- newEntries[i] = oldList.shift();
1924
+ const [reused, oldIndex] = oldList.shift();
1925
+ if (Object.is(reused.item, item)) {
1926
+ reused.item = item;
1927
+ newEntries[i] = reused;
1928
+ newIndexToOldIndex[i] = oldIndex + 1;
1929
+ if (oldIndex < maxOldSeen) moved = true;
1930
+ else maxOldSeen = oldIndex;
1931
+ } else {
1932
+ if (!batchFragment) batchFragment = document.createDocumentFragment();
1933
+ disposeItem(reused);
1934
+ newEntries[i] = renderItem(item, i, batchFragment, null);
1935
+ }
1936
1936
  } else {
1937
+ if (!batchFragment) batchFragment = document.createDocumentFragment();
1937
1938
  newEntries[i] = renderItem(item, i, batchFragment, null);
1938
1939
  }
1939
1940
  }
1940
1941
  for (const list of oldKeyMap.values()) {
1941
- for (const entry of list) {
1942
- toRemove.push(entry);
1942
+ for (const [entry] of list) toRemove.push(entry);
1943
+ }
1944
+ for (const entry of toRemove) disposeItem(entry);
1945
+ const lis = moved ? getSequence(newIndexToOldIndex) : [];
1946
+ let lisCursor = lis.length - 1;
1947
+ let nextNode = marker;
1948
+ for (let i = newLen - 1; i >= 0; i--) {
1949
+ const entry = newEntries[i];
1950
+ const nodes = entry.nodes;
1951
+ const isFresh = newIndexToOldIndex[i] === 0;
1952
+ const inLis = !isFresh && moved && lisCursor >= 0 && i === lis[lisCursor];
1953
+ if (inLis) {
1954
+ lisCursor--;
1955
+ for (let j = nodes.length - 1; j >= 0; j--) nextNode = nodes[j];
1956
+ continue;
1957
+ }
1958
+ for (let j = nodes.length - 1; j >= 0; j--) {
1959
+ const node = nodes[j];
1960
+ if (node.nextSibling !== nextNode) {
1961
+ parent.insertBefore(node, nextNode);
1962
+ }
1963
+ nextNode = node;
1943
1964
  }
1944
- }
1945
- for (const entry of toRemove) {
1946
- disposeItem(entry);
1947
- }
1948
- for (let i = 0; i < newLen; i++) {
1949
- const node = newEntries[i].node;
1950
- parent.insertBefore(node, marker);
1951
1965
  }
1952
1966
  entries = newEntries;
1953
1967
  }
1954
1968
  onCleanup(() => {
1969
+ effectRunner.stop();
1955
1970
  for (const entry of entries) {
1956
1971
  disposeItem(entry);
1957
1972
  }
1958
- if (fallbackNode && fallbackNode.parentNode) {
1959
- fallbackNode.parentNode.removeChild(fallbackNode);
1960
- }
1973
+ entries = [];
1974
+ clearFallback();
1961
1975
  if (marker.parentNode) {
1962
1976
  marker.parentNode.removeChild(marker);
1963
1977
  }
@@ -1966,6 +1980,6 @@ function For(props) {
1966
1980
  }
1967
1981
  For[FOR_COMPONENT] = true;
1968
1982
 
1969
- export { Component, For, Fragment, Portal, Suspense, addEvent, addEventListener, bindElement, createApp, createComponent, createResource, createScope, delegateEvents, disposeScope, endHydration, getActiveScope, getFirstDOMNode, getHydrationKey, getRenderedElement, hydrate, inject, insert, insertNode, isComponent, isFragment, isHydrating, isPortal, isSameNode, isSuspense, mapNodes, mapSSRNodes, normalizeClass, normalizeNode, omitProps, onCleanup, onDestroy, onMount, onUpdate, patchAttr, patchClass, patchStyle, provide, removeNode, replaceNode, resetHydrationKey, runWithScope, setActiveScope, setStyle, shallowCompare, startHydration, template };
1983
+ export { Component, For, Fragment, Portal, Suspense, addEvent, addEventListener, beginHydration, bindElement, child, clearDelegatedEvents, consumeTeleportAnchor, consumeTeleportBlock, createApp, createComponent, createResource, defineAsyncComponent, delegateEvents, endHydration, getHydrationKey, getRenderedElement, hydrate, inject, insert, isComponent, isFragment, isHydrating, isPortal, isSuspense, next, normalizeClass, nthChild, omitProps, onDestroy, onMount, onUpdate, patchAttr, patchAttrHydrate, patchClass, patchClassHydrate, patchStyle, patchStyleHydrate, provide, resetHydrationKey, setStyle, template };
1970
1984
  //# sourceMappingURL=template.dev.esm.js.map
1971
1985
  //# sourceMappingURL=template.dev.esm.js.map