ripple 0.2.29 → 0.2.31
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/package.json +1 -1
- package/src/compiler/phases/2-analyze/index.js +16 -9
- package/src/compiler/phases/3-transform/index.js +26 -15
- package/src/compiler/utils.js +42 -1
- package/src/runtime/array.js +18 -40
- package/src/runtime/internal/client/index.js +1 -0
- package/src/runtime/internal/client/runtime.js +114 -31
- package/src/runtime/map.js +5 -5
- package/src/runtime/set.js +6 -6
- package/tests/accessors-props.test.ripple +150 -0
- package/tests/basic.test.ripple +0 -1
- package/tests/boundaries.ripple +0 -1
- package/tests/compiler.test.ripple +77 -3
- package/tests/composite.test.ripple +0 -1
- package/tests/context.test.ripple +0 -1
- package/tests/decorators.ripple +0 -1
- package/tests/map.test.ripple +9 -2
package/package.json
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
is_event_attribute,
|
|
7
7
|
is_inside_component,
|
|
8
8
|
is_ripple_import,
|
|
9
|
+
is_tracked_computed_property,
|
|
9
10
|
is_tracked_name,
|
|
10
11
|
} from '../../utils.js';
|
|
11
12
|
import { extract_paths } from '../../../utils/ast.js';
|
|
@@ -101,15 +102,21 @@ const visitors = {
|
|
|
101
102
|
MemberExpression(node, context) {
|
|
102
103
|
const parent = context.path.at(-1);
|
|
103
104
|
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
if (context.state.metadata?.tracking === false && parent.type !== 'AssignmentExpression') {
|
|
106
|
+
if (
|
|
107
|
+
node.property.type === 'Identifier' &&
|
|
108
|
+
!node.computed &&
|
|
109
|
+
is_tracked_name(node.property.name)
|
|
110
|
+
) {
|
|
111
|
+
context.state.metadata.tracking = true;
|
|
112
|
+
} else if (
|
|
113
|
+
node.computed &&
|
|
114
|
+
is_tracked_computed_property(node.object, node.property, context)
|
|
115
|
+
) {
|
|
116
|
+
context.state.metadata.tracking = true;
|
|
117
|
+
}
|
|
112
118
|
}
|
|
119
|
+
|
|
113
120
|
context.next();
|
|
114
121
|
},
|
|
115
122
|
|
|
@@ -212,7 +219,7 @@ const visitors = {
|
|
|
212
219
|
if (!computed) {
|
|
213
220
|
return node;
|
|
214
221
|
}
|
|
215
|
-
return b.call('$.set_property', node,
|
|
222
|
+
return b.call('$.set_property', node, computed, value, b.id('__block'));
|
|
216
223
|
},
|
|
217
224
|
};
|
|
218
225
|
break;
|
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
is_ripple_import,
|
|
20
20
|
is_declared_within_component,
|
|
21
21
|
is_inside_call_expression,
|
|
22
|
+
is_tracked_computed_property,
|
|
23
|
+
is_value_static,
|
|
22
24
|
} from '../../utils.js';
|
|
23
25
|
import is_reference from 'is-reference';
|
|
24
26
|
import { extract_paths, object } from '../../../utils/ast.js';
|
|
@@ -169,6 +171,10 @@ const visitors = {
|
|
|
169
171
|
return context.next();
|
|
170
172
|
}
|
|
171
173
|
|
|
174
|
+
if (is_value_static(node)) {
|
|
175
|
+
return context.next();
|
|
176
|
+
}
|
|
177
|
+
|
|
172
178
|
return b.call(
|
|
173
179
|
'$.with_scope',
|
|
174
180
|
b.id('__block'),
|
|
@@ -192,7 +198,10 @@ const visitors = {
|
|
|
192
198
|
: property.type === 'Literal' && is_tracked_name(property.value);
|
|
193
199
|
|
|
194
200
|
// TODO should we enforce that the identifier is tracked too?
|
|
195
|
-
if (
|
|
201
|
+
if (
|
|
202
|
+
(node.computed && is_tracked_computed_property(node.object, node.property, context)) ||
|
|
203
|
+
tracked_name
|
|
204
|
+
) {
|
|
196
205
|
if (context.state.metadata?.tracking === false) {
|
|
197
206
|
context.state.metadata.tracking = true;
|
|
198
207
|
}
|
|
@@ -861,19 +870,20 @@ const visitors = {
|
|
|
861
870
|
|
|
862
871
|
const left = node.left;
|
|
863
872
|
|
|
864
|
-
if (
|
|
865
|
-
left.type === '
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
873
|
+
if (left.type === 'MemberExpression') {
|
|
874
|
+
if (left.property.type === 'Identifier' && is_tracked_name(left.property.name)) {
|
|
875
|
+
if (left.property.name !== '$length') {
|
|
876
|
+
return b.call(
|
|
877
|
+
'$.set_property',
|
|
878
|
+
context.visit(left.object),
|
|
879
|
+
left.computed ? context.visit(left.property) : b.literal(left.property.name),
|
|
880
|
+
visit_assignment_expression(node, context, build_assignment) ?? context.next(),
|
|
881
|
+
b.id('__block'),
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
} else if (!is_tracked_computed_property(left.object, left.property, context)) {
|
|
885
|
+
return context.next();
|
|
886
|
+
}
|
|
877
887
|
}
|
|
878
888
|
|
|
879
889
|
const visited = visit_assignment_expression(node, context, build_assignment) ?? context.next();
|
|
@@ -900,7 +910,8 @@ const visitors = {
|
|
|
900
910
|
if (
|
|
901
911
|
argument.type === 'MemberExpression' &&
|
|
902
912
|
((argument.property.type === 'Identifier' && is_tracked_name(argument.property.name)) ||
|
|
903
|
-
argument.computed
|
|
913
|
+
(argument.computed &&
|
|
914
|
+
is_tracked_computed_property(argument.object, argument.property, context)))
|
|
904
915
|
) {
|
|
905
916
|
return b.call(
|
|
906
917
|
node.prefix ? '$.update_pre_property' : '$.update_property',
|
package/src/compiler/utils.js
CHANGED
|
@@ -370,6 +370,47 @@ export function is_tracked_name(name) {
|
|
|
370
370
|
return typeof name === 'string' && name.startsWith('$') && name.length > 1 && name[1] !== '$';
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
+
export function is_value_static(node) {
|
|
374
|
+
if (node.type === 'Literal') {
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
if (node.type === 'ArrayExpression') {
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
if (node.type === 'NewExpression') {
|
|
381
|
+
if (node.callee.type === 'Identifier' && node.callee.name === 'Array') {
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export function is_tracked_computed_property(object, property, context) {
|
|
391
|
+
const binding = context.state.scope.get(object.name);
|
|
392
|
+
|
|
393
|
+
if (binding) {
|
|
394
|
+
const initial = binding.initial;
|
|
395
|
+
if (initial && is_value_static(initial)) {
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (property.type === 'Identifier') {
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
if (
|
|
403
|
+
property.type === 'Literal' &&
|
|
404
|
+
typeof property.value === 'string' &&
|
|
405
|
+
is_tracked_name(property.value)
|
|
406
|
+
) {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// TODO: do we need to handle more logic here? default to false for now
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
|
|
373
414
|
export function is_ripple_import(callee, context) {
|
|
374
415
|
if (callee.type === 'Identifier') {
|
|
375
416
|
const binding = context.state.scope.get(callee.name);
|
|
@@ -507,7 +548,7 @@ export function build_assignment(operator, left, right, context) {
|
|
|
507
548
|
return transform.assign(
|
|
508
549
|
object,
|
|
509
550
|
value,
|
|
510
|
-
left.type === 'MemberExpression' && left.computed ? left.property : undefined,
|
|
551
|
+
left.type === 'MemberExpression' && left.computed ? context.visit(left.property) : undefined,
|
|
511
552
|
);
|
|
512
553
|
}
|
|
513
554
|
|
package/src/runtime/array.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** @import { Block } from '#client' */
|
|
2
2
|
|
|
3
3
|
import { TRACKED_OBJECT } from './internal/client/constants.js';
|
|
4
|
-
import { get, increment,
|
|
4
|
+
import { get, increment, safe_scope, set, tracked } from './internal/client/runtime.js';
|
|
5
5
|
|
|
6
6
|
var symbol_iterator = Symbol.iterator;
|
|
7
7
|
|
|
@@ -36,17 +36,6 @@ const introspect_methods = [
|
|
|
36
36
|
];
|
|
37
37
|
|
|
38
38
|
let init = false;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* @param {Block | null} block
|
|
42
|
-
* @throws {Error}
|
|
43
|
-
*/
|
|
44
|
-
function check_block(block) {
|
|
45
|
-
if (block === null) {
|
|
46
|
-
throw new Error('Cannot set $length outside of a reactive context');
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
39
|
export class RippleArray extends Array {
|
|
51
40
|
#tracked_elements = [];
|
|
52
41
|
#tracked_index;
|
|
@@ -62,12 +51,11 @@ export class RippleArray extends Array {
|
|
|
62
51
|
constructor(...elements) {
|
|
63
52
|
super(...elements);
|
|
64
53
|
|
|
65
|
-
var block =
|
|
66
|
-
check_block(block);
|
|
54
|
+
var block = safe_scope();
|
|
67
55
|
var tracked_elements = this.#tracked_elements;
|
|
68
56
|
|
|
69
57
|
for (var i = 0; i < this.length; i++) {
|
|
70
|
-
tracked_elements[i] = tracked(
|
|
58
|
+
tracked_elements[i] = tracked(elements[i], block);
|
|
71
59
|
}
|
|
72
60
|
this.#tracked_index = tracked(this.length, block);
|
|
73
61
|
|
|
@@ -90,20 +78,18 @@ export class RippleArray extends Array {
|
|
|
90
78
|
}
|
|
91
79
|
}
|
|
92
80
|
|
|
93
|
-
fill() {
|
|
94
|
-
var block =
|
|
95
|
-
check_block(block);
|
|
81
|
+
fill(val, start, end) {
|
|
82
|
+
var block = safe_scope();
|
|
96
83
|
var tracked_elements = this.#tracked_elements;
|
|
97
84
|
|
|
98
|
-
super.fill();
|
|
85
|
+
super.fill(val, start, end);
|
|
99
86
|
for (var i = 0; i < this.length; i++) {
|
|
100
87
|
increment(tracked_elements[i], block);
|
|
101
88
|
}
|
|
102
89
|
}
|
|
103
90
|
|
|
104
91
|
reverse() {
|
|
105
|
-
var block =
|
|
106
|
-
check_block(block);
|
|
92
|
+
var block = safe_scope();
|
|
107
93
|
var tracked_elements = this.#tracked_elements;
|
|
108
94
|
|
|
109
95
|
super.reverse();
|
|
@@ -113,8 +99,7 @@ export class RippleArray extends Array {
|
|
|
113
99
|
}
|
|
114
100
|
|
|
115
101
|
sort(fn) {
|
|
116
|
-
var block =
|
|
117
|
-
check_block(block);
|
|
102
|
+
var block = safe_scope();
|
|
118
103
|
var tracked_elements = this.#tracked_elements;
|
|
119
104
|
|
|
120
105
|
super.sort(fn);
|
|
@@ -128,8 +113,7 @@ export class RippleArray extends Array {
|
|
|
128
113
|
* @returns {number}
|
|
129
114
|
*/
|
|
130
115
|
unshift(...elements) {
|
|
131
|
-
var block =
|
|
132
|
-
check_block(block);
|
|
116
|
+
var block = safe_scope();
|
|
133
117
|
var tracked_elements = this.#tracked_elements;
|
|
134
118
|
|
|
135
119
|
super.unshift(...elements);
|
|
@@ -144,15 +128,14 @@ export class RippleArray extends Array {
|
|
|
144
128
|
}
|
|
145
129
|
|
|
146
130
|
shift() {
|
|
147
|
-
var block =
|
|
148
|
-
check_block(block);
|
|
131
|
+
var block = safe_scope();
|
|
149
132
|
var tracked_elements = this.#tracked_elements;
|
|
150
133
|
|
|
151
134
|
super.shift();
|
|
152
|
-
for (var i = 0; i < tracked_elements.length; i++) {
|
|
153
|
-
|
|
135
|
+
for (var i = 0; i < tracked_elements.length - 1; i++) {
|
|
136
|
+
set(tracked_elements[i], tracked_elements[i + 1].v, block);
|
|
154
137
|
}
|
|
155
|
-
tracked_elements.
|
|
138
|
+
tracked_elements.pop();
|
|
156
139
|
|
|
157
140
|
set(this.#tracked_index, this.length, block);
|
|
158
141
|
}
|
|
@@ -162,8 +145,7 @@ export class RippleArray extends Array {
|
|
|
162
145
|
* @returns {number}
|
|
163
146
|
*/
|
|
164
147
|
push(...elements) {
|
|
165
|
-
var block =
|
|
166
|
-
check_block(block);
|
|
148
|
+
var block = safe_scope();
|
|
167
149
|
var start_index = this.length;
|
|
168
150
|
var tracked_elements = this.#tracked_elements;
|
|
169
151
|
|
|
@@ -178,8 +160,7 @@ export class RippleArray extends Array {
|
|
|
178
160
|
}
|
|
179
161
|
|
|
180
162
|
pop() {
|
|
181
|
-
var block =
|
|
182
|
-
check_block(block);
|
|
163
|
+
var block = safe_scope();
|
|
183
164
|
var tracked_elements = this.#tracked_elements;
|
|
184
165
|
|
|
185
166
|
super.pop();
|
|
@@ -198,8 +179,7 @@ export class RippleArray extends Array {
|
|
|
198
179
|
* @returns {any[]}
|
|
199
180
|
*/
|
|
200
181
|
splice(start, delete_count, ...elements) {
|
|
201
|
-
var block =
|
|
202
|
-
check_block(block);
|
|
182
|
+
var block = safe_scope();
|
|
203
183
|
var tracked_elements = this.#tracked_elements;
|
|
204
184
|
|
|
205
185
|
super.splice(start, delete_count, ...elements);
|
|
@@ -220,11 +200,10 @@ export class RippleArray extends Array {
|
|
|
220
200
|
}
|
|
221
201
|
|
|
222
202
|
set $length(length) {
|
|
223
|
-
var block =
|
|
224
|
-
check_block(block);
|
|
203
|
+
var block = safe_scope();
|
|
225
204
|
var tracked_elements = this.#tracked_elements;
|
|
226
205
|
|
|
227
|
-
if (length !== this
|
|
206
|
+
if (length !== this.length) {
|
|
228
207
|
for (var i = 0; i < tracked_elements.length; i++) {
|
|
229
208
|
increment(tracked_elements[i], block);
|
|
230
209
|
}
|
|
@@ -260,4 +239,3 @@ export function get_all_elements(array) {
|
|
|
260
239
|
|
|
261
240
|
return arr;
|
|
262
241
|
}
|
|
263
|
-
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** @import { Block, Component, Dependency, Computed, Tracked } from '#client' */
|
|
2
|
+
/** @import { RippleArray } from 'ripple' */
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
destroy_block,
|
|
@@ -777,9 +778,8 @@ export function flush_sync(fn) {
|
|
|
777
778
|
}
|
|
778
779
|
|
|
779
780
|
/**
|
|
780
|
-
* @
|
|
781
|
-
* @
|
|
782
|
-
* @returns {T & { [SPREAD_OBJECT]: () => T }}
|
|
781
|
+
* @param {() => Object} fn
|
|
782
|
+
* @returns {Object}
|
|
783
783
|
*/
|
|
784
784
|
export function tracked_spread_object(fn) {
|
|
785
785
|
var obj = fn();
|
|
@@ -815,8 +815,7 @@ export function tracked_object(obj, properties, block) {
|
|
|
815
815
|
/** @type {Tracked | Computed} */
|
|
816
816
|
var tracked_property;
|
|
817
817
|
|
|
818
|
-
// accessor passed in, to avoid an expensive get_descriptor call
|
|
819
|
-
// in the fast path
|
|
818
|
+
// accessor passed in, to avoid an expensive get_descriptor call in the fast path
|
|
820
819
|
if (property[0] === '#') {
|
|
821
820
|
property = property.slice(1);
|
|
822
821
|
var descriptor = /** @type {PropertyDescriptor} */ (get_descriptor(obj, property));
|
|
@@ -824,7 +823,10 @@ export function tracked_object(obj, properties, block) {
|
|
|
824
823
|
tracked_property = computed(desc_get, block);
|
|
825
824
|
/** @type {any} */
|
|
826
825
|
var initial = run_computed(/** @type {Computed} */ (tracked_property));
|
|
827
|
-
|
|
826
|
+
// If there's a setter, we need to set the initial value
|
|
827
|
+
if (descriptor.set !== undefined) {
|
|
828
|
+
obj[property] = initial;
|
|
829
|
+
}
|
|
828
830
|
} else {
|
|
829
831
|
var initial = obj[property];
|
|
830
832
|
|
|
@@ -856,6 +858,12 @@ export function computed_property(fn) {
|
|
|
856
858
|
return fn;
|
|
857
859
|
}
|
|
858
860
|
|
|
861
|
+
/**
|
|
862
|
+
* @param {any} obj
|
|
863
|
+
* @param {string | number | symbol} property
|
|
864
|
+
* @param {boolean} [chain=false]
|
|
865
|
+
* @returns {any}
|
|
866
|
+
*/
|
|
859
867
|
export function get_property(obj, property, chain = false) {
|
|
860
868
|
if (chain && obj == null) {
|
|
861
869
|
return undefined;
|
|
@@ -865,7 +873,10 @@ export function get_property(obj, property, chain = false) {
|
|
|
865
873
|
var tracked_property = tracked_properties?.[property];
|
|
866
874
|
|
|
867
875
|
if (tracked_property !== undefined) {
|
|
868
|
-
value =
|
|
876
|
+
value = get(tracked_property);
|
|
877
|
+
if (obj[property] !== value) {
|
|
878
|
+
obj[property] = value;
|
|
879
|
+
}
|
|
869
880
|
} else if (SPREAD_OBJECT in obj) {
|
|
870
881
|
var spread_fn = obj[SPREAD_OBJECT];
|
|
871
882
|
var properties = spread_fn();
|
|
@@ -875,6 +886,13 @@ export function get_property(obj, property, chain = false) {
|
|
|
875
886
|
return value;
|
|
876
887
|
}
|
|
877
888
|
|
|
889
|
+
/**
|
|
890
|
+
* @param {any} obj
|
|
891
|
+
* @param {string | number | symbol} property
|
|
892
|
+
* @param {any} value
|
|
893
|
+
* @param {Block} block
|
|
894
|
+
* @returns {any}
|
|
895
|
+
*/
|
|
878
896
|
export function set_property(obj, property, value, block) {
|
|
879
897
|
var res = (obj[property] = value);
|
|
880
898
|
var tracked_properties = obj[TRACKED_OBJECT];
|
|
@@ -882,9 +900,9 @@ export function set_property(obj, property, value, block) {
|
|
|
882
900
|
|
|
883
901
|
if (tracked === undefined) {
|
|
884
902
|
// Handle computed assignments to arrays
|
|
885
|
-
if (obj.$length && tracked_properties !== undefined
|
|
903
|
+
if (is_array(obj) && obj.$length && tracked_properties !== undefined) {
|
|
886
904
|
with_scope(block, () => {
|
|
887
|
-
obj.splice(property, 1, value);
|
|
905
|
+
obj.splice(/** @type {number} */ (property), 1, value);
|
|
888
906
|
});
|
|
889
907
|
}
|
|
890
908
|
return res;
|
|
@@ -893,6 +911,12 @@ export function set_property(obj, property, value, block) {
|
|
|
893
911
|
set(tracked, value, block);
|
|
894
912
|
}
|
|
895
913
|
|
|
914
|
+
/**
|
|
915
|
+
* @param {Tracked} tracked
|
|
916
|
+
* @param {Block} block
|
|
917
|
+
* @param {number} [d]
|
|
918
|
+
* @returns {number}
|
|
919
|
+
*/
|
|
896
920
|
export function update(tracked, block, d = 1) {
|
|
897
921
|
var value = get(tracked);
|
|
898
922
|
var result = d === 1 ? value++ : value--;
|
|
@@ -902,66 +926,110 @@ export function update(tracked, block, d = 1) {
|
|
|
902
926
|
return result;
|
|
903
927
|
}
|
|
904
928
|
|
|
929
|
+
/**
|
|
930
|
+
* @param {Tracked} tracked
|
|
931
|
+
* @param {Block} block
|
|
932
|
+
* @returns {void}
|
|
933
|
+
*/
|
|
905
934
|
export function increment(tracked, block) {
|
|
906
935
|
set(tracked, tracked.v + 1, block);
|
|
907
936
|
}
|
|
908
937
|
|
|
938
|
+
/**
|
|
939
|
+
* @param {Tracked} tracked
|
|
940
|
+
* @param {Block} block
|
|
941
|
+
* @returns {void}
|
|
942
|
+
*/
|
|
909
943
|
export function decrement(tracked, block) {
|
|
910
944
|
set(tracked, tracked.v - 1, block);
|
|
911
945
|
}
|
|
912
946
|
|
|
947
|
+
/**
|
|
948
|
+
* @param {Tracked} tracked
|
|
949
|
+
* @param {Block} block
|
|
950
|
+
* @param {number} [d]
|
|
951
|
+
* @returns {number}
|
|
952
|
+
*/
|
|
913
953
|
export function update_pre(tracked, block, d = 1) {
|
|
914
954
|
var value = get(tracked);
|
|
955
|
+
var new_value = d === 1 ? ++value : --value;
|
|
915
956
|
|
|
916
|
-
|
|
957
|
+
set(tracked, new_value, block);
|
|
958
|
+
|
|
959
|
+
return new_value;
|
|
917
960
|
}
|
|
918
961
|
|
|
962
|
+
/**
|
|
963
|
+
* @param {any} obj
|
|
964
|
+
* @param {string | number | symbol} property
|
|
965
|
+
* @param {Block} block
|
|
966
|
+
* @param {number} [d]
|
|
967
|
+
* @returns {number}
|
|
968
|
+
*/
|
|
919
969
|
export function update_property(obj, property, block, d = 1) {
|
|
920
970
|
var tracked_properties = obj[TRACKED_OBJECT];
|
|
921
971
|
var tracked = tracked_properties?.[property];
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
return d === 1 ? obj[property]++ : obj[property]--;
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
var value = get(tracked);
|
|
928
|
-
var result = d === 1 ? value++ : value--;
|
|
972
|
+
var tracked_exists = tracked !== undefined;
|
|
973
|
+
var value = tracked_exists ? get(tracked) : obj[property];
|
|
929
974
|
|
|
930
975
|
if (d === 1) {
|
|
931
|
-
|
|
976
|
+
value++;
|
|
977
|
+
if (tracked_exists) {
|
|
978
|
+
increment(tracked, block);
|
|
979
|
+
}
|
|
932
980
|
} else {
|
|
933
|
-
|
|
981
|
+
value--;
|
|
982
|
+
if (tracked_exists) {
|
|
983
|
+
decrement(tracked, block);
|
|
984
|
+
}
|
|
934
985
|
}
|
|
935
986
|
|
|
936
|
-
|
|
987
|
+
obj[property] = value
|
|
988
|
+
|
|
989
|
+
return value;
|
|
937
990
|
}
|
|
938
991
|
|
|
992
|
+
/**
|
|
993
|
+
* @param {any} obj
|
|
994
|
+
* @param {string | number | symbol} property
|
|
995
|
+
* @param {Block} block
|
|
996
|
+
* @param {number} [d]
|
|
997
|
+
* @returns {number}
|
|
998
|
+
*/
|
|
939
999
|
export function update_pre_property(obj, property, block, d = 1) {
|
|
940
1000
|
var tracked_properties = obj[TRACKED_OBJECT];
|
|
941
1001
|
var tracked = tracked_properties?.[property];
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
return d === 1 ? ++obj[property] : --obj[property];
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
var value = get(tracked);
|
|
948
|
-
var result = d === 1 ? ++value : --value;
|
|
1002
|
+
var tracked_exists = tracked !== undefined;
|
|
1003
|
+
var value = tracked_exists ? get(tracked) : obj[property];
|
|
949
1004
|
|
|
950
1005
|
if (d === 1) {
|
|
951
|
-
|
|
1006
|
+
++value
|
|
1007
|
+
if (tracked_exists) {
|
|
1008
|
+
increment(tracked, block);
|
|
1009
|
+
}
|
|
952
1010
|
} else {
|
|
953
|
-
|
|
1011
|
+
--value
|
|
1012
|
+
if (tracked_exists) {
|
|
1013
|
+
decrement(tracked, block);
|
|
1014
|
+
}
|
|
954
1015
|
}
|
|
955
1016
|
|
|
956
|
-
|
|
1017
|
+
obj[property] = value;
|
|
1018
|
+
|
|
1019
|
+
return value;
|
|
957
1020
|
}
|
|
958
1021
|
|
|
1022
|
+
/**
|
|
1023
|
+
* @param {any} val
|
|
1024
|
+
* @param {StructuredSerializeOptions} [options]
|
|
1025
|
+
* @returns {any}
|
|
1026
|
+
*/
|
|
959
1027
|
export function structured_clone(val, options) {
|
|
960
1028
|
if (typeof val === 'object' && val !== null) {
|
|
961
1029
|
var tracked_properties = val[TRACKED_OBJECT];
|
|
962
1030
|
if (tracked_properties !== undefined) {
|
|
963
1031
|
if (is_array(val)) {
|
|
964
|
-
val.$length;
|
|
1032
|
+
/** @type {RippleArray<any>} */ (val).$length;
|
|
965
1033
|
}
|
|
966
1034
|
return structured_clone(object_values(val), options);
|
|
967
1035
|
}
|
|
@@ -1048,10 +1116,25 @@ export function with_scope(block, fn) {
|
|
|
1048
1116
|
}
|
|
1049
1117
|
}
|
|
1050
1118
|
|
|
1119
|
+
/**
|
|
1120
|
+
* @returns {Block | null}
|
|
1121
|
+
*/
|
|
1051
1122
|
export function scope() {
|
|
1052
1123
|
return active_scope;
|
|
1053
1124
|
}
|
|
1054
1125
|
|
|
1126
|
+
/**
|
|
1127
|
+
* @param {string} [err]
|
|
1128
|
+
* @returns {Block | never}
|
|
1129
|
+
*/
|
|
1130
|
+
export function safe_scope(err = 'Cannot access outside of a component context') {
|
|
1131
|
+
if (active_scope === null) {
|
|
1132
|
+
throw new Error(err);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
return /** @type {Block} */ (active_scope);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1055
1138
|
export function push_component() {
|
|
1056
1139
|
var component = {
|
|
1057
1140
|
c: null,
|
package/src/runtime/map.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { get, increment,
|
|
1
|
+
import { get, increment, safe_scope, set, tracked } from './internal/client/runtime.js';
|
|
2
2
|
|
|
3
3
|
const introspect_methods = ['entries', 'forEach', 'values', Symbol.iterator];
|
|
4
4
|
|
|
@@ -11,7 +11,7 @@ export class RippleMap extends Map {
|
|
|
11
11
|
constructor(iterable) {
|
|
12
12
|
super();
|
|
13
13
|
|
|
14
|
-
var block =
|
|
14
|
+
var block = safe_scope();
|
|
15
15
|
|
|
16
16
|
if (iterable) {
|
|
17
17
|
for (var [key, value] of iterable) {
|
|
@@ -75,7 +75,7 @@ export class RippleMap extends Map {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
set(key, value) {
|
|
78
|
-
var block =
|
|
78
|
+
var block = safe_scope();
|
|
79
79
|
var tracked_items = this.#tracked_items;
|
|
80
80
|
var t = tracked_items.get(key);
|
|
81
81
|
var prev_res = super.get(key);
|
|
@@ -93,7 +93,7 @@ export class RippleMap extends Map {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
delete(key) {
|
|
96
|
-
var block =
|
|
96
|
+
var block = safe_scope();
|
|
97
97
|
var tracked_items = this.#tracked_items;
|
|
98
98
|
var t = tracked_items.get(key);
|
|
99
99
|
var result = super.delete(key);
|
|
@@ -108,7 +108,7 @@ export class RippleMap extends Map {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
clear() {
|
|
111
|
-
var block =
|
|
111
|
+
var block = safe_scope();
|
|
112
112
|
|
|
113
113
|
if (super.size === 0) {
|
|
114
114
|
return;
|
package/src/runtime/set.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { get, increment,
|
|
1
|
+
import { get, increment, safe_scope, set, tracked } from './internal/client/runtime.js';
|
|
2
2
|
|
|
3
3
|
const introspect_methods = ['entries', 'forEach', 'keys', 'values', Symbol.iterator];
|
|
4
4
|
|
|
@@ -15,7 +15,7 @@ export class RippleSet extends Set {
|
|
|
15
15
|
constructor(iterable) {
|
|
16
16
|
super();
|
|
17
17
|
|
|
18
|
-
var block =
|
|
18
|
+
var block = safe_scope();
|
|
19
19
|
|
|
20
20
|
if (iterable) {
|
|
21
21
|
for (var item of iterable) {
|
|
@@ -82,7 +82,7 @@ export class RippleSet extends Set {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
add(value) {
|
|
85
|
-
var block =
|
|
85
|
+
var block = safe_scope();
|
|
86
86
|
|
|
87
87
|
if (!super.has(value)) {
|
|
88
88
|
super.add(value);
|
|
@@ -94,7 +94,7 @@ export class RippleSet extends Set {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
delete(value) {
|
|
97
|
-
var block =
|
|
97
|
+
var block = safe_scope();
|
|
98
98
|
|
|
99
99
|
if (!super.delete(value)) {
|
|
100
100
|
return false;
|
|
@@ -110,7 +110,7 @@ export class RippleSet extends Set {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
has(value) {
|
|
113
|
-
var block =
|
|
113
|
+
var block = safe_scope();
|
|
114
114
|
var has = super.has(value);
|
|
115
115
|
var tracked_items = this.#tracked_items;
|
|
116
116
|
var t = tracked_items.get(value);
|
|
@@ -129,7 +129,7 @@ export class RippleSet extends Set {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
clear() {
|
|
132
|
-
var block =
|
|
132
|
+
var block = safe_scope();
|
|
133
133
|
|
|
134
134
|
if (super.size === 0) {
|
|
135
135
|
return;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { mount, flushSync, effect } from 'ripple';
|
|
3
|
+
|
|
4
|
+
describe('prop accessors', () => {
|
|
5
|
+
let container;
|
|
6
|
+
|
|
7
|
+
function render(component) {
|
|
8
|
+
mount(component, {
|
|
9
|
+
target: container
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
container = document.createElement('div');
|
|
15
|
+
document.body.appendChild(container);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
document.body.removeChild(container);
|
|
20
|
+
container = null;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should render a basic prop accessor on a composite component', () => {
|
|
24
|
+
const logs = [];
|
|
25
|
+
|
|
26
|
+
component Child(props) {
|
|
27
|
+
effect(() => {
|
|
28
|
+
logs.push('App effect', props.$foo);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
<button onClick={() => props.$foo++}>{"Increment foo"}</button>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
component App(props) {
|
|
35
|
+
let $foo = 0;
|
|
36
|
+
|
|
37
|
+
<Child $foo:={() => {
|
|
38
|
+
return $foo;
|
|
39
|
+
}, v => {
|
|
40
|
+
$foo = v;
|
|
41
|
+
}} />
|
|
42
|
+
|
|
43
|
+
<div>{"parent foo: " + $foo}</div>
|
|
44
|
+
|
|
45
|
+
<button onClick={() => $foo++}>{"Increment parent foo"}</button>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
render(App);
|
|
49
|
+
flushSync();
|
|
50
|
+
|
|
51
|
+
expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 0');
|
|
52
|
+
expect(logs).toEqual(['App effect', 0]);
|
|
53
|
+
|
|
54
|
+
const [button, button2] = container.querySelectorAll('button');
|
|
55
|
+
|
|
56
|
+
button.click();
|
|
57
|
+
flushSync();
|
|
58
|
+
|
|
59
|
+
expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 1');
|
|
60
|
+
expect(logs).toEqual(['App effect', 0, 'App effect', 1]);
|
|
61
|
+
|
|
62
|
+
button2.click();
|
|
63
|
+
flushSync();
|
|
64
|
+
|
|
65
|
+
expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 2');
|
|
66
|
+
expect(logs).toEqual(['App effect', 0, 'App effect', 1, 'App effect', 2]);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should render a basic prop accessor on a composite component #2', () => {
|
|
70
|
+
const logs = [];
|
|
71
|
+
|
|
72
|
+
component Child(props) {
|
|
73
|
+
effect(() => {
|
|
74
|
+
logs.push('App effect', props.$foo);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
<button onClick={() => props.$foo++}>{"Increment foo"}</button>
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
component App(props) {
|
|
81
|
+
let $foo = 0;
|
|
82
|
+
|
|
83
|
+
<Child $foo:={() => {
|
|
84
|
+
return $foo;
|
|
85
|
+
}, v => {
|
|
86
|
+
// do not update parent
|
|
87
|
+
}} />
|
|
88
|
+
|
|
89
|
+
<div>{"parent foo: " + $foo}</div>
|
|
90
|
+
|
|
91
|
+
<button onClick={() => $foo++}>{"Increment parent foo"}</button>
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
render(App);
|
|
95
|
+
flushSync();
|
|
96
|
+
|
|
97
|
+
expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 0');
|
|
98
|
+
expect(logs).toEqual(['App effect', 0]);
|
|
99
|
+
|
|
100
|
+
const [button, button2] = container.querySelectorAll('button');
|
|
101
|
+
|
|
102
|
+
button.click();
|
|
103
|
+
flushSync();
|
|
104
|
+
|
|
105
|
+
expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 0');
|
|
106
|
+
expect(logs).toEqual(['App effect', 0, 'App effect', 1]);
|
|
107
|
+
|
|
108
|
+
button2.click();
|
|
109
|
+
flushSync();
|
|
110
|
+
|
|
111
|
+
expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 1');
|
|
112
|
+
expect(logs).toEqual(['App effect', 0, 'App effect', 1, 'App effect', 1]);
|
|
113
|
+
|
|
114
|
+
button2.click();
|
|
115
|
+
flushSync();
|
|
116
|
+
|
|
117
|
+
expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 2');
|
|
118
|
+
expect(logs).toEqual(['App effect', 0, 'App effect', 1, 'App effect', 1, 'App effect', 2]);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
it('handles a simple getter prop accessor with no setter', () =>{
|
|
123
|
+
component Parent() {
|
|
124
|
+
let $value = 123;
|
|
125
|
+
|
|
126
|
+
<Child $value:={() => $value} />
|
|
127
|
+
|
|
128
|
+
<button onClick={() => $value++}>{"Increment value"}</button>
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
component Child({ $value }) {
|
|
132
|
+
<div>{$value}</div>
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
render(Parent);
|
|
136
|
+
|
|
137
|
+
expect(container.querySelector('div').textContent).toBe('123');
|
|
138
|
+
|
|
139
|
+
const button = container.querySelector('button');
|
|
140
|
+
button.click();
|
|
141
|
+
flushSync();
|
|
142
|
+
|
|
143
|
+
expect(container.querySelector('div').textContent).toBe('124');
|
|
144
|
+
|
|
145
|
+
button.click();
|
|
146
|
+
flushSync();
|
|
147
|
+
|
|
148
|
+
expect(container.querySelector('div').textContent).toBe('125');
|
|
149
|
+
});
|
|
150
|
+
});
|
package/tests/basic.test.ripple
CHANGED
package/tests/boundaries.ripple
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { mount } from 'ripple';
|
|
2
|
+
import { mount, RippleArray } from 'ripple';
|
|
4
3
|
|
|
5
4
|
describe('compiler success tests', () => {
|
|
6
5
|
let container;
|
|
@@ -78,5 +77,80 @@ describe('compiler success tests', () => {
|
|
|
78
77
|
}
|
|
79
78
|
|
|
80
79
|
render(App);
|
|
81
|
-
})
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('properly handles JS assignments, reads and updates to array indices', () => {
|
|
83
|
+
const logs = [];
|
|
84
|
+
|
|
85
|
+
component App() {
|
|
86
|
+
let items = [];
|
|
87
|
+
let $items = [];
|
|
88
|
+
let items2 = new Array();
|
|
89
|
+
let items3 = new RippleArray();
|
|
90
|
+
let i = 0;
|
|
91
|
+
|
|
92
|
+
logs.push(items[0]);
|
|
93
|
+
logs.push(items[i]);
|
|
94
|
+
logs.push($items[0]);
|
|
95
|
+
logs.push($items[i]);
|
|
96
|
+
logs.push(items2[0]);
|
|
97
|
+
logs.push(items2[i]);
|
|
98
|
+
logs.push(items3[0]);
|
|
99
|
+
logs.push(items3[i]);
|
|
100
|
+
|
|
101
|
+
items[0] = 123;
|
|
102
|
+
items[i] = 123;
|
|
103
|
+
$items[0] = 123;
|
|
104
|
+
$items[i] = 123;
|
|
105
|
+
items2[0] = 123;
|
|
106
|
+
items2[i] = 123;
|
|
107
|
+
items3[0] = 123;
|
|
108
|
+
items3[i] = 123;
|
|
109
|
+
|
|
110
|
+
logs.push(items[0]);
|
|
111
|
+
logs.push(items[i]);
|
|
112
|
+
logs.push($items[0]);
|
|
113
|
+
logs.push($items[i]);
|
|
114
|
+
logs.push(items2[0]);
|
|
115
|
+
logs.push(items2[i]);
|
|
116
|
+
logs.push(items3[0]);
|
|
117
|
+
logs.push(items3[i]);
|
|
118
|
+
|
|
119
|
+
items[0]++;
|
|
120
|
+
items[i]++;
|
|
121
|
+
$items[0]++;
|
|
122
|
+
$items[i]++;
|
|
123
|
+
items2[0]++;
|
|
124
|
+
items2[i]++;
|
|
125
|
+
items3[0]++;
|
|
126
|
+
items3[i]++;
|
|
127
|
+
|
|
128
|
+
logs.push(items[0]);
|
|
129
|
+
logs.push(items[i]);
|
|
130
|
+
logs.push($items[0]);
|
|
131
|
+
logs.push($items[i]);
|
|
132
|
+
logs.push(items2[0]);
|
|
133
|
+
logs.push(items2[i]);
|
|
134
|
+
logs.push(items3[0]);
|
|
135
|
+
logs.push(items3[i]);
|
|
136
|
+
|
|
137
|
+
logs.push(--items[0]);
|
|
138
|
+
logs.push(--items[i]);
|
|
139
|
+
logs.push(--$items[0]);
|
|
140
|
+
logs.push(--$items[i]);
|
|
141
|
+
logs.push(--items2[0]);
|
|
142
|
+
logs.push(--items2[i]);
|
|
143
|
+
logs.push(--items3[0]);
|
|
144
|
+
logs.push(--items3[i]);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
render(App);
|
|
148
|
+
|
|
149
|
+
expect(logs).toEqual([
|
|
150
|
+
undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined,
|
|
151
|
+
123, 123, 123, 123, 123, 123, 123, 123,
|
|
152
|
+
125, 125, 125, 125, 125, 125, 125, 125,
|
|
153
|
+
124, 123, 124, 123, 124, 123, 124, 123
|
|
154
|
+
]);
|
|
155
|
+
});
|
|
82
156
|
});
|
package/tests/decorators.ripple
CHANGED
package/tests/map.test.ripple
CHANGED
|
@@ -128,7 +128,14 @@ describe('RippleMap', () => {
|
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
it('toJSON returns correct object', () => {
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
component MapTest() {
|
|
132
|
+
let map = new RippleMap([['foo', 1], ['bar', 2]]);
|
|
133
|
+
|
|
134
|
+
<pre>{JSON.stringify(map)}</pre>
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
render(MapTest);
|
|
138
|
+
|
|
139
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[["foo",1],["bar",2]]');
|
|
133
140
|
});
|
|
134
141
|
});
|