zero-query 0.9.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -31
- package/cli/args.js +1 -1
- package/cli/commands/build.js +2 -2
- package/cli/commands/bundle.js +15 -15
- package/cli/commands/create.js +41 -7
- package/cli/commands/dev/devtools/index.js +1 -1
- package/cli/commands/dev/devtools/js/core.js +14 -14
- package/cli/commands/dev/devtools/js/elements.js +4 -4
- package/cli/commands/dev/devtools/js/stats.js +1 -1
- package/cli/commands/dev/devtools/styles.css +2 -2
- package/cli/commands/dev/index.js +2 -2
- package/cli/commands/dev/logger.js +1 -1
- package/cli/commands/dev/overlay.js +21 -14
- package/cli/commands/dev/server.js +5 -5
- package/cli/commands/dev/validator.js +7 -7
- package/cli/commands/dev/watcher.js +6 -6
- package/cli/help.js +4 -2
- package/cli/index.js +2 -2
- package/cli/scaffold/default/app/app.js +17 -18
- package/cli/scaffold/default/app/components/about.js +9 -9
- package/cli/scaffold/default/app/components/api-demo.js +6 -6
- package/cli/scaffold/default/app/components/contact-card.js +4 -4
- package/cli/scaffold/default/app/components/contacts/contacts.css +2 -2
- package/cli/scaffold/default/app/components/contacts/contacts.html +3 -3
- package/cli/scaffold/default/app/components/contacts/contacts.js +11 -11
- package/cli/scaffold/default/app/components/counter.js +8 -8
- package/cli/scaffold/default/app/components/home.js +13 -13
- package/cli/scaffold/default/app/components/not-found.js +1 -1
- package/cli/scaffold/default/app/components/playground/playground.css +1 -1
- package/cli/scaffold/default/app/components/playground/playground.html +11 -11
- package/cli/scaffold/default/app/components/playground/playground.js +11 -11
- package/cli/scaffold/default/app/components/todos.js +8 -8
- package/cli/scaffold/default/app/components/toolkit/toolkit.css +1 -1
- package/cli/scaffold/default/app/components/toolkit/toolkit.html +4 -4
- package/cli/scaffold/default/app/components/toolkit/toolkit.js +7 -7
- package/cli/scaffold/default/app/routes.js +1 -1
- package/cli/scaffold/default/app/store.js +1 -1
- package/cli/scaffold/default/global.css +2 -2
- package/cli/scaffold/default/index.html +2 -2
- package/cli/scaffold/minimal/app/app.js +6 -7
- package/cli/scaffold/minimal/app/components/about.js +5 -5
- package/cli/scaffold/minimal/app/components/counter.js +6 -6
- package/cli/scaffold/minimal/app/components/home.js +8 -8
- package/cli/scaffold/minimal/app/components/not-found.js +1 -1
- package/cli/scaffold/minimal/app/routes.js +1 -1
- package/cli/scaffold/minimal/app/store.js +1 -1
- package/cli/scaffold/minimal/global.css +2 -2
- package/cli/scaffold/minimal/index.html +1 -1
- package/cli/scaffold/ssr/app/app.js +29 -0
- package/cli/scaffold/ssr/app/components/about.js +28 -0
- package/cli/scaffold/ssr/app/components/home.js +37 -0
- package/cli/scaffold/ssr/app/components/not-found.js +15 -0
- package/cli/scaffold/ssr/app/routes.js +6 -0
- package/cli/scaffold/ssr/global.css +113 -0
- package/cli/scaffold/ssr/index.html +31 -0
- package/cli/scaffold/ssr/package.json +8 -0
- package/cli/scaffold/ssr/server/index.js +118 -0
- package/cli/utils.js +6 -6
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +565 -228
- package/dist/zquery.min.js +2 -2
- package/index.d.ts +25 -12
- package/index.js +11 -7
- package/package.json +9 -3
- package/src/component.js +64 -63
- package/src/core.js +15 -15
- package/src/diff.js +38 -38
- package/src/errors.js +72 -18
- package/src/expression.js +15 -17
- package/src/http.js +4 -4
- package/src/package.json +1 -0
- package/src/reactive.js +75 -9
- package/src/router.js +104 -24
- package/src/ssr.js +133 -39
- package/src/store.js +103 -21
- package/src/utils.js +64 -12
- package/tests/audit.test.js +143 -15
- package/tests/cli.test.js +20 -20
- package/tests/component.test.js +121 -121
- package/tests/core.test.js +56 -56
- package/tests/diff.test.js +42 -42
- package/tests/errors.test.js +425 -147
- package/tests/expression.test.js +58 -53
- package/tests/http.test.js +20 -20
- package/tests/reactive.test.js +185 -24
- package/tests/router.test.js +501 -74
- package/tests/ssr.test.js +444 -10
- package/tests/store.test.js +264 -23
- package/tests/utils.test.js +163 -26
- package/types/collection.d.ts +2 -2
- package/types/component.d.ts +5 -5
- package/types/errors.d.ts +36 -4
- package/types/http.d.ts +3 -3
- package/types/misc.d.ts +9 -9
- package/types/reactive.d.ts +25 -3
- package/types/router.d.ts +10 -6
- package/types/ssr.d.ts +22 -2
- package/types/store.d.ts +40 -5
- package/types/utils.d.ts +1 -1
package/tests/diff.test.js
CHANGED
|
@@ -22,7 +22,7 @@ function morphAndGet(oldHTML, newHTML) {
|
|
|
22
22
|
// Basic morphing
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
24
|
|
|
25
|
-
describe('morph
|
|
25
|
+
describe('morph - basic', () => {
|
|
26
26
|
it('updates text content', () => {
|
|
27
27
|
const root = morphAndGet('<p>old</p>', '<p>new</p>');
|
|
28
28
|
expect(root.innerHTML).toBe('<p>new</p>');
|
|
@@ -87,7 +87,7 @@ describe('morph — basic', () => {
|
|
|
87
87
|
// Attribute morphing
|
|
88
88
|
// ---------------------------------------------------------------------------
|
|
89
89
|
|
|
90
|
-
describe('morph
|
|
90
|
+
describe('morph - attributes', () => {
|
|
91
91
|
it('adds new attributes', () => {
|
|
92
92
|
const root = morphAndGet('<div></div>', '<div class="active"></div>');
|
|
93
93
|
expect(root.children[0].className).toBe('active');
|
|
@@ -133,7 +133,7 @@ describe('morph — attributes', () => {
|
|
|
133
133
|
// Keyed reconciliation
|
|
134
134
|
// ---------------------------------------------------------------------------
|
|
135
135
|
|
|
136
|
-
describe('morph
|
|
136
|
+
describe('morph - keyed', () => {
|
|
137
137
|
it('matches elements by z-key', () => {
|
|
138
138
|
const root = el('<div z-key="a">A</div><div z-key="b">B</div>');
|
|
139
139
|
morph(root, '<div z-key="b">B-updated</div><div z-key="a">A-updated</div>');
|
|
@@ -206,7 +206,7 @@ describe('morph — keyed', () => {
|
|
|
206
206
|
// Input / form element handling
|
|
207
207
|
// ---------------------------------------------------------------------------
|
|
208
208
|
|
|
209
|
-
describe('morph
|
|
209
|
+
describe('morph - form elements', () => {
|
|
210
210
|
it('syncs input value', () => {
|
|
211
211
|
const root = el('<input value="old">');
|
|
212
212
|
morph(root, '<input value="new">');
|
|
@@ -253,7 +253,7 @@ describe('morph — form elements', () => {
|
|
|
253
253
|
// Text and comment nodes
|
|
254
254
|
// ---------------------------------------------------------------------------
|
|
255
255
|
|
|
256
|
-
describe('morph
|
|
256
|
+
describe('morph - text nodes', () => {
|
|
257
257
|
it('updates text nodes', () => {
|
|
258
258
|
const root = document.createElement('div');
|
|
259
259
|
root.textContent = 'old';
|
|
@@ -283,7 +283,7 @@ describe('morph — text nodes', () => {
|
|
|
283
283
|
// Nested children
|
|
284
284
|
// ---------------------------------------------------------------------------
|
|
285
285
|
|
|
286
|
-
describe('morph
|
|
286
|
+
describe('morph - nested', () => {
|
|
287
287
|
it('recursively morphs nested elements', () => {
|
|
288
288
|
const root = morphAndGet(
|
|
289
289
|
'<ul><li>a</li><li>b</li></ul>',
|
|
@@ -329,7 +329,7 @@ describe('morph — nested', () => {
|
|
|
329
329
|
// Preserves unchanged nodes (identity)
|
|
330
330
|
// ---------------------------------------------------------------------------
|
|
331
331
|
|
|
332
|
-
describe('morph
|
|
332
|
+
describe('morph - preservation', () => {
|
|
333
333
|
it('does not replace elements that have not changed', () => {
|
|
334
334
|
const root = el('<p>same</p><p>will-change</p>');
|
|
335
335
|
const firstP = root.children[0];
|
|
@@ -351,7 +351,7 @@ describe('morph — preservation', () => {
|
|
|
351
351
|
// z-skip directive
|
|
352
352
|
// ---------------------------------------------------------------------------
|
|
353
353
|
|
|
354
|
-
describe('morph
|
|
354
|
+
describe('morph - z-skip', () => {
|
|
355
355
|
it('skips subtrees with z-skip attribute', () => {
|
|
356
356
|
const root = el('<div z-skip><p>original</p></div>');
|
|
357
357
|
const p = root.querySelector('p');
|
|
@@ -374,7 +374,7 @@ describe('morph — z-skip', () => {
|
|
|
374
374
|
// isEqualNode fast bail-out
|
|
375
375
|
// ---------------------------------------------------------------------------
|
|
376
376
|
|
|
377
|
-
describe('morph
|
|
377
|
+
describe('morph - fast bail-out', () => {
|
|
378
378
|
it('uses isEqualNode to skip identical elements', () => {
|
|
379
379
|
const root = el('<div><p class="a" id="b">text</p></div>');
|
|
380
380
|
const p = root.querySelector('p');
|
|
@@ -389,7 +389,7 @@ describe('morph — fast bail-out', () => {
|
|
|
389
389
|
// Edge cases & stress
|
|
390
390
|
// ---------------------------------------------------------------------------
|
|
391
391
|
|
|
392
|
-
describe('morph
|
|
392
|
+
describe('morph - edge cases', () => {
|
|
393
393
|
it('handles whitespace-only text nodes', () => {
|
|
394
394
|
const root = morphAndGet('<div> </div>', '<div>text</div>');
|
|
395
395
|
expect(root.children[0].textContent).toBe('text');
|
|
@@ -432,7 +432,7 @@ describe('morph — edge cases', () => {
|
|
|
432
432
|
// Auto-keyed reconciliation (id / data-id / data-key)
|
|
433
433
|
// ---------------------------------------------------------------------------
|
|
434
434
|
|
|
435
|
-
describe('morph
|
|
435
|
+
describe('morph - auto-keyed via id attribute', () => {
|
|
436
436
|
it('reorders elements by id without z-key', () => {
|
|
437
437
|
const root = el(
|
|
438
438
|
'<div id="a">A</div><div id="b">B</div><div id="c">C</div>'
|
|
@@ -440,7 +440,7 @@ describe('morph — auto-keyed via id attribute', () => {
|
|
|
440
440
|
const refA = root.children[0];
|
|
441
441
|
const refB = root.children[1];
|
|
442
442
|
morph(root, '<div id="c">C</div><div id="a">A</div><div id="b">B</div>');
|
|
443
|
-
// Node identity preserved
|
|
443
|
+
// Node identity preserved - same DOM nodes, different order
|
|
444
444
|
expect(root.children[1]).toBe(refA);
|
|
445
445
|
expect(root.children[2]).toBe(refB);
|
|
446
446
|
expect([...root.children].map(c => c.id)).toEqual(['c', 'a', 'b']);
|
|
@@ -486,7 +486,7 @@ describe('morph — auto-keyed via id attribute', () => {
|
|
|
486
486
|
|
|
487
487
|
|
|
488
488
|
// ---------------------------------------------------------------------------
|
|
489
|
-
// morphElement
|
|
489
|
+
// morphElement - single-element morph
|
|
490
490
|
// ---------------------------------------------------------------------------
|
|
491
491
|
|
|
492
492
|
describe('morphElement', () => {
|
|
@@ -529,7 +529,7 @@ describe('morphElement', () => {
|
|
|
529
529
|
// Deep keyed reconciliation edge cases
|
|
530
530
|
// ---------------------------------------------------------------------------
|
|
531
531
|
|
|
532
|
-
describe('morph
|
|
532
|
+
describe('morph - keyed edge cases', () => {
|
|
533
533
|
it('handles single keyed element swap', () => {
|
|
534
534
|
const root = el('<div z-key="a">A</div>');
|
|
535
535
|
const nodeA = root.children[0];
|
|
@@ -620,7 +620,7 @@ describe('morph — keyed edge cases', () => {
|
|
|
620
620
|
// Boolean attribute morphing
|
|
621
621
|
// ---------------------------------------------------------------------------
|
|
622
622
|
|
|
623
|
-
describe('morph
|
|
623
|
+
describe('morph - boolean attributes', () => {
|
|
624
624
|
it('adds hidden attribute', () => {
|
|
625
625
|
const root = morphAndGet('<div></div>', '<div hidden></div>');
|
|
626
626
|
expect(root.children[0].hasAttribute('hidden')).toBe(true);
|
|
@@ -654,7 +654,7 @@ describe('morph — boolean attributes', () => {
|
|
|
654
654
|
// Complex form element morphing
|
|
655
655
|
// ---------------------------------------------------------------------------
|
|
656
656
|
|
|
657
|
-
describe('morph
|
|
657
|
+
describe('morph - complex form elements', () => {
|
|
658
658
|
it('syncs input type change', () => {
|
|
659
659
|
const root = morphAndGet('<input type="text" value="hello">', '<input type="password" value="secret">');
|
|
660
660
|
const input = root.querySelector('input');
|
|
@@ -706,7 +706,7 @@ describe('morph — complex form elements', () => {
|
|
|
706
706
|
// Text / comment node edge cases
|
|
707
707
|
// ---------------------------------------------------------------------------
|
|
708
708
|
|
|
709
|
-
describe('morph
|
|
709
|
+
describe('morph - text and comment edge cases', () => {
|
|
710
710
|
it('morphs comment to text node', () => {
|
|
711
711
|
const root = document.createElement('div');
|
|
712
712
|
root.appendChild(document.createComment('comment'));
|
|
@@ -750,7 +750,7 @@ describe('morph — text and comment edge cases', () => {
|
|
|
750
750
|
// Nested structure edge cases
|
|
751
751
|
// ---------------------------------------------------------------------------
|
|
752
752
|
|
|
753
|
-
describe('morph
|
|
753
|
+
describe('morph - nested structure edge cases', () => {
|
|
754
754
|
it('handles 5-level deep nesting change', () => {
|
|
755
755
|
const root = morphAndGet(
|
|
756
756
|
'<div><div><div><div><div>deep old</div></div></div></div></div>',
|
|
@@ -800,7 +800,7 @@ describe('morph — nested structure edge cases', () => {
|
|
|
800
800
|
// z-skip with keyed children
|
|
801
801
|
// ---------------------------------------------------------------------------
|
|
802
802
|
|
|
803
|
-
describe('morph
|
|
803
|
+
describe('morph - z-skip + keyed interactions', () => {
|
|
804
804
|
it('z-skip on parent skips entire keyed children', () => {
|
|
805
805
|
const root = el('<div z-skip><p z-key="a">A</p><p z-key="b">B</p></div>');
|
|
806
806
|
const pA = root.querySelector('p[z-key="a"]');
|
|
@@ -827,7 +827,7 @@ describe('morph — z-skip + keyed interactions', () => {
|
|
|
827
827
|
// morphElement advanced cases
|
|
828
828
|
// ---------------------------------------------------------------------------
|
|
829
829
|
|
|
830
|
-
describe('morphElement
|
|
830
|
+
describe('morphElement - advanced', () => {
|
|
831
831
|
it('morphs only attributes without child changes', () => {
|
|
832
832
|
const root = el('<p class="old" data-x="1">text</p>');
|
|
833
833
|
const target = root.children[0];
|
|
@@ -862,7 +862,7 @@ describe('morphElement — advanced', () => {
|
|
|
862
862
|
// Stress tests
|
|
863
863
|
// ---------------------------------------------------------------------------
|
|
864
864
|
|
|
865
|
-
describe('morph
|
|
865
|
+
describe('morph - stress tests', () => {
|
|
866
866
|
it('handles 500 unkeyed children update', () => {
|
|
867
867
|
const oldItems = Array.from({ length: 500 }, (_, i) => `<span>${i}</span>`).join('');
|
|
868
868
|
const newItems = Array.from({ length: 500 }, (_, i) => `<span>${i + 1}</span>`).join('');
|
|
@@ -914,7 +914,7 @@ describe('morph — stress tests', () => {
|
|
|
914
914
|
// BUG FIX: keyed morph cursor after node replacement
|
|
915
915
|
// ---------------------------------------------------------------------------
|
|
916
916
|
|
|
917
|
-
describe('morph
|
|
917
|
+
describe('morph - keyed cursor after replaceChild', () => {
|
|
918
918
|
it('correctly positions nodes when tag changes during keyed morph', () => {
|
|
919
919
|
const root = el(
|
|
920
920
|
'<div z-key="a">A</div>' +
|
|
@@ -942,7 +942,7 @@ describe('morph — keyed cursor after replaceChild', () => {
|
|
|
942
942
|
// BUG FIX: attribute removal on live NamedNodeMap
|
|
943
943
|
// ---------------------------------------------------------------------------
|
|
944
944
|
|
|
945
|
-
describe('morph
|
|
945
|
+
describe('morph - attribute removal stability', () => {
|
|
946
946
|
it('removes multiple stale attributes without index errors', () => {
|
|
947
947
|
const root = el('<div class="a" data-x="1" data-y="2" data-z="3" id="t1">hi</div>');
|
|
948
948
|
morph(root, '<div id="t1">hi</div>');
|
|
@@ -967,10 +967,10 @@ describe('morph — attribute removal stability', () => {
|
|
|
967
967
|
|
|
968
968
|
|
|
969
969
|
// ---------------------------------------------------------------------------
|
|
970
|
-
// Round 3
|
|
970
|
+
// Round 3 - Extended coverage
|
|
971
971
|
// ---------------------------------------------------------------------------
|
|
972
972
|
|
|
973
|
-
describe('morph
|
|
973
|
+
describe('morph - __zqMorphHook performance hook', () => {
|
|
974
974
|
it('calls the hook with root element and elapsed time for morph()', () => {
|
|
975
975
|
const calls = [];
|
|
976
976
|
window.__zqMorphHook = (el, ms) => calls.push({ el, ms });
|
|
@@ -1018,7 +1018,7 @@ describe('morph — __zqMorphHook performance hook', () => {
|
|
|
1018
1018
|
});
|
|
1019
1019
|
});
|
|
1020
1020
|
|
|
1021
|
-
describe('morph
|
|
1021
|
+
describe('morph - element to text node type change', () => {
|
|
1022
1022
|
it('replaces element with text node at same position', () => {
|
|
1023
1023
|
const root = el('<p>hello</p><div>world</div>');
|
|
1024
1024
|
morph(root, 'just text');
|
|
@@ -1047,7 +1047,7 @@ describe('morph — element to text node type change', () => {
|
|
|
1047
1047
|
});
|
|
1048
1048
|
});
|
|
1049
1049
|
|
|
1050
|
-
describe('morph
|
|
1050
|
+
describe('morph - attributes fast-path bypass', () => {
|
|
1051
1051
|
it('skips attribute update when count and values are identical but children differ', () => {
|
|
1052
1052
|
const root = el('<div class="a" data-x="1"><p>old child</p></div>');
|
|
1053
1053
|
const child = root.firstElementChild;
|
|
@@ -1071,7 +1071,7 @@ describe('morph — attributes fast-path bypass', () => {
|
|
|
1071
1071
|
});
|
|
1072
1072
|
});
|
|
1073
1073
|
|
|
1074
|
-
describe('morph
|
|
1074
|
+
describe('morph - textarea edge cases', () => {
|
|
1075
1075
|
it('syncs textarea with null-like textContent', () => {
|
|
1076
1076
|
const root = el('<textarea>old value</textarea>');
|
|
1077
1077
|
morph(root, '<textarea></textarea>');
|
|
@@ -1085,7 +1085,7 @@ describe('morph — textarea edge cases', () => {
|
|
|
1085
1085
|
});
|
|
1086
1086
|
});
|
|
1087
1087
|
|
|
1088
|
-
describe('morph
|
|
1088
|
+
describe('morph - select value sync', () => {
|
|
1089
1089
|
it('syncs select value after morphing options', () => {
|
|
1090
1090
|
const root = el('<select><option value="a">A</option><option value="b">B</option></select>');
|
|
1091
1091
|
root.querySelector('select').value = 'a';
|
|
@@ -1101,7 +1101,7 @@ describe('morph — select value sync', () => {
|
|
|
1101
1101
|
});
|
|
1102
1102
|
});
|
|
1103
1103
|
|
|
1104
|
-
describe('morph
|
|
1104
|
+
describe('morph - input sync edge cases', () => {
|
|
1105
1105
|
it('syncs input value when new element has no value attribute', () => {
|
|
1106
1106
|
const root = el('<input type="text" value="old">');
|
|
1107
1107
|
morph(root, '<input type="text">');
|
|
@@ -1133,7 +1133,7 @@ describe('morph — input sync edge cases', () => {
|
|
|
1133
1133
|
});
|
|
1134
1134
|
});
|
|
1135
1135
|
|
|
1136
|
-
describe('morph
|
|
1136
|
+
describe('morph - keyed with multiple unkeyed leftover', () => {
|
|
1137
1137
|
it('consumes some unkeyed and removes remaining', () => {
|
|
1138
1138
|
const root = el(
|
|
1139
1139
|
'<div z-key="a">A</div><p>unkeyed1</p><p>unkeyed2</p><p>unkeyed3</p><div z-key="b">B</div>'
|
|
@@ -1160,7 +1160,7 @@ describe('morph — keyed with multiple unkeyed leftover', () => {
|
|
|
1160
1160
|
});
|
|
1161
1161
|
});
|
|
1162
1162
|
|
|
1163
|
-
describe('morph
|
|
1163
|
+
describe('morph - LIS edge cases', () => {
|
|
1164
1164
|
it('handles all entries being unmatched (-1)', () => {
|
|
1165
1165
|
// All new keyed elements with no matching old keys
|
|
1166
1166
|
const root = el('<div z-key="x">X</div><div z-key="y">Y</div>');
|
|
@@ -1184,7 +1184,7 @@ describe('morph — LIS edge cases', () => {
|
|
|
1184
1184
|
morph(root,
|
|
1185
1185
|
'<div z-key="a">A2</div><div z-key="b">B2</div><div z-key="c">C2</div>'
|
|
1186
1186
|
);
|
|
1187
|
-
// All in order
|
|
1187
|
+
// All in order - identity preserved, no moves
|
|
1188
1188
|
expect(root.children[0]).toBe(refs[0]);
|
|
1189
1189
|
expect(root.children[1]).toBe(refs[1]);
|
|
1190
1190
|
expect(root.children[2]).toBe(refs[2]);
|
|
@@ -1204,7 +1204,7 @@ describe('morph — LIS edge cases', () => {
|
|
|
1204
1204
|
});
|
|
1205
1205
|
});
|
|
1206
1206
|
|
|
1207
|
-
describe('morph
|
|
1207
|
+
describe('morph - mixed content type transitions', () => {
|
|
1208
1208
|
it('morphs from elements to mixed text and elements', () => {
|
|
1209
1209
|
const root = el('<p>one</p><p>two</p>');
|
|
1210
1210
|
morph(root, 'text<p>element</p>more text');
|
|
@@ -1230,7 +1230,7 @@ describe('morph — mixed content type transitions', () => {
|
|
|
1230
1230
|
});
|
|
1231
1231
|
});
|
|
1232
1232
|
|
|
1233
|
-
describe('morph
|
|
1233
|
+
describe('morph - deeply nested keyed reorder', () => {
|
|
1234
1234
|
it('reorders keyed elements inside a nested structure', () => {
|
|
1235
1235
|
const root = el('<ul><li z-key="c">C</li><li z-key="a">A</li><li z-key="b">B</li></ul>');
|
|
1236
1236
|
const ul = root.querySelector('ul');
|
|
@@ -1244,7 +1244,7 @@ describe('morph — deeply nested keyed reorder', () => {
|
|
|
1244
1244
|
});
|
|
1245
1245
|
});
|
|
1246
1246
|
|
|
1247
|
-
describe('morph
|
|
1247
|
+
describe('morph - large scale stress tests', () => {
|
|
1248
1248
|
it('handles 1000 unkeyed elements', () => {
|
|
1249
1249
|
const genItems = n => Array.from({ length: n }, (_, i) => `<li>${i}</li>`).join('');
|
|
1250
1250
|
const root = el(genItems(1000));
|
|
@@ -1273,7 +1273,7 @@ describe('morph — large scale stress tests', () => {
|
|
|
1273
1273
|
});
|
|
1274
1274
|
});
|
|
1275
1275
|
|
|
1276
|
-
describe('morph
|
|
1276
|
+
describe('morph - auto-key edge cases', () => {
|
|
1277
1277
|
it('handles mixed auto-key types (id, data-id, data-key)', () => {
|
|
1278
1278
|
const root = el(
|
|
1279
1279
|
'<div id="x">X</div><div data-id="y">Y</div><div data-key="z">Z</div>'
|
|
@@ -1295,7 +1295,7 @@ describe('morph — auto-key edge cases', () => {
|
|
|
1295
1295
|
});
|
|
1296
1296
|
});
|
|
1297
1297
|
|
|
1298
|
-
describe('morph
|
|
1298
|
+
describe('morph - whitespace and boundary edge cases', () => {
|
|
1299
1299
|
it('handles only whitespace in new HTML', () => {
|
|
1300
1300
|
const root = el('<p>content</p>');
|
|
1301
1301
|
morph(root, ' \n\t ');
|
|
@@ -1316,7 +1316,7 @@ describe('morph — whitespace and boundary edge cases', () => {
|
|
|
1316
1316
|
});
|
|
1317
1317
|
});
|
|
1318
1318
|
|
|
1319
|
-
describe('morph
|
|
1319
|
+
describe('morph - z-skip edge cases', () => {
|
|
1320
1320
|
it('preserves z-skip subtree even when parent changes', () => {
|
|
1321
1321
|
const root = el('<div class="outer"><div z-skip>KEEP ME</div><p>old</p></div>');
|
|
1322
1322
|
const skipDiv = root.querySelector('[z-skip]');
|
|
@@ -1337,7 +1337,7 @@ describe('morph — z-skip edge cases', () => {
|
|
|
1337
1337
|
});
|
|
1338
1338
|
});
|
|
1339
1339
|
|
|
1340
|
-
describe('morph
|
|
1340
|
+
describe('morph - morphElement edge cases', () => {
|
|
1341
1341
|
it('handles morphElement with complex nested content', () => {
|
|
1342
1342
|
const node = document.createElement('div');
|
|
1343
1343
|
node.innerHTML = '<ul><li>a</li><li>b</li></ul>';
|
|
@@ -1375,7 +1375,7 @@ describe('morph — morphElement edge cases', () => {
|
|
|
1375
1375
|
});
|
|
1376
1376
|
});
|
|
1377
1377
|
|
|
1378
|
-
describe('morph
|
|
1378
|
+
describe('morph - keyed nodes with tag changes during reorder', () => {
|
|
1379
1379
|
it('handles keyed nodes where matched node has different tag (replaceChild path)', () => {
|
|
1380
1380
|
// This tests the cursor stability fix: capture nextSibling before _morphNode
|
|
1381
1381
|
const root = el(
|
|
@@ -1391,7 +1391,7 @@ describe('morph — keyed nodes with tag changes during reorder', () => {
|
|
|
1391
1391
|
});
|
|
1392
1392
|
});
|
|
1393
1393
|
|
|
1394
|
-
describe('morph
|
|
1394
|
+
describe('morph - identity preservation across morphs', () => {
|
|
1395
1395
|
it('preserves element identity through multiple consecutive morphs', () => {
|
|
1396
1396
|
const root = el('<div id="target"><p>v1</p></div>');
|
|
1397
1397
|
const target = root.firstElementChild;
|