ripple 0.3.127 → 0.3.128
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/package.json +2 -2
- package/src/runtime/internal/client/blocks.js +20 -24
- package/src/runtime/internal/client/constants.js +5 -5
- package/src/runtime/internal/client/expression.js +142 -101
- package/src/runtime/internal/client/for.js +217 -145
- package/src/runtime/internal/client/index.js +5 -1
- package/src/runtime/internal/client/operations.js +24 -10
- package/src/runtime/internal/client/render.js +22 -3
- package/src/runtime/internal/client/runtime.js +246 -182
- package/src/runtime/internal/client/selector.js +74 -0
- package/src/runtime/internal/client/types.d.ts +11 -0
- package/src/runtime/internal/server/index.js +2 -0
- package/tests/client/basic/basic.reactivity.test.tsrx +33 -0
- package/tests/client/for-selector.test.tsrx +221 -0
- package/tests/hydration/compiled/client/basic.js +44 -44
- package/tests/hydration/compiled/client/composite.js +3 -3
- package/tests/hydration/compiled/client/events.js +56 -29
- package/tests/hydration/compiled/client/for.js +76 -70
- package/tests/hydration/compiled/client/head.js +11 -8
- package/tests/hydration/compiled/client/hmr.js +4 -4
- package/tests/hydration/compiled/client/html-in-template.js +3 -3
- package/tests/hydration/compiled/client/html.js +141 -141
- package/tests/hydration/compiled/client/if-children.js +29 -29
- package/tests/hydration/compiled/client/if-fragment-controlflow.js +16 -16
- package/tests/hydration/compiled/client/if.js +10 -10
- package/tests/hydration/compiled/client/mixed-control-flow.js +26 -22
- package/tests/hydration/compiled/client/nested-control-flow.js +114 -92
- package/tests/hydration/compiled/client/portal.js +8 -8
- package/tests/hydration/compiled/client/reactivity.js +38 -15
- package/tests/hydration/compiled/client/streaming.js +16 -16
- package/tests/hydration/compiled/client/switch.js +4 -4
- package/tests/hydration/compiled/client/track-async-serialization.js +18 -18
- package/tests/hydration/compiled/client/try.js +8 -8
- package/tests/hydration/compiled/server/events.js +5 -53
- package/tests/hydration/compiled/server/for.js +2 -18
- package/tests/hydration/compiled/server/head.js +1 -9
- package/tests/hydration/compiled/server/html.js +1 -9
- package/tests/hydration/compiled/server/reactivity.js +2 -34
|
@@ -27,42 +27,46 @@ import { array_from, is_array } from '@tsrx/core/runtime/language-helpers';
|
|
|
27
27
|
* @returns {Block}
|
|
28
28
|
*/
|
|
29
29
|
function create_item(anchor, value, index, render_fn, is_indexed, is_keyed) {
|
|
30
|
-
var
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
var block = /** @type {Block} */ (active_block);
|
|
31
|
+
var tracked_index = is_indexed ? tracked(index, block) : undefined;
|
|
32
|
+
var tracked_value = is_keyed ? tracked(value, block) : value;
|
|
33
|
+
var state = {
|
|
34
|
+
start: null,
|
|
35
|
+
end: null,
|
|
36
|
+
i: tracked_index,
|
|
37
|
+
v: tracked_value,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Passed through module state rather than a per-item closure; run_item
|
|
41
|
+
// reads them before rendering, so nested loops cannot observe a stale pair.
|
|
42
|
+
item_anchor = anchor;
|
|
43
|
+
item_render_fn = render_fn;
|
|
44
|
+
var b = branch(run_item, 0, state);
|
|
45
|
+
|
|
46
|
+
// The item's tracked value and index are owned by the item block itself.
|
|
47
|
+
if (is_keyed) {
|
|
48
|
+
/** @type {Tracked} */ (tracked_value).b = b;
|
|
49
|
+
}
|
|
50
|
+
if (is_indexed) {
|
|
51
|
+
/** @type {Tracked} */ (tracked_index).b = b;
|
|
52
|
+
}
|
|
53
|
+
return b;
|
|
54
|
+
}
|
|
34
55
|
|
|
35
|
-
|
|
36
|
-
|
|
56
|
+
/** @type {Node | null} */
|
|
57
|
+
var item_anchor = null;
|
|
58
|
+
/** @type {((anchor: Node, value: any, index?: any) => Block) | null} */
|
|
59
|
+
var item_render_fn = null;
|
|
37
60
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
start: null,
|
|
48
|
-
end: null,
|
|
49
|
-
i: tracked_index,
|
|
50
|
-
v: tracked_value,
|
|
51
|
-
};
|
|
52
|
-
} else {
|
|
53
|
-
if (is_indexed) {
|
|
54
|
-
tracked_index = block.s.i;
|
|
55
|
-
}
|
|
56
|
-
if (is_keyed) {
|
|
57
|
-
tracked_index = block.s.v;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
render_fn(anchor, tracked_value, tracked_index);
|
|
61
|
-
} else {
|
|
62
|
-
render_fn(anchor, tracked_value);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
return b;
|
|
61
|
+
/**
|
|
62
|
+
* @param {{ i: Tracked | undefined, v: any }} state
|
|
63
|
+
*/
|
|
64
|
+
function run_item(state) {
|
|
65
|
+
var render_fn = /** @type {(anchor: Node, value: any, index?: any) => Block} */ (item_render_fn);
|
|
66
|
+
var anchor = /** @type {Node} */ (item_anchor);
|
|
67
|
+
item_render_fn = null;
|
|
68
|
+
item_anchor = null;
|
|
69
|
+
render_fn(anchor, state.v, state.i);
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
/**
|
|
@@ -316,7 +320,10 @@ function update_index(block, index) {
|
|
|
316
320
|
* @returns {void}
|
|
317
321
|
*/
|
|
318
322
|
function update_value(block, value) {
|
|
319
|
-
|
|
323
|
+
var tracked_value = block.s.v;
|
|
324
|
+
if (tracked_value.__v !== value) {
|
|
325
|
+
set(tracked_value, value);
|
|
326
|
+
}
|
|
320
327
|
}
|
|
321
328
|
|
|
322
329
|
/**
|
|
@@ -346,10 +353,6 @@ function reconcile_by_key(
|
|
|
346
353
|
|
|
347
354
|
// Variables used in conditional branches - declare with initial values
|
|
348
355
|
/** @type {number} */
|
|
349
|
-
var a_start = 0;
|
|
350
|
-
/** @type {number} */
|
|
351
|
-
var b_start = 0;
|
|
352
|
-
/** @type {number} */
|
|
353
356
|
var a_left = 0;
|
|
354
357
|
/** @type {number} */
|
|
355
358
|
var b_left = 0;
|
|
@@ -425,39 +428,31 @@ function reconcile_by_key(
|
|
|
425
428
|
|
|
426
429
|
var a_blocks = state.blocks;
|
|
427
430
|
var a_keys = state.keys;
|
|
428
|
-
var
|
|
429
|
-
var
|
|
430
|
-
var a_key = a_keys[j];
|
|
431
|
-
var b_key = b_keys[j];
|
|
431
|
+
var a_start = 0;
|
|
432
|
+
var b_start = 0;
|
|
432
433
|
var a_end = a_length - 1;
|
|
433
434
|
var b_end = b_length - 1;
|
|
435
|
+
var b_val;
|
|
434
436
|
var b_block;
|
|
435
437
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
438
|
+
// Match from both ends first, including the two end-crossing cases: an old
|
|
439
|
+
// item that moved to the far end of the new list, and a run whose ends were
|
|
440
|
+
// exchanged (a reversal, or a swap of two items). Those complete with plain
|
|
441
|
+
// moves; only what is left afterwards needs the map and LIS below.
|
|
442
|
+
while (a_start <= a_end && b_start <= b_end) {
|
|
443
|
+
if (a_keys[a_start] === b_keys[b_start]) {
|
|
444
|
+
b_val = b[b_start];
|
|
445
|
+
b_block = b_blocks[b_start] = a_blocks[a_start];
|
|
440
446
|
if (is_indexed) {
|
|
441
|
-
update_index(b_block,
|
|
447
|
+
update_index(b_block, b_start);
|
|
442
448
|
}
|
|
443
449
|
update_value(b_block, b_val);
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
}
|
|
448
|
-
a_val = a[j];
|
|
449
|
-
b_val = b[j];
|
|
450
|
-
a_key = a_keys[j];
|
|
451
|
-
b_key = b_keys[j];
|
|
450
|
+
a_start++;
|
|
451
|
+
b_start++;
|
|
452
|
+
continue;
|
|
452
453
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
b_val = b[b_end];
|
|
456
|
-
a_key = a_keys[a_end];
|
|
457
|
-
b_key = b_keys[b_end];
|
|
458
|
-
|
|
459
|
-
while (a_key === b_key) {
|
|
460
|
-
a[a_end] = b_val;
|
|
454
|
+
if (a_keys[a_end] === b_keys[b_end]) {
|
|
455
|
+
b_val = b[b_end];
|
|
461
456
|
b_block = b_blocks[b_end] = a_blocks[a_end];
|
|
462
457
|
if (is_indexed) {
|
|
463
458
|
update_index(b_block, b_end);
|
|
@@ -465,36 +460,64 @@ function reconcile_by_key(
|
|
|
465
460
|
update_value(b_block, b_val);
|
|
466
461
|
a_end--;
|
|
467
462
|
b_end--;
|
|
468
|
-
|
|
469
|
-
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
if (a_start === a_end || a_blocks[a_start].s.start === null) {
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
if (a_keys[a_end] === b_keys[b_start]) {
|
|
469
|
+
// Last old item is the next new one: move it in front of the old run.
|
|
470
|
+
b_val = b[b_start];
|
|
471
|
+
b_block = b_blocks[b_start] = a_blocks[a_end];
|
|
472
|
+
if (is_indexed) {
|
|
473
|
+
update_index(b_block, b_start);
|
|
470
474
|
}
|
|
471
|
-
|
|
475
|
+
update_value(b_block, b_val);
|
|
476
|
+
move(b_block, /** @type {ChildNode} */ (a_blocks[a_start].s.start));
|
|
477
|
+
a_end--;
|
|
478
|
+
b_start++;
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
if (a_keys[a_start] === b_keys[b_end]) {
|
|
482
|
+
// First old item is the last new one: move it behind the old run.
|
|
472
483
|
b_val = b[b_end];
|
|
473
|
-
|
|
474
|
-
|
|
484
|
+
b_block = b_blocks[b_end] = a_blocks[a_start];
|
|
485
|
+
if (is_indexed) {
|
|
486
|
+
update_index(b_block, b_end);
|
|
487
|
+
}
|
|
488
|
+
update_value(b_block, b_val);
|
|
489
|
+
move(b_block, block_start(b_blocks, b_end + 1, b_length, anchor));
|
|
490
|
+
a_start++;
|
|
491
|
+
b_end--;
|
|
492
|
+
continue;
|
|
475
493
|
}
|
|
494
|
+
break;
|
|
476
495
|
}
|
|
477
496
|
|
|
478
497
|
var fast_path_removal = false;
|
|
479
498
|
|
|
480
|
-
if (
|
|
481
|
-
if (
|
|
482
|
-
var
|
|
483
|
-
while (
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
499
|
+
if (a_start > a_end) {
|
|
500
|
+
if (b_start <= b_end) {
|
|
501
|
+
var target_node = block_start(b_blocks, b_end + 1, b_length, anchor);
|
|
502
|
+
while (b_start <= b_end) {
|
|
503
|
+
b_blocks[b_start] = create_item(
|
|
504
|
+
target_node,
|
|
505
|
+
b[b_start],
|
|
506
|
+
b_start,
|
|
507
|
+
render_fn,
|
|
508
|
+
is_indexed,
|
|
509
|
+
true,
|
|
510
|
+
);
|
|
511
|
+
b_start++;
|
|
487
512
|
}
|
|
488
513
|
}
|
|
489
|
-
} else if (
|
|
490
|
-
while (
|
|
491
|
-
destroy_block(a_blocks[
|
|
514
|
+
} else if (b_start > b_end) {
|
|
515
|
+
while (a_start <= a_end) {
|
|
516
|
+
destroy_block(a_blocks[a_start++]);
|
|
492
517
|
}
|
|
493
518
|
} else {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
a_left = a_end - j + 1;
|
|
497
|
-
b_left = b_end - j + 1;
|
|
519
|
+
a_left = a_end - a_start + 1;
|
|
520
|
+
b_left = b_end - b_start + 1;
|
|
498
521
|
sources = new Int32Array(b_left + 1);
|
|
499
522
|
moved = false;
|
|
500
523
|
pos = 0;
|
|
@@ -506,13 +529,9 @@ function reconcile_by_key(
|
|
|
506
529
|
// When sizes are small, just loop them through
|
|
507
530
|
if (b_length < 4 || (a_left | b_left) < 32) {
|
|
508
531
|
for (i = a_start; i <= a_end; ++i) {
|
|
509
|
-
a_val = a[i];
|
|
510
|
-
a_key = a_keys[i];
|
|
511
532
|
if (patched < b_left) {
|
|
512
533
|
for (j = b_start; j <= b_end; j++) {
|
|
513
|
-
|
|
514
|
-
b_key = b_keys[j];
|
|
515
|
-
if (a_key === b_key) {
|
|
534
|
+
if (a_keys[i] === b_keys[j]) {
|
|
516
535
|
sources[j - b_start] = i + 1;
|
|
517
536
|
if (fast_path_removal) {
|
|
518
537
|
fast_path_removal = false;
|
|
@@ -525,6 +544,7 @@ function reconcile_by_key(
|
|
|
525
544
|
} else {
|
|
526
545
|
pos = j;
|
|
527
546
|
}
|
|
547
|
+
b_val = b[j];
|
|
528
548
|
b_block = b_blocks[j] = a_blocks[i];
|
|
529
549
|
if (is_indexed) {
|
|
530
550
|
update_index(b_block, j);
|
|
@@ -549,11 +569,8 @@ function reconcile_by_key(
|
|
|
549
569
|
}
|
|
550
570
|
|
|
551
571
|
for (i = a_start; i <= a_end; ++i) {
|
|
552
|
-
a_val = a[i];
|
|
553
|
-
a_key = a_keys[i];
|
|
554
|
-
|
|
555
572
|
if (patched < b_left) {
|
|
556
|
-
j = map.get(
|
|
573
|
+
j = map.get(a_keys[i]);
|
|
557
574
|
|
|
558
575
|
if (j !== undefined) {
|
|
559
576
|
if (fast_path_removal) {
|
|
@@ -568,12 +585,12 @@ function reconcile_by_key(
|
|
|
568
585
|
} else {
|
|
569
586
|
pos = j;
|
|
570
587
|
}
|
|
571
|
-
block = b_blocks[j] = a_blocks[i];
|
|
572
588
|
b_val = b[j];
|
|
589
|
+
b_block = b_blocks[j] = a_blocks[i];
|
|
573
590
|
if (is_indexed) {
|
|
574
|
-
update_index(
|
|
591
|
+
update_index(b_block, j);
|
|
575
592
|
}
|
|
576
|
-
update_value(
|
|
593
|
+
update_value(b_block, b_val);
|
|
577
594
|
++patched;
|
|
578
595
|
} else if (!fast_path_removal) {
|
|
579
596
|
destroy_block(a_blocks[i]);
|
|
@@ -594,6 +611,25 @@ function reconcile_by_key(
|
|
|
594
611
|
var seq = lis_algorithm(sources);
|
|
595
612
|
j = seq.length - 1;
|
|
596
613
|
|
|
614
|
+
// When most surviving items have to move anyway, re-lay the whole range
|
|
615
|
+
// in order before a fixed target: inserting before the same node is
|
|
616
|
+
// noticeably cheaper per item than moving into arbitrary positions.
|
|
617
|
+
if ((patched - seq.length) * 3 > b_left * 2) {
|
|
618
|
+
var relay_target = block_start(b_blocks, b_end + 1, b_length, anchor);
|
|
619
|
+
for (i = 0; i < b_left; i++) {
|
|
620
|
+
pos = i + b_start;
|
|
621
|
+
if (sources[i] === 0) {
|
|
622
|
+
b_blocks[pos] = create_item(relay_target, b[pos], pos, render_fn, is_indexed, true);
|
|
623
|
+
} else {
|
|
624
|
+
move(b_blocks[pos], relay_target);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
state.array = b;
|
|
628
|
+
state.blocks = b_blocks;
|
|
629
|
+
state.keys = b_keys;
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
597
633
|
for (i = b_left - 1; i >= 0; i--) {
|
|
598
634
|
if (sources[i] === 0) {
|
|
599
635
|
pos = i + b_start;
|
|
@@ -604,7 +640,6 @@ function reconcile_by_key(
|
|
|
604
640
|
b_blocks[pos] = create_item(target, b_val, pos, render_fn, is_indexed, true);
|
|
605
641
|
} else if (j < 0 || i !== seq[j]) {
|
|
606
642
|
pos = i + b_start;
|
|
607
|
-
b_val = b[pos];
|
|
608
643
|
next_pos = pos + 1;
|
|
609
644
|
|
|
610
645
|
var target = block_start(b_blocks, next_pos, b_length, anchor);
|
|
@@ -647,10 +682,6 @@ function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed
|
|
|
647
682
|
|
|
648
683
|
// Variables used in conditional branches - declare with initial values
|
|
649
684
|
/** @type {number} */
|
|
650
|
-
var a_start = 0;
|
|
651
|
-
/** @type {number} */
|
|
652
|
-
var b_start = 0;
|
|
653
|
-
/** @type {number} */
|
|
654
685
|
var a_left = 0;
|
|
655
686
|
/** @type {number} */
|
|
656
687
|
var b_left = 0;
|
|
@@ -722,66 +753,92 @@ function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed
|
|
|
722
753
|
}
|
|
723
754
|
|
|
724
755
|
var a_blocks = state.blocks;
|
|
725
|
-
var
|
|
726
|
-
var
|
|
756
|
+
var a_start = 0;
|
|
757
|
+
var b_start = 0;
|
|
727
758
|
var a_end = a_length - 1;
|
|
728
759
|
var b_end = b_length - 1;
|
|
760
|
+
var b_val;
|
|
729
761
|
var b_block;
|
|
730
762
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
763
|
+
// Match from both ends first, including the two end-crossing cases: an old
|
|
764
|
+
// item that moved to the far end of the new list, and a run whose ends were
|
|
765
|
+
// exchanged (a reversal, or a swap of two items). Those complete with plain
|
|
766
|
+
// moves; only what is left afterwards needs the map and LIS below.
|
|
767
|
+
while (a_start <= a_end && b_start <= b_end) {
|
|
768
|
+
if (a[a_start] === b[b_start]) {
|
|
769
|
+
b_val = b[b_start];
|
|
770
|
+
b_block = b_blocks[b_start] = a_blocks[a_start];
|
|
735
771
|
if (is_indexed) {
|
|
736
|
-
update_index(b_block,
|
|
737
|
-
}
|
|
738
|
-
++j;
|
|
739
|
-
if (j > a_end || j > b_end) {
|
|
740
|
-
break outer;
|
|
772
|
+
update_index(b_block, b_start);
|
|
741
773
|
}
|
|
742
|
-
|
|
743
|
-
|
|
774
|
+
a_start++;
|
|
775
|
+
b_start++;
|
|
776
|
+
continue;
|
|
744
777
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
b_val = b[b_end];
|
|
748
|
-
|
|
749
|
-
while (a_val === b_val) {
|
|
750
|
-
a[a_end] = b_val;
|
|
778
|
+
if (a[a_end] === b[b_end]) {
|
|
779
|
+
b_val = b[b_end];
|
|
751
780
|
b_block = b_blocks[b_end] = a_blocks[a_end];
|
|
752
781
|
if (is_indexed) {
|
|
753
782
|
update_index(b_block, b_end);
|
|
754
783
|
}
|
|
755
784
|
a_end--;
|
|
756
785
|
b_end--;
|
|
757
|
-
|
|
758
|
-
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
if (a_start === a_end || a_blocks[a_start].s.start === null) {
|
|
789
|
+
break;
|
|
790
|
+
}
|
|
791
|
+
if (a[a_end] === b[b_start]) {
|
|
792
|
+
// Last old item is the next new one: move it in front of the old run.
|
|
793
|
+
b_val = b[b_start];
|
|
794
|
+
b_block = b_blocks[b_start] = a_blocks[a_end];
|
|
795
|
+
if (is_indexed) {
|
|
796
|
+
update_index(b_block, b_start);
|
|
759
797
|
}
|
|
760
|
-
|
|
798
|
+
move(b_block, /** @type {ChildNode} */ (a_blocks[a_start].s.start));
|
|
799
|
+
a_end--;
|
|
800
|
+
b_start++;
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
if (a[a_start] === b[b_end]) {
|
|
804
|
+
// First old item is the last new one: move it behind the old run.
|
|
761
805
|
b_val = b[b_end];
|
|
806
|
+
b_block = b_blocks[b_end] = a_blocks[a_start];
|
|
807
|
+
if (is_indexed) {
|
|
808
|
+
update_index(b_block, b_end);
|
|
809
|
+
}
|
|
810
|
+
move(b_block, block_start(b_blocks, b_end + 1, b_length, anchor));
|
|
811
|
+
a_start++;
|
|
812
|
+
b_end--;
|
|
813
|
+
continue;
|
|
762
814
|
}
|
|
815
|
+
break;
|
|
763
816
|
}
|
|
764
817
|
|
|
765
818
|
var fast_path_removal = false;
|
|
766
819
|
|
|
767
|
-
if (
|
|
768
|
-
if (
|
|
769
|
-
var
|
|
770
|
-
while (
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
820
|
+
if (a_start > a_end) {
|
|
821
|
+
if (b_start <= b_end) {
|
|
822
|
+
var target_node = block_start(b_blocks, b_end + 1, b_length, anchor);
|
|
823
|
+
while (b_start <= b_end) {
|
|
824
|
+
b_blocks[b_start] = create_item(
|
|
825
|
+
target_node,
|
|
826
|
+
b[b_start],
|
|
827
|
+
b_start,
|
|
828
|
+
render_fn,
|
|
829
|
+
is_indexed,
|
|
830
|
+
false,
|
|
831
|
+
);
|
|
832
|
+
b_start++;
|
|
774
833
|
}
|
|
775
834
|
}
|
|
776
|
-
} else if (
|
|
777
|
-
while (
|
|
778
|
-
destroy_block(a_blocks[
|
|
835
|
+
} else if (b_start > b_end) {
|
|
836
|
+
while (a_start <= a_end) {
|
|
837
|
+
destroy_block(a_blocks[a_start++]);
|
|
779
838
|
}
|
|
780
839
|
} else {
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
a_left = a_end - j + 1;
|
|
784
|
-
b_left = b_end - j + 1;
|
|
840
|
+
a_left = a_end - a_start + 1;
|
|
841
|
+
b_left = b_end - b_start + 1;
|
|
785
842
|
sources = new Int32Array(b_left + 1);
|
|
786
843
|
moved = false;
|
|
787
844
|
pos = 0;
|
|
@@ -793,11 +850,9 @@ function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed
|
|
|
793
850
|
// When sizes are small, just loop them through
|
|
794
851
|
if (b_length < 4 || (a_left | b_left) < 32) {
|
|
795
852
|
for (i = a_start; i <= a_end; ++i) {
|
|
796
|
-
a_val = a[i];
|
|
797
853
|
if (patched < b_left) {
|
|
798
854
|
for (j = b_start; j <= b_end; j++) {
|
|
799
|
-
|
|
800
|
-
if (a_val === b_val) {
|
|
855
|
+
if (a[i] === b[j]) {
|
|
801
856
|
sources[j - b_start] = i + 1;
|
|
802
857
|
if (fast_path_removal) {
|
|
803
858
|
fast_path_removal = false;
|
|
@@ -810,6 +865,7 @@ function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed
|
|
|
810
865
|
} else {
|
|
811
866
|
pos = j;
|
|
812
867
|
}
|
|
868
|
+
b_val = b[j];
|
|
813
869
|
b_block = b_blocks[j] = a_blocks[i];
|
|
814
870
|
if (is_indexed) {
|
|
815
871
|
update_index(b_block, j);
|
|
@@ -833,10 +889,8 @@ function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed
|
|
|
833
889
|
}
|
|
834
890
|
|
|
835
891
|
for (i = a_start; i <= a_end; ++i) {
|
|
836
|
-
a_val = a[i];
|
|
837
|
-
|
|
838
892
|
if (patched < b_left) {
|
|
839
|
-
j = map.get(
|
|
893
|
+
j = map.get(a[i]);
|
|
840
894
|
|
|
841
895
|
if (j !== undefined) {
|
|
842
896
|
if (fast_path_removal) {
|
|
@@ -851,9 +905,10 @@ function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed
|
|
|
851
905
|
} else {
|
|
852
906
|
pos = j;
|
|
853
907
|
}
|
|
854
|
-
|
|
908
|
+
b_val = b[j];
|
|
909
|
+
b_block = b_blocks[j] = a_blocks[i];
|
|
855
910
|
if (is_indexed) {
|
|
856
|
-
update_index(
|
|
911
|
+
update_index(b_block, j);
|
|
857
912
|
}
|
|
858
913
|
++patched;
|
|
859
914
|
} else if (!fast_path_removal) {
|
|
@@ -875,6 +930,24 @@ function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed
|
|
|
875
930
|
var seq = lis_algorithm(sources);
|
|
876
931
|
j = seq.length - 1;
|
|
877
932
|
|
|
933
|
+
// When most surviving items have to move anyway, re-lay the whole range
|
|
934
|
+
// in order before a fixed target: inserting before the same node is
|
|
935
|
+
// noticeably cheaper per item than moving into arbitrary positions.
|
|
936
|
+
if ((patched - seq.length) * 3 > b_left * 2) {
|
|
937
|
+
var relay_target = block_start(b_blocks, b_end + 1, b_length, anchor);
|
|
938
|
+
for (i = 0; i < b_left; i++) {
|
|
939
|
+
pos = i + b_start;
|
|
940
|
+
if (sources[i] === 0) {
|
|
941
|
+
b_blocks[pos] = create_item(relay_target, b[pos], pos, render_fn, is_indexed, false);
|
|
942
|
+
} else {
|
|
943
|
+
move(b_blocks[pos], relay_target);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
state.array = b;
|
|
947
|
+
state.blocks = b_blocks;
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
|
|
878
951
|
for (i = b_left - 1; i >= 0; i--) {
|
|
879
952
|
if (sources[i] === 0) {
|
|
880
953
|
pos = i + b_start;
|
|
@@ -885,7 +958,6 @@ function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed
|
|
|
885
958
|
b_blocks[pos] = create_item(target, b_val, pos, render_fn, is_indexed, false);
|
|
886
959
|
} else if (j < 0 || i !== seq[j]) {
|
|
887
960
|
pos = i + b_start;
|
|
888
|
-
b_val = b[pos];
|
|
889
961
|
next_pos = pos + 1;
|
|
890
962
|
|
|
891
963
|
var target = block_start(b_blocks, next_pos, b_length, anchor);
|
|
@@ -2,6 +2,8 @@ export {
|
|
|
2
2
|
first_child as child,
|
|
3
3
|
first_child_frag,
|
|
4
4
|
next_sibling as sibling,
|
|
5
|
+
hydrate_first_child as hydrate_child,
|
|
6
|
+
hydrate_next_sibling as hydrate_sibling,
|
|
5
7
|
append_into,
|
|
6
8
|
document,
|
|
7
9
|
create_text,
|
|
@@ -90,6 +92,8 @@ export { render_component } from './component.js';
|
|
|
90
92
|
|
|
91
93
|
export { for_block as for, for_block_keyed as for_keyed } from './for.js';
|
|
92
94
|
|
|
95
|
+
export { selector, selector_match } from './selector.js';
|
|
96
|
+
|
|
93
97
|
export { if_block as if } from './if.js';
|
|
94
98
|
|
|
95
99
|
export { try_block as try, get_pending_boundary } from './try.js';
|
|
@@ -134,6 +138,6 @@ export { TRY_BLOCK, HMR } from './constants.js';
|
|
|
134
138
|
|
|
135
139
|
export { hmr } from './hmr.js';
|
|
136
140
|
|
|
137
|
-
export { pop, next } from './hydration.js';
|
|
141
|
+
export { pop, next, hydrating } from './hydration.js';
|
|
138
142
|
|
|
139
143
|
export { is_tsrx_element, tsrx_element, normalize_children } from '../../element.js';
|
|
@@ -37,6 +37,8 @@ export function init_operations() {
|
|
|
37
37
|
|
|
38
38
|
// the following assignments improve perf of lookups on DOM nodes
|
|
39
39
|
element_prototype.__click = undefined;
|
|
40
|
+
element_prototype.__className = undefined;
|
|
41
|
+
Text.prototype.__t = undefined;
|
|
40
42
|
event_target_prototype.__root = undefined;
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -66,8 +68,16 @@ export function get_last_child(node) {
|
|
|
66
68
|
*/
|
|
67
69
|
export function first_child(node, is_text) {
|
|
68
70
|
if (!hydrating) {
|
|
69
|
-
return
|
|
71
|
+
return node.firstChild;
|
|
70
72
|
}
|
|
73
|
+
return hydrate_first_child(is_text);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @param {boolean} [is_text]
|
|
78
|
+
* @returns {Node | null}
|
|
79
|
+
*/
|
|
80
|
+
export function hydrate_first_child(is_text) {
|
|
71
81
|
var child = get_first_child(/** @type {Node} */ (hydrate_node));
|
|
72
82
|
|
|
73
83
|
// Handles the case where we have `<p>{text}</p>`, where `text` is empty
|
|
@@ -144,17 +154,21 @@ export function get_next_sibling(node) {
|
|
|
144
154
|
* @returns {Node | null}
|
|
145
155
|
*/
|
|
146
156
|
export function next_sibling(node, is_text) {
|
|
147
|
-
let next_sibling = hydrating ? hydrate_node : node;
|
|
148
|
-
var last_sibling;
|
|
149
|
-
|
|
150
|
-
next_sibling = /** @type {ChildNode | null} */ (
|
|
151
|
-
get_next_sibling(/** @type {ChildNode} */ (next_sibling))
|
|
152
|
-
);
|
|
153
|
-
last_sibling = next_sibling;
|
|
154
|
-
|
|
155
157
|
if (!hydrating) {
|
|
156
|
-
return
|
|
158
|
+
return node.nextSibling;
|
|
157
159
|
}
|
|
160
|
+
return hydrate_next_sibling(is_text);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @param {boolean} [is_text]
|
|
165
|
+
* @returns {Node | null}
|
|
166
|
+
*/
|
|
167
|
+
export function hydrate_next_sibling(is_text) {
|
|
168
|
+
var next_sibling = /** @type {ChildNode | null} */ (
|
|
169
|
+
get_next_sibling(/** @type {ChildNode} */ (hydrate_node))
|
|
170
|
+
);
|
|
171
|
+
var last_sibling = next_sibling;
|
|
158
172
|
|
|
159
173
|
// if a sibling {expression} is empty during SSR, there might be no
|
|
160
174
|
// text node to hydrate — we must therefore create one
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { event } from './events.js';
|
|
13
13
|
import { get_attribute_event_name, is_event_attribute } from '@tsrx/core/runtime/events';
|
|
14
14
|
import { get } from './runtime.js';
|
|
15
|
+
import { hydrating } from './hydration.js';
|
|
15
16
|
import { clsx } from 'clsx';
|
|
16
17
|
import { normalize_css_property_name } from '@tsrx/core/runtime/html';
|
|
17
18
|
|
|
@@ -23,7 +24,13 @@ import { normalize_css_property_name } from '@tsrx/core/runtime/html';
|
|
|
23
24
|
export function set_text(text, value) {
|
|
24
25
|
// For objects, we apply string coercion
|
|
25
26
|
var str = value == null ? '' : typeof value === 'object' ? value + '' : value;
|
|
26
|
-
|
|
27
|
+
var previous = text.__t;
|
|
28
|
+
// Only server-rendered text can already hold the value; a fresh template
|
|
29
|
+
// text node never does, so skip reading it back.
|
|
30
|
+
if (previous === undefined && hydrating) {
|
|
31
|
+
previous = text.__t = text.nodeValue;
|
|
32
|
+
}
|
|
33
|
+
if (str !== previous) {
|
|
27
34
|
text.__t = str;
|
|
28
35
|
text.nodeValue = str + '';
|
|
29
36
|
}
|
|
@@ -168,16 +175,28 @@ function set_attribute_helper(element, key, value, remove_listeners, prev) {
|
|
|
168
175
|
export function set_class(dom, value, hash, is_html = true) {
|
|
169
176
|
var class_value =
|
|
170
177
|
value == null
|
|
171
|
-
?
|
|
178
|
+
? hash === undefined
|
|
179
|
+
? null
|
|
180
|
+
: hash
|
|
172
181
|
: // Fast-path for string values
|
|
173
182
|
typeof value === 'string'
|
|
174
183
|
? value + (hash ? ' ' + hash : '')
|
|
175
184
|
: clsx([value, hash]);
|
|
176
185
|
|
|
186
|
+
// Skip the DOM write when the class we last applied is unchanged, or when
|
|
187
|
+
// an element that never had a class would only receive an empty one (the
|
|
188
|
+
// server omits an empty class attribute as well).
|
|
189
|
+
var previous = dom.__className;
|
|
190
|
+
if (previous === class_value || (previous === undefined && class_value === '')) {
|
|
191
|
+
dom.__className = class_value;
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
dom.__className = class_value;
|
|
195
|
+
|
|
177
196
|
// Removing the attribute when the value is only an empty string causes
|
|
178
197
|
// performance issues vs simply making the className an empty string. So
|
|
179
198
|
// we should only remove the class if the value is nullish.
|
|
180
|
-
if (
|
|
199
|
+
if (class_value === null) {
|
|
181
200
|
dom.removeAttribute('class');
|
|
182
201
|
} else {
|
|
183
202
|
if (is_html) {
|