ripple 0.3.64 → 0.3.66
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 +46 -0
- package/package.json +3 -3
- package/src/jsx-runtime.d.ts +17 -3
- package/src/runtime/index-client.js +47 -8
- package/src/runtime/index-server.js +0 -2
- package/src/runtime/internal/client/index.js +5 -0
- package/src/runtime/internal/client/runtime.js +111 -11
- package/src/runtime/internal/client/types.d.ts +5 -0
- package/src/runtime/internal/server/blocks.js +1 -0
- package/src/runtime/internal/server/index.js +175 -20
- package/src/utils/errors.js +13 -0
- package/tests/client/async-suspend.test.tsrx +2 -2
- package/tests/client/basic/basic.get-set.test.tsrx +26 -26
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +1 -1
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.tsrx.snap +1 -1
- package/tests/client/compiler/compiler.assignments.test.tsrx +80 -0
- package/tests/client/compiler/compiler.tracked-access.test.tsrx +52 -8
- package/tests/client/compiler/compiler.typescript.test.tsrx +23 -0
- package/tests/client/lazy-array.test.tsrx +34 -0
- package/tests/client/lazy-destructuring.test.tsrx +79 -8
- package/tests/client/tracked-index-access.test.tsrx +113 -0
- package/tests/client/tsx.test.tsrx +66 -21
- package/tests/hydration/compiled/client/basic.js +2 -2
- package/tests/hydration/compiled/client/events.js +9 -9
- package/tests/hydration/compiled/client/for.js +50 -54
- package/tests/hydration/compiled/client/head.js +9 -9
- package/tests/hydration/compiled/client/hmr.js +1 -1
- package/tests/hydration/compiled/client/html.js +2 -2
- package/tests/hydration/compiled/client/if-children.js +14 -14
- package/tests/hydration/compiled/client/if.js +10 -10
- package/tests/hydration/compiled/client/mixed-control-flow.js +7 -7
- package/tests/hydration/compiled/client/portal.js +2 -2
- package/tests/hydration/compiled/client/reactivity.js +7 -7
- package/tests/hydration/compiled/client/return.js +37 -37
- package/tests/hydration/compiled/client/switch.js +8 -8
- package/tests/hydration/compiled/client/track-async-serialization.js +12 -12
- package/tests/hydration/compiled/client/try.js +116 -33
- package/tests/hydration/compiled/server/basic.js +2 -2
- package/tests/hydration/compiled/server/events.js +8 -8
- package/tests/hydration/compiled/server/for.js +21 -21
- package/tests/hydration/compiled/server/head.js +10 -10
- package/tests/hydration/compiled/server/hmr.js +1 -1
- package/tests/hydration/compiled/server/html.js +1 -1
- package/tests/hydration/compiled/server/if-children.js +9 -9
- package/tests/hydration/compiled/server/if.js +6 -6
- package/tests/hydration/compiled/server/mixed-control-flow.js +4 -4
- package/tests/hydration/compiled/server/portal.js +1 -1
- package/tests/hydration/compiled/server/reactivity.js +7 -7
- package/tests/hydration/compiled/server/return.js +14 -14
- package/tests/hydration/compiled/server/switch.js +4 -4
- package/tests/hydration/compiled/server/track-async-serialization.js +12 -12
- package/tests/hydration/compiled/server/try.js +116 -4
- package/tests/hydration/components/basic.tsrx +3 -1
- package/tests/hydration/components/try.tsrx +26 -0
- package/tests/hydration/try.test.js +100 -1
- package/tests/server/await.test.tsrx +1 -1
- package/tests/server/basic.test.tsrx +3 -1
- package/tests/server/compiler.test.tsrx +109 -0
- package/tests/server/lazy-destructuring.test.tsrx +62 -0
- package/tests/server/tracked-index-access.test.tsrx +76 -0
- package/tests/setup-hydration.js +31 -0
- package/types/index.d.ts +11 -9
- package/types/server.d.ts +2 -1
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
} from '../client/constants.js';
|
|
29
29
|
import { DEV } from 'esm-env';
|
|
30
30
|
import { is_ripple_object } from '../client/utils.js';
|
|
31
|
-
import { array_slice, is_array } from '@tsrx/core/runtime/language-helpers';
|
|
31
|
+
import { iterable_array_from, array_slice, is_array } from '@tsrx/core/runtime/language-helpers';
|
|
32
32
|
import {
|
|
33
33
|
escape,
|
|
34
34
|
escape_script,
|
|
@@ -44,6 +44,10 @@ import {
|
|
|
44
44
|
is_tag_valid_with_ancestor,
|
|
45
45
|
} from '../../../html-tree-validation.js';
|
|
46
46
|
import { get_async_track_result } from '../../../utils/async.js';
|
|
47
|
+
import {
|
|
48
|
+
throw_tracked_index_reference_error,
|
|
49
|
+
throw_tracked_index_value_error,
|
|
50
|
+
} from '../../../utils/errors.js';
|
|
47
51
|
import { get_track_async_script_id } from '../../../utils/track-async-serialization.js';
|
|
48
52
|
import * as devalue from 'devalue';
|
|
49
53
|
import {
|
|
@@ -696,17 +700,26 @@ export async function render(component, passed_in_options = {}) {
|
|
|
696
700
|
}
|
|
697
701
|
},
|
|
698
702
|
(error) => {
|
|
699
|
-
// TODO - allow a global error template in ripple.config.ts
|
|
700
703
|
// We're not going to send the error in the stream stream.error()
|
|
701
704
|
// as we should send sent the error template
|
|
702
705
|
|
|
703
706
|
// store the error to be returned
|
|
704
707
|
top_level_error = error;
|
|
705
|
-
|
|
708
|
+
const output = /** @type {Block | null} */ (root_block)?.o;
|
|
709
|
+
if (output?.isSyncRun()) {
|
|
710
|
+
output._decrementPending();
|
|
711
|
+
output._finishSyncRun();
|
|
712
|
+
}
|
|
713
|
+
if (options.rootBoundary?.catch) {
|
|
714
|
+
options.rootBoundary.catch({ error, reset: noop });
|
|
715
|
+
} else {
|
|
716
|
+
console.error(error);
|
|
717
|
+
}
|
|
706
718
|
},
|
|
707
719
|
() => {
|
|
708
|
-
|
|
709
|
-
|
|
720
|
+
if (options.rootBoundary?.pending) {
|
|
721
|
+
options.rootBoundary.pending({});
|
|
722
|
+
}
|
|
710
723
|
},
|
|
711
724
|
);
|
|
712
725
|
|
|
@@ -863,12 +876,49 @@ export function get(tracked) {
|
|
|
863
876
|
return tracked;
|
|
864
877
|
}
|
|
865
878
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
879
|
+
return (tracked.f & DERIVED) !== 0
|
|
880
|
+
? get_derived(/** @type {Derived} */ (tracked))
|
|
881
|
+
: get_tracked(/** @type {Tracked} */ (tracked));
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* @param {Tracked} tracked
|
|
886
|
+
* @returns {any}
|
|
887
|
+
*/
|
|
888
|
+
export function get_tracked(tracked) {
|
|
889
|
+
if (tracking) {
|
|
890
|
+
register_dependency(tracked);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
if (tracked.v === SUSPENSE_PENDING || tracked.v === SUSPENSE_REJECTED) {
|
|
894
|
+
var is_try_block = false;
|
|
895
|
+
if (
|
|
896
|
+
!inside_async_track &&
|
|
897
|
+
(!active_block ||
|
|
898
|
+
active_block.f & COMPONENT_BLOCK ||
|
|
899
|
+
(is_try_block = (active_block.f & TRY_BLOCK) !== 0))
|
|
900
|
+
) {
|
|
901
|
+
throw new Error(
|
|
902
|
+
`Reads on pending tracked or derived values directly inside ${is_try_block ? 'try' : 'component'} body are prohibited. Use trackPending() test for safe access or create another derived instead.`,
|
|
903
|
+
);
|
|
870
904
|
}
|
|
871
|
-
|
|
905
|
+
|
|
906
|
+
// this will be caught by the run_block and the block will be re-run
|
|
907
|
+
// once the async tracked dependency's promise resolves
|
|
908
|
+
throw ASYNC_DERIVED_READ_THROWN;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
var g = tracked.a.get;
|
|
912
|
+
return g ? g(tracked.v) : tracked.v;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* @param {Derived} tracked
|
|
917
|
+
* @returns {any}
|
|
918
|
+
*/
|
|
919
|
+
export function get_derived(tracked) {
|
|
920
|
+
update_derived(tracked);
|
|
921
|
+
if (tracking) {
|
|
872
922
|
register_dependency(tracked);
|
|
873
923
|
}
|
|
874
924
|
|
|
@@ -894,6 +944,60 @@ export function get(tracked) {
|
|
|
894
944
|
return g ? g(tracked.v) : tracked.v;
|
|
895
945
|
}
|
|
896
946
|
|
|
947
|
+
/**
|
|
948
|
+
* @param {any} lazy
|
|
949
|
+
* @param {number} [index]
|
|
950
|
+
* @returns {any}
|
|
951
|
+
*/
|
|
952
|
+
export function lazy_array_get(lazy, index = 0) {
|
|
953
|
+
if (is_array(lazy)) {
|
|
954
|
+
return lazy[index];
|
|
955
|
+
}
|
|
956
|
+
var flags = lazy.f;
|
|
957
|
+
if (flags === TRACKED) {
|
|
958
|
+
return index === 0
|
|
959
|
+
? get_tracked(/** @type {Tracked} */ (lazy))
|
|
960
|
+
: index === 1
|
|
961
|
+
? lazy
|
|
962
|
+
: undefined;
|
|
963
|
+
}
|
|
964
|
+
if (flags === DERIVED) {
|
|
965
|
+
return index === 0
|
|
966
|
+
? get_derived(/** @type {Derived} */ (lazy))
|
|
967
|
+
: index === 1
|
|
968
|
+
? lazy
|
|
969
|
+
: undefined;
|
|
970
|
+
}
|
|
971
|
+
return iterable_array_from(lazy, index)[0];
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* @param {any} lazy
|
|
976
|
+
* @param {number} [index]
|
|
977
|
+
* @returns {any[]}
|
|
978
|
+
*/
|
|
979
|
+
export function lazy_array_rest(lazy, index = 0) {
|
|
980
|
+
if (is_array(lazy)) {
|
|
981
|
+
return lazy.slice(index);
|
|
982
|
+
}
|
|
983
|
+
var flags = lazy.f;
|
|
984
|
+
if (flags === TRACKED) {
|
|
985
|
+
return index === 0
|
|
986
|
+
? [get_tracked(/** @type {Tracked} */ (lazy)), lazy]
|
|
987
|
+
: index === 1
|
|
988
|
+
? [lazy]
|
|
989
|
+
: [];
|
|
990
|
+
}
|
|
991
|
+
if (flags === DERIVED) {
|
|
992
|
+
return index === 0
|
|
993
|
+
? [get_derived(/** @type {Derived} */ (lazy)), lazy]
|
|
994
|
+
: index === 1
|
|
995
|
+
? [lazy]
|
|
996
|
+
: [];
|
|
997
|
+
}
|
|
998
|
+
return iterable_array_from(lazy, index);
|
|
999
|
+
}
|
|
1000
|
+
|
|
897
1001
|
/**
|
|
898
1002
|
* @param {Derived | Tracked} tracked
|
|
899
1003
|
* @param {any} value
|
|
@@ -908,6 +1012,57 @@ export function set(tracked, value) {
|
|
|
908
1012
|
}
|
|
909
1013
|
}
|
|
910
1014
|
|
|
1015
|
+
/**
|
|
1016
|
+
* @param {any} lazy
|
|
1017
|
+
* @param {any} value
|
|
1018
|
+
* @param {number} [index]
|
|
1019
|
+
* @returns {void}
|
|
1020
|
+
*/
|
|
1021
|
+
export function lazy_array_set(lazy, value, index = 0) {
|
|
1022
|
+
if (is_array(lazy)) {
|
|
1023
|
+
lazy[index] = value;
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
var flags = lazy.f;
|
|
1027
|
+
if (flags === TRACKED || flags === DERIVED) {
|
|
1028
|
+
if (index === 0) {
|
|
1029
|
+
set(/** @type {Derived | Tracked} */ (lazy), value);
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
if (index === 1) {
|
|
1033
|
+
throw_tracked_index_reference_error();
|
|
1034
|
+
}
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
lazy[index] = value;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* @param {any} lazy
|
|
1042
|
+
* @param {number} [index]
|
|
1043
|
+
* @param {number} [d]
|
|
1044
|
+
* @returns {number}
|
|
1045
|
+
*/
|
|
1046
|
+
export function lazy_array_update(lazy, index = 0, d = 1) {
|
|
1047
|
+
var value = lazy_array_get(lazy, index);
|
|
1048
|
+
var result = d === 1 ? value++ : value--;
|
|
1049
|
+
lazy_array_set(lazy, value, index);
|
|
1050
|
+
return result;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
/**
|
|
1054
|
+
* @param {any} lazy
|
|
1055
|
+
* @param {number} [index]
|
|
1056
|
+
* @param {number} [d]
|
|
1057
|
+
* @returns {number}
|
|
1058
|
+
*/
|
|
1059
|
+
export function lazy_array_update_pre(lazy, index = 0, d = 1) {
|
|
1060
|
+
var value = lazy_array_get(lazy, index);
|
|
1061
|
+
var new_value = d === 1 ? ++value : --value;
|
|
1062
|
+
lazy_array_set(lazy, new_value, index);
|
|
1063
|
+
return new_value;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
911
1066
|
/**
|
|
912
1067
|
* @param {Tracked} tracked
|
|
913
1068
|
* @param {number} [d]
|
|
@@ -1082,19 +1237,19 @@ class TrackedValue {
|
|
|
1082
1237
|
}
|
|
1083
1238
|
/** @returns {any} */
|
|
1084
1239
|
get [0]() {
|
|
1085
|
-
return
|
|
1240
|
+
return throw_tracked_index_value_error();
|
|
1086
1241
|
}
|
|
1087
1242
|
/** @param {any} v */
|
|
1088
1243
|
set [0](v) {
|
|
1089
|
-
|
|
1244
|
+
throw_tracked_index_value_error();
|
|
1090
1245
|
}
|
|
1091
1246
|
/** @returns {Tracked} */
|
|
1092
1247
|
get [1]() {
|
|
1093
|
-
return
|
|
1248
|
+
return throw_tracked_index_reference_error();
|
|
1094
1249
|
}
|
|
1095
1250
|
/** @returns {any} */
|
|
1096
1251
|
get value() {
|
|
1097
|
-
return
|
|
1252
|
+
return get_tracked(/** @type {Tracked} */ (this));
|
|
1098
1253
|
}
|
|
1099
1254
|
/** @param {any} v */
|
|
1100
1255
|
set value(v) {
|
|
@@ -1106,7 +1261,7 @@ class TrackedValue {
|
|
|
1106
1261
|
}
|
|
1107
1262
|
/** @returns {Iterator<any | Tracked>} */
|
|
1108
1263
|
*[Symbol.iterator]() {
|
|
1109
|
-
yield
|
|
1264
|
+
yield get_tracked(/** @type {Tracked} */ (this));
|
|
1110
1265
|
yield this;
|
|
1111
1266
|
}
|
|
1112
1267
|
}
|
|
@@ -1140,19 +1295,19 @@ class DerivedValue {
|
|
|
1140
1295
|
}
|
|
1141
1296
|
/** @returns {any} */
|
|
1142
1297
|
get [0]() {
|
|
1143
|
-
return
|
|
1298
|
+
return throw_tracked_index_value_error();
|
|
1144
1299
|
}
|
|
1145
1300
|
/** @param {any} v */
|
|
1146
1301
|
set [0](v) {
|
|
1147
|
-
|
|
1302
|
+
throw_tracked_index_value_error();
|
|
1148
1303
|
}
|
|
1149
1304
|
/** @returns {Derived} */
|
|
1150
1305
|
get [1]() {
|
|
1151
|
-
return
|
|
1306
|
+
return throw_tracked_index_reference_error();
|
|
1152
1307
|
}
|
|
1153
1308
|
/** @returns {any} */
|
|
1154
1309
|
get value() {
|
|
1155
|
-
return
|
|
1310
|
+
return get_derived(/** @type {Derived} */ (this));
|
|
1156
1311
|
}
|
|
1157
1312
|
/** @param {any} v */
|
|
1158
1313
|
set value(v) {
|
|
@@ -1164,7 +1319,7 @@ class DerivedValue {
|
|
|
1164
1319
|
}
|
|
1165
1320
|
/** @returns {Iterator<any | Derived>} */
|
|
1166
1321
|
*[Symbol.iterator]() {
|
|
1167
|
-
yield
|
|
1322
|
+
yield get_derived(/** @type {Derived} */ (this));
|
|
1168
1323
|
yield this;
|
|
1169
1324
|
}
|
|
1170
1325
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** @returns {never} */
|
|
2
|
+
export function throw_tracked_index_value_error() {
|
|
3
|
+
throw new Error(
|
|
4
|
+
'Do not access tracked values with [0]. Use .value or &[] lazy destructuring instead. Numeric tracked access leads to degraded performance.',
|
|
5
|
+
);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** @returns {never} */
|
|
9
|
+
export function throw_tracked_index_reference_error() {
|
|
10
|
+
throw new Error(
|
|
11
|
+
'Do not access tracked values with [1]. Use the tracked value directly instead. Numeric tracked access leads to degraded performance.',
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -659,7 +659,7 @@ describe('async suspense', () => {
|
|
|
659
659
|
},
|
|
660
660
|
);
|
|
661
661
|
|
|
662
|
-
it('
|
|
662
|
+
it('uses the root pending boundary when trackAsync has no local try/pending boundary', () => {
|
|
663
663
|
component App() {
|
|
664
664
|
let &[value] = trackAsync(() => Promise.resolve('test'));
|
|
665
665
|
<div>{value}</div>
|
|
@@ -667,6 +667,6 @@ describe('async suspense', () => {
|
|
|
667
667
|
|
|
668
668
|
expect(() => {
|
|
669
669
|
render(App);
|
|
670
|
-
}).toThrow(
|
|
670
|
+
}).not.toThrow();
|
|
671
671
|
});
|
|
672
672
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { effect, flushSync,
|
|
1
|
+
import { effect, flushSync, track, untrack } from 'ripple';
|
|
2
2
|
|
|
3
|
-
describe('basic client >
|
|
3
|
+
describe('basic client > tracked value access', () => {
|
|
4
4
|
it('gets tracked value', () => {
|
|
5
5
|
component Test() {
|
|
6
6
|
let count = track(0);
|
|
7
7
|
|
|
8
|
-
<div>{
|
|
8
|
+
<div>{count.value}</div>
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
render(Test);
|
|
@@ -18,7 +18,7 @@ describe('basic client > get/set functions', () => {
|
|
|
18
18
|
component Test() {
|
|
19
19
|
let &[count] = track(0);
|
|
20
20
|
|
|
21
|
-
<p>{
|
|
21
|
+
<p>{count}</p>
|
|
22
22
|
<button onClick={() => count++}>{'increment'}</button>
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -38,7 +38,7 @@ describe('basic client > get/set functions', () => {
|
|
|
38
38
|
component Test() {
|
|
39
39
|
let &[count] = track(0);
|
|
40
40
|
|
|
41
|
-
<p>{
|
|
41
|
+
<p>{count}</p>
|
|
42
42
|
<button
|
|
43
43
|
onClick={() => {
|
|
44
44
|
count++;
|
|
@@ -66,8 +66,8 @@ describe('basic client > get/set functions', () => {
|
|
|
66
66
|
component Test() {
|
|
67
67
|
let count = track(0);
|
|
68
68
|
|
|
69
|
-
<p>{
|
|
70
|
-
<button onClick={() =>
|
|
69
|
+
<p>{count.value}</p>
|
|
70
|
+
<button onClick={() => (count.value = 10)}>{'set to 10'}</button>
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
render(Test);
|
|
@@ -86,12 +86,12 @@ describe('basic client > get/set functions', () => {
|
|
|
86
86
|
component Test() {
|
|
87
87
|
let count = track(0);
|
|
88
88
|
|
|
89
|
-
<p>{
|
|
89
|
+
<p>{count.value}</p>
|
|
90
90
|
<button
|
|
91
91
|
onClick={() => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
count.value = 5;
|
|
93
|
+
count.value = 15;
|
|
94
|
+
count.value = 25;
|
|
95
95
|
}}
|
|
96
96
|
>
|
|
97
97
|
{'set multiple times'}
|
|
@@ -114,8 +114,8 @@ describe('basic client > get/set functions', () => {
|
|
|
114
114
|
component Test() {
|
|
115
115
|
let count = track(0);
|
|
116
116
|
|
|
117
|
-
<p>{
|
|
118
|
-
<button onClick={() =>
|
|
117
|
+
<p>{count.value}</p>
|
|
118
|
+
<button onClick={() => (count.value = count.value + 10)}>{'add 10'}</button>
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
render(Test);
|
|
@@ -140,12 +140,12 @@ describe('basic client > get/set functions', () => {
|
|
|
140
140
|
component Test() {
|
|
141
141
|
let count = track(0);
|
|
142
142
|
|
|
143
|
-
<p>{
|
|
143
|
+
<p>{count.value}</p>
|
|
144
144
|
<button
|
|
145
145
|
onClick={() => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
count.value = count.value + 5;
|
|
147
|
+
count.value = count.value + 15;
|
|
148
|
+
count.value = count.value + 25;
|
|
149
149
|
}}
|
|
150
150
|
>
|
|
151
151
|
{'add multiple times'}
|
|
@@ -177,7 +177,7 @@ describe('basic client > get/set functions', () => {
|
|
|
177
177
|
|
|
178
178
|
component Test() {
|
|
179
179
|
let count = store();
|
|
180
|
-
<p>{
|
|
180
|
+
<p>{count.value}</p>
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
render(Test);
|
|
@@ -194,8 +194,8 @@ describe('basic client > get/set functions', () => {
|
|
|
194
194
|
component Test() {
|
|
195
195
|
let count = store();
|
|
196
196
|
|
|
197
|
-
<p>{
|
|
198
|
-
<button onClick={() =>
|
|
197
|
+
<p>{count.value}</p>
|
|
198
|
+
<button onClick={() => (count.value = 50)}>{'set to 50'}</button>
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
render(Test);
|
|
@@ -216,11 +216,11 @@ describe('basic client > get/set functions', () => {
|
|
|
216
216
|
let double = track(0);
|
|
217
217
|
|
|
218
218
|
effect(() => {
|
|
219
|
-
|
|
219
|
+
double.value = count.value * 2;
|
|
220
220
|
});
|
|
221
221
|
|
|
222
|
-
<p>{
|
|
223
|
-
<button onClick={() =>
|
|
222
|
+
<p>{double.value}</p>
|
|
223
|
+
<button onClick={() => (count.value = count.value + 1)}>{'increment'}</button>
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
render(Test);
|
|
@@ -247,12 +247,12 @@ describe('basic client > get/set functions', () => {
|
|
|
247
247
|
|
|
248
248
|
effect(() => {
|
|
249
249
|
untrack(() => {
|
|
250
|
-
|
|
250
|
+
double.value = count.value * 2;
|
|
251
251
|
});
|
|
252
252
|
});
|
|
253
253
|
|
|
254
|
-
<p>{
|
|
255
|
-
<button onClick={() =>
|
|
254
|
+
<p>{double.value}</p>
|
|
255
|
+
<button onClick={() => (count.value = count.value + 1)}>{'increment'}</button>
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
render(Test);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
-
exports[`compiler > assignments > compiles tracked values in effect with assignment expression 1`] = `"state.count =
|
|
3
|
+
exports[`compiler > assignments > compiles tracked values in effect with assignment expression 1`] = `"state.count = lazy.value;"`;
|
|
4
4
|
|
|
5
5
|
exports[`compiler > assignments > compiles tracked values in effect with update expressions 1`] = `
|
|
6
6
|
"_$_.untrack(() => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
-
exports[`compiler > assignments > compiles tracked values in effect with assignment expression 1`] = `"state.count =
|
|
3
|
+
exports[`compiler > assignments > compiles tracked values in effect with assignment expression 1`] = `"state.count = lazy.value;"`;
|
|
4
4
|
|
|
5
5
|
exports[`compiler > assignments > compiles tracked values in effect with update expressions 1`] = `
|
|
6
6
|
"_$_.untrack(() => {
|
|
@@ -139,4 +139,84 @@ component App() {
|
|
|
139
139
|
const effect_match = result.code.match(EFFECT_BODY_REGEX);
|
|
140
140
|
expect(effect_match?.[1].trim()).toMatchSnapshot();
|
|
141
141
|
});
|
|
142
|
+
|
|
143
|
+
it('compiles unknown lazy array destructuring through lazy array helpers', () => {
|
|
144
|
+
const source = `import { track } from 'ripple';
|
|
145
|
+
component Child({ tr: &[count, tr] }) {
|
|
146
|
+
count++;
|
|
147
|
+
tr[0]++;
|
|
148
|
+
<div>{count}</div>
|
|
149
|
+
}
|
|
150
|
+
component App() {
|
|
151
|
+
let tracked = track(0);
|
|
152
|
+
<Child tr={tracked} />
|
|
153
|
+
}`;
|
|
154
|
+
const { code } = compile(source, 'test.tsrx', { mode: 'client' });
|
|
155
|
+
|
|
156
|
+
expect(code).toContain('_$_.lazy_array_get(lazy, 0)');
|
|
157
|
+
expect(code).toContain('_$_.lazy_array_update(lazy, 0)');
|
|
158
|
+
expect(code).toContain('_$_.lazy_array_get(lazy, 1)');
|
|
159
|
+
expect(code).toContain('_$_.lazy_array_update(_$_.lazy_array_get(lazy, 1), 0)');
|
|
160
|
+
expect(code).not.toContain('lazy[0]');
|
|
161
|
+
expect(code).not.toContain('lazy[1][0]');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('compiles writes to unknown lazy array index 1 through lazy array helpers', () => {
|
|
165
|
+
const source = `component Child({ pair: &[first, second] }) {
|
|
166
|
+
second = 10;
|
|
167
|
+
<div>{first}</div>
|
|
168
|
+
}
|
|
169
|
+
component App() {
|
|
170
|
+
<Child pair={[0, 1]} />
|
|
171
|
+
}`;
|
|
172
|
+
const { code } = compile(source, 'test.tsrx', { mode: 'client' });
|
|
173
|
+
|
|
174
|
+
expect(code).toContain('_$_.lazy_array_set(lazy, 10, 1)');
|
|
175
|
+
expect(code).not.toContain('lazy[1] =');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('does not double-wrap member access on lazy array value bindings', () => {
|
|
179
|
+
const source = `component Child({ pair: &[first] }) {
|
|
180
|
+
let value = first[0];
|
|
181
|
+
<div>{value}</div>
|
|
182
|
+
}
|
|
183
|
+
component App() {
|
|
184
|
+
<Child pair={[{ 0: 'x' }]} />
|
|
185
|
+
}`;
|
|
186
|
+
const { code } = compile(source, 'test.tsrx', { mode: 'client' });
|
|
187
|
+
|
|
188
|
+
expect(code).toContain('let value = _$_.lazy_array_get(lazy, 0)[0];');
|
|
189
|
+
expect(code).not.toContain('_$_.lazy_array_get(_$_.lazy_array_get(lazy, 0), 0)');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('throws on indexed access through known tracked lazy destructures', () => {
|
|
193
|
+
const source = `import { track } from 'ripple';
|
|
194
|
+
component App() {
|
|
195
|
+
let &[value, tracked_ref] = track({ 0: 'x' });
|
|
196
|
+
let nested = value[0];
|
|
197
|
+
tracked_ref[0] = { 0: 'y' };
|
|
198
|
+
let next = value[0];
|
|
199
|
+
<div>{nested}{next}</div>
|
|
200
|
+
}`;
|
|
201
|
+
|
|
202
|
+
expect(() => compile(source, 'test.tsrx', { mode: 'client' })).toThrow(
|
|
203
|
+
/Use \.value or &\[\] lazy destructuring/,
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('throws on known tracked indexed access', () => {
|
|
208
|
+
const source = `import { track } from 'ripple';
|
|
209
|
+
component App() {
|
|
210
|
+
let tracked = track(0);
|
|
211
|
+
tracked[0]++;
|
|
212
|
+
tracked[0] = tracked[0] + 1;
|
|
213
|
+
let value = tracked[0];
|
|
214
|
+
let ref = tracked[1];
|
|
215
|
+
<div>{value}</div>
|
|
216
|
+
}`;
|
|
217
|
+
|
|
218
|
+
expect(() => compile(source, 'test.tsrx', { mode: 'client' })).toThrow(
|
|
219
|
+
/Use \.value or &\[\] lazy destructuring/,
|
|
220
|
+
);
|
|
221
|
+
});
|
|
142
222
|
});
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { compile } from '@tsrx/ripple';
|
|
2
2
|
|
|
3
|
+
const value_message = /Use \.value or &\[\] lazy destructuring/;
|
|
4
|
+
const reference_message = /Use the tracked value directly instead/;
|
|
5
|
+
|
|
3
6
|
describe('Compiler: Tracked Object Direct Access Checks', () => {
|
|
4
7
|
it('should error on direct access to __v of a tracked object', () => {
|
|
5
8
|
const code = `
|
|
@@ -77,12 +80,12 @@ import { track } from 'ripple';
|
|
|
77
80
|
expect(() => compile(code, 'test.tsrx')).not.toThrow();
|
|
78
81
|
});
|
|
79
82
|
|
|
80
|
-
it('should compile successfully with correct
|
|
83
|
+
it('should compile successfully with correct value access', () => {
|
|
81
84
|
const code = `
|
|
82
|
-
import {
|
|
85
|
+
import { track } from 'ripple';
|
|
83
86
|
export default component App() {
|
|
84
87
|
let count = track(0);
|
|
85
|
-
console.log(
|
|
88
|
+
console.log(count.value);
|
|
86
89
|
}
|
|
87
90
|
`;
|
|
88
91
|
expect(() => compile(code, 'test.tsrx')).not.toThrow();
|
|
@@ -122,7 +125,7 @@ import { get, track } from 'ripple';
|
|
|
122
125
|
},
|
|
123
126
|
);
|
|
124
127
|
|
|
125
|
-
it('should
|
|
128
|
+
it('should error on indexed [0] access on a tracked object', () => {
|
|
126
129
|
const code = `
|
|
127
130
|
import { track } from 'ripple';
|
|
128
131
|
export default component App() {
|
|
@@ -130,10 +133,10 @@ import { track } from 'ripple';
|
|
|
130
133
|
console.log(count[0]);
|
|
131
134
|
}
|
|
132
135
|
`;
|
|
133
|
-
expect(() => compile(code, 'test.tsrx')).
|
|
136
|
+
expect(() => compile(code, 'test.tsrx')).toThrow(value_message);
|
|
134
137
|
});
|
|
135
138
|
|
|
136
|
-
it('should
|
|
139
|
+
it('should error on indexed [1] access on a tracked object', () => {
|
|
137
140
|
const code = `
|
|
138
141
|
import { track } from 'ripple';
|
|
139
142
|
export default component App() {
|
|
@@ -141,16 +144,57 @@ import { track } from 'ripple';
|
|
|
141
144
|
let raw = count[1];
|
|
142
145
|
}
|
|
143
146
|
`;
|
|
144
|
-
expect(() => compile(code, 'test.tsrx')).
|
|
147
|
+
expect(() => compile(code, 'test.tsrx')).toThrow(reference_message);
|
|
145
148
|
});
|
|
146
149
|
|
|
147
|
-
it('should
|
|
150
|
+
it('should error on indexed [0] write on a tracked object', () => {
|
|
148
151
|
const code = `
|
|
149
152
|
import { track } from 'ripple';
|
|
150
153
|
export default component App() {
|
|
151
154
|
let count = track(0);
|
|
152
155
|
count[0] = 5;
|
|
153
156
|
}
|
|
157
|
+
`;
|
|
158
|
+
expect(() => compile(code, 'test.tsrx')).toThrow(value_message);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('collects tracked numeric index errors in loose mode', () => {
|
|
162
|
+
const code = `
|
|
163
|
+
import { track } from 'ripple';
|
|
164
|
+
export default component App() {
|
|
165
|
+
let count = track(0);
|
|
166
|
+
console.log(count[0]);
|
|
167
|
+
let raw = count[1];
|
|
168
|
+
}
|
|
169
|
+
`;
|
|
170
|
+
const result = compile(code, 'test.tsrx', { loose: true });
|
|
171
|
+
|
|
172
|
+
expect(result.errors.map((error) => error.message)).toEqual(
|
|
173
|
+
expect.arrayContaining([
|
|
174
|
+
expect.stringMatching(value_message),
|
|
175
|
+
expect.stringMatching(reference_message),
|
|
176
|
+
]),
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should error on indexed access through a known tracked lazy ref binding', () => {
|
|
181
|
+
const code = `
|
|
182
|
+
import { track } from 'ripple';
|
|
183
|
+
export default component App() {
|
|
184
|
+
let &[value, tracked_ref] = track(0);
|
|
185
|
+
tracked_ref[0]++;
|
|
186
|
+
}
|
|
187
|
+
`;
|
|
188
|
+
expect(() => compile(code, 'test.tsrx')).toThrow(value_message);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should allow lazy destructuring a tracked value', () => {
|
|
192
|
+
const code = `
|
|
193
|
+
import { track } from 'ripple';
|
|
194
|
+
export default component App() {
|
|
195
|
+
let &[value, tracked_ref] = track(0);
|
|
196
|
+
console.log(value, tracked_ref.value);
|
|
197
|
+
}
|
|
154
198
|
`;
|
|
155
199
|
expect(() => compile(code, 'test.tsrx')).not.toThrow();
|
|
156
200
|
});
|