ripple 0.2.28 → 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/1-parse/index.js +11 -1
- package/src/compiler/phases/2-analyze/index.js +23 -9
- package/src/compiler/phases/3-transform/index.js +49 -17
- package/src/compiler/utils.js +47 -5
- package/src/runtime/array.js +18 -40
- package/src/runtime/index.js +2 -0
- package/src/runtime/internal/client/index.js +1 -0
- package/src/runtime/internal/client/runtime.js +114 -31
- package/src/runtime/map.js +147 -0
- package/src/runtime/set.js +37 -60
- package/tests/accessors-props.test.ripple +150 -0
- package/tests/basic.test.ripple +0 -1
- package/tests/boundaries.ripple +108 -0
- package/tests/compiler.test.ripple +97 -2
- package/tests/composite.test.ripple +0 -1
- package/tests/context.test.ripple +0 -1
- package/tests/{use.test.ripple → decorators.ripple} +0 -1
- package/tests/map.test.ripple +141 -0
- package/tests/set.test.ripple +1 -1
- package/types/index.d.ts +5 -0
package/package.json
CHANGED
|
@@ -424,12 +424,22 @@ function RipplePlugin(config) {
|
|
|
424
424
|
// This node is used for Prettier, we don't actually need
|
|
425
425
|
// the node for Ripple's transform process
|
|
426
426
|
element.children = [component.css];
|
|
427
|
+
// Ensure we escape JSX <tag></tag> context
|
|
428
|
+
const tokContexts = this.acornTypeScript.tokContexts;
|
|
429
|
+
const curContext = this.curContext();
|
|
430
|
+
|
|
431
|
+
if (curContext === tokContexts.tc_expr) {
|
|
432
|
+
this.context.pop();
|
|
433
|
+
}
|
|
434
|
+
|
|
427
435
|
return element;
|
|
428
436
|
} else {
|
|
437
|
+
this.enterScope(0);
|
|
429
438
|
this.parseTemplateBody(element.children);
|
|
439
|
+
this.exitScope();
|
|
430
440
|
}
|
|
441
|
+
// Ensure we escape JSX <tag></tag> context
|
|
431
442
|
const tokContexts = this.acornTypeScript.tokContexts;
|
|
432
|
-
|
|
433
443
|
const curContext = this.curContext();
|
|
434
444
|
|
|
435
445
|
if (curContext === tokContexts.tc_expr) {
|
|
@@ -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
|
|
|
@@ -151,6 +158,13 @@ const visitors = {
|
|
|
151
158
|
const { state, visit, path } = context;
|
|
152
159
|
|
|
153
160
|
for (const declarator of node.declarations) {
|
|
161
|
+
if (is_inside_component(context) && node.kind === 'var') {
|
|
162
|
+
error(
|
|
163
|
+
'var declarations are not allowed in components, use let or const instead',
|
|
164
|
+
state.analysis.module.filename,
|
|
165
|
+
declarator,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
154
168
|
const metadata = { tracking: false, await: false };
|
|
155
169
|
const parent = path.at(-1);
|
|
156
170
|
const init_is_untracked =
|
|
@@ -205,7 +219,7 @@ const visitors = {
|
|
|
205
219
|
if (!computed) {
|
|
206
220
|
return node;
|
|
207
221
|
}
|
|
208
|
-
return b.call('$.set_property', node,
|
|
222
|
+
return b.call('$.set_property', node, computed, value, b.id('__block'));
|
|
209
223
|
},
|
|
210
224
|
};
|
|
211
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';
|
|
@@ -133,6 +135,7 @@ const visitors = {
|
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
if (
|
|
138
|
+
!is_inside_component(context, true) ||
|
|
136
139
|
context.state.to_ts ||
|
|
137
140
|
(parent?.type === 'MemberExpression' && parent.property === node) ||
|
|
138
141
|
is_inside_call_expression(context) ||
|
|
@@ -164,6 +167,14 @@ const visitors = {
|
|
|
164
167
|
context.state.metadata.tracking = true;
|
|
165
168
|
}
|
|
166
169
|
|
|
170
|
+
if (!is_inside_component(context, true) || is_inside_call_expression(context)) {
|
|
171
|
+
return context.next();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (is_value_static(node)) {
|
|
175
|
+
return context.next();
|
|
176
|
+
}
|
|
177
|
+
|
|
167
178
|
return b.call(
|
|
168
179
|
'$.with_scope',
|
|
169
180
|
b.id('__block'),
|
|
@@ -187,7 +198,10 @@ const visitors = {
|
|
|
187
198
|
: property.type === 'Literal' && is_tracked_name(property.value);
|
|
188
199
|
|
|
189
200
|
// TODO should we enforce that the identifier is tracked too?
|
|
190
|
-
if (
|
|
201
|
+
if (
|
|
202
|
+
(node.computed && is_tracked_computed_property(node.object, node.property, context)) ||
|
|
203
|
+
tracked_name
|
|
204
|
+
) {
|
|
191
205
|
if (context.state.metadata?.tracking === false) {
|
|
192
206
|
context.state.metadata.tracking = true;
|
|
193
207
|
}
|
|
@@ -572,7 +586,18 @@ const visitors = {
|
|
|
572
586
|
);
|
|
573
587
|
}
|
|
574
588
|
|
|
575
|
-
|
|
589
|
+
const init = [];
|
|
590
|
+
const update = [];
|
|
591
|
+
|
|
592
|
+
transform_children(node.children, { visit, state: { ...state, init, update }, root: false });
|
|
593
|
+
|
|
594
|
+
if (init.length > 0) {
|
|
595
|
+
state.init.push(b.block(init));
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (update.length > 0) {
|
|
599
|
+
state.init.push(b.stmt(b.call('$.render', b.thunk(b.block(update)))));
|
|
600
|
+
}
|
|
576
601
|
|
|
577
602
|
state.template.push(`</${node.id.name}>`);
|
|
578
603
|
} else {
|
|
@@ -845,19 +870,20 @@ const visitors = {
|
|
|
845
870
|
|
|
846
871
|
const left = node.left;
|
|
847
872
|
|
|
848
|
-
if (
|
|
849
|
-
left.type === '
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
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
|
+
}
|
|
861
887
|
}
|
|
862
888
|
|
|
863
889
|
const visited = visit_assignment_expression(node, context, build_assignment) ?? context.next();
|
|
@@ -884,7 +910,8 @@ const visitors = {
|
|
|
884
910
|
if (
|
|
885
911
|
argument.type === 'MemberExpression' &&
|
|
886
912
|
((argument.property.type === 'Identifier' && is_tracked_name(argument.property.name)) ||
|
|
887
|
-
argument.computed
|
|
913
|
+
(argument.computed &&
|
|
914
|
+
is_tracked_computed_property(argument.object, argument.property, context)))
|
|
888
915
|
) {
|
|
889
916
|
return b.call(
|
|
890
917
|
node.prefix ? '$.update_pre_property' : '$.update_property',
|
|
@@ -1258,7 +1285,7 @@ function transform_ts_child(node, context) {
|
|
|
1258
1285
|
.filter((attr) => {
|
|
1259
1286
|
if (attr.type === 'UseAttribute') {
|
|
1260
1287
|
use_attributes.push(attr);
|
|
1261
|
-
return false;
|
|
1288
|
+
return false;
|
|
1262
1289
|
}
|
|
1263
1290
|
return true;
|
|
1264
1291
|
})
|
|
@@ -1415,7 +1442,12 @@ function transform_ts_child(node, context) {
|
|
|
1415
1442
|
),
|
|
1416
1443
|
),
|
|
1417
1444
|
);
|
|
1445
|
+
} else if (node.type === 'Component') {
|
|
1446
|
+
const component = visit(node, context.state);
|
|
1447
|
+
|
|
1448
|
+
state.init.push(component);
|
|
1418
1449
|
} else {
|
|
1450
|
+
debugger;
|
|
1419
1451
|
throw new Error('TODO');
|
|
1420
1452
|
}
|
|
1421
1453
|
}
|
package/src/compiler/utils.js
CHANGED
|
@@ -327,15 +327,16 @@ export function build_hoisted_params(node, context) {
|
|
|
327
327
|
return params;
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
-
export function is_inside_component(context) {
|
|
330
|
+
export function is_inside_component(context, includes_functions = false) {
|
|
331
331
|
for (let i = context.path.length - 1; i >= 0; i -= 1) {
|
|
332
332
|
const context_node = context.path[i];
|
|
333
333
|
const type = context_node.type;
|
|
334
334
|
|
|
335
335
|
if (
|
|
336
|
-
|
|
337
|
-
type === '
|
|
338
|
-
|
|
336
|
+
!includes_functions &&
|
|
337
|
+
(type === 'FunctionExpression' ||
|
|
338
|
+
type === 'ArrowFunctionExpression' ||
|
|
339
|
+
type === 'FunctionDeclaration')
|
|
339
340
|
) {
|
|
340
341
|
return false;
|
|
341
342
|
}
|
|
@@ -369,6 +370,47 @@ export function is_tracked_name(name) {
|
|
|
369
370
|
return typeof name === 'string' && name.startsWith('$') && name.length > 1 && name[1] !== '$';
|
|
370
371
|
}
|
|
371
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
|
+
|
|
372
414
|
export function is_ripple_import(callee, context) {
|
|
373
415
|
if (callee.type === 'Identifier') {
|
|
374
416
|
const binding = context.state.scope.get(callee.name);
|
|
@@ -506,7 +548,7 @@ export function build_assignment(operator, left, right, context) {
|
|
|
506
548
|
return transform.assign(
|
|
507
549
|
object,
|
|
508
550
|
value,
|
|
509
|
-
left.type === 'MemberExpression' && left.computed ? left.property : undefined,
|
|
551
|
+
left.type === 'MemberExpression' && left.computed ? context.visit(left.property) : undefined,
|
|
510
552
|
);
|
|
511
553
|
}
|
|
512
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
|
-
|
package/src/runtime/index.js
CHANGED
|
@@ -42,6 +42,8 @@ export { RippleArray } from './array.js';
|
|
|
42
42
|
|
|
43
43
|
export { RippleSet } from './set.js';
|
|
44
44
|
|
|
45
|
+
export { RippleMap } from './map.js';
|
|
46
|
+
|
|
45
47
|
export { keyed } from './internal/client/for.js';
|
|
46
48
|
|
|
47
49
|
export { user_effect as effect } from './internal/client/blocks.js';
|