ripple 0.2.50 → 0.2.52
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 +9 -8
- package/src/compiler/phases/2-analyze/index.js +99 -188
- package/src/compiler/phases/3-transform/index.js +37 -202
- package/src/compiler/utils.js +24 -11
- package/src/runtime/internal/client/constants.js +0 -1
- package/src/runtime/internal/client/index.js +2 -1
- package/src/runtime/internal/client/runtime.js +31 -15
- package/src/runtime/internal/client/try.js +1 -1
- package/tests/array.test.ripple +20 -9
- package/tests/composite.test.ripple +42 -1
package/package.json
CHANGED
|
@@ -292,6 +292,14 @@ function RipplePlugin(config) {
|
|
|
292
292
|
this.next();
|
|
293
293
|
node.block = this.parseBlock();
|
|
294
294
|
node.handler = null;
|
|
295
|
+
|
|
296
|
+
if (this.value === 'pending') {
|
|
297
|
+
this.next();
|
|
298
|
+
node.pending = this.parseBlock();
|
|
299
|
+
} else {
|
|
300
|
+
node.pending = null;
|
|
301
|
+
}
|
|
302
|
+
|
|
295
303
|
if (this.type === tt._catch) {
|
|
296
304
|
var clause = this.startNode();
|
|
297
305
|
this.next();
|
|
@@ -310,14 +318,7 @@ function RipplePlugin(config) {
|
|
|
310
318
|
}
|
|
311
319
|
node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null;
|
|
312
320
|
|
|
313
|
-
if (
|
|
314
|
-
this.next();
|
|
315
|
-
node.async = this.parseBlock();
|
|
316
|
-
} else {
|
|
317
|
-
node.async = null;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (!node.handler && !node.finalizer && !node.async) {
|
|
321
|
+
if (!node.handler && !node.finalizer && !node.pending) {
|
|
321
322
|
this.raise(node.start, 'Missing catch or finally clause');
|
|
322
323
|
}
|
|
323
324
|
return this.finishNode(node, 'TryStatement');
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
is_inside_component,
|
|
9
9
|
is_ripple_import,
|
|
10
10
|
is_tracked_computed_property,
|
|
11
|
-
is_tracked_name,
|
|
12
11
|
is_void_element,
|
|
13
12
|
} from '../../utils.js';
|
|
14
13
|
import { extract_paths } from '../../../utils/ast.js';
|
|
@@ -16,7 +15,7 @@ import is_reference from 'is-reference';
|
|
|
16
15
|
import { prune_css } from './prune.js';
|
|
17
16
|
import { error } from '../../errors.js';
|
|
18
17
|
|
|
19
|
-
function
|
|
18
|
+
function mark_control_flow_has_template(path) {
|
|
20
19
|
for (let i = path.length - 1; i >= 0; i -= 1) {
|
|
21
20
|
const node = path[i];
|
|
22
21
|
|
|
@@ -31,10 +30,11 @@ function mark_for_loop_has_template(path) {
|
|
|
31
30
|
if (
|
|
32
31
|
node.type === 'ForStatement' ||
|
|
33
32
|
node.type === 'ForInStatement' ||
|
|
34
|
-
node.type === 'ForOfStatement'
|
|
33
|
+
node.type === 'ForOfStatement' ||
|
|
34
|
+
node.type === 'TryStatement' ||
|
|
35
|
+
node.type === 'IfStatement'
|
|
35
36
|
) {
|
|
36
37
|
node.metadata.has_template = true;
|
|
37
|
-
break;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -47,34 +47,6 @@ function visit_function(node, context) {
|
|
|
47
47
|
tracked: false,
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
if (node.params.length > 0) {
|
|
51
|
-
for (let i = 0; i < node.params.length; i += 1) {
|
|
52
|
-
const param = node.params[i];
|
|
53
|
-
|
|
54
|
-
if (param.type === 'ObjectPattern' || param.type === 'ArrayPattern') {
|
|
55
|
-
const paths = extract_paths(param);
|
|
56
|
-
const id = context.state.scope.generate('__arg');
|
|
57
|
-
const arg_id = b.id(id);
|
|
58
|
-
|
|
59
|
-
for (const path of paths) {
|
|
60
|
-
const name = path.node.name;
|
|
61
|
-
|
|
62
|
-
const expression = path.expression(arg_id);
|
|
63
|
-
const binding = context.state.scope.get(name);
|
|
64
|
-
|
|
65
|
-
if (binding !== null && is_tracked_name(name)) {
|
|
66
|
-
node.params[i] = b.id(id);
|
|
67
|
-
binding.kind = path.has_default_value ? 'prop_fallback' : 'prop';
|
|
68
|
-
|
|
69
|
-
binding.transform = {
|
|
70
|
-
read: (_, __, visit) => visit(expression),
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
50
|
context.next({
|
|
79
51
|
...context.state,
|
|
80
52
|
function_depth: context.state.function_depth + 1,
|
|
@@ -129,7 +101,7 @@ const visitors = {
|
|
|
129
101
|
|
|
130
102
|
if (
|
|
131
103
|
is_reference(node, /** @type {Node} */ (parent)) &&
|
|
132
|
-
|
|
104
|
+
node.tracked &&
|
|
133
105
|
binding?.node !== node
|
|
134
106
|
) {
|
|
135
107
|
mark_as_tracked(context.path);
|
|
@@ -155,18 +127,7 @@ const visitors = {
|
|
|
155
127
|
const parent = context.path.at(-1);
|
|
156
128
|
|
|
157
129
|
if (context.state.metadata?.tracking === false && parent.type !== 'AssignmentExpression') {
|
|
158
|
-
|
|
159
|
-
node.property.type === 'Identifier' &&
|
|
160
|
-
!node.computed &&
|
|
161
|
-
is_tracked_name(node.property.name)
|
|
162
|
-
) {
|
|
163
|
-
context.state.metadata.tracking = true;
|
|
164
|
-
} else if (
|
|
165
|
-
node.computed &&
|
|
166
|
-
is_tracked_computed_property(node.object, node.property, context)
|
|
167
|
-
) {
|
|
168
|
-
context.state.metadata.tracking = true;
|
|
169
|
-
}
|
|
130
|
+
context.state.metadata.tracking = true;
|
|
170
131
|
}
|
|
171
132
|
|
|
172
133
|
context.next();
|
|
@@ -184,32 +145,6 @@ const visitors = {
|
|
|
184
145
|
context.next();
|
|
185
146
|
},
|
|
186
147
|
|
|
187
|
-
ObjectExpression(node, context) {
|
|
188
|
-
for (const property of node.properties) {
|
|
189
|
-
if (
|
|
190
|
-
property.type === 'Property' &&
|
|
191
|
-
!property.computed &&
|
|
192
|
-
property.key.type === 'Identifier' &&
|
|
193
|
-
property.kind === 'init' &&
|
|
194
|
-
is_tracked_name(property.key.name)
|
|
195
|
-
) {
|
|
196
|
-
mark_as_tracked(context.path);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
context.next();
|
|
201
|
-
},
|
|
202
|
-
|
|
203
|
-
ArrayExpression(node, context) {
|
|
204
|
-
for (const element of node.elements) {
|
|
205
|
-
if (element !== null && element.type === 'Identifier' && is_tracked_name(element.name)) {
|
|
206
|
-
mark_as_tracked(context.path);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
context.next();
|
|
211
|
-
},
|
|
212
|
-
|
|
213
148
|
VariableDeclaration(node, context) {
|
|
214
149
|
const { state, visit, path } = context;
|
|
215
150
|
|
|
@@ -234,42 +169,17 @@ const visitors = {
|
|
|
234
169
|
const binding = state.scope.get(declarator.id.name);
|
|
235
170
|
|
|
236
171
|
if (binding !== null && parent?.type !== 'ForOfStatement') {
|
|
237
|
-
if (
|
|
238
|
-
binding.kind = 'tracked';
|
|
239
|
-
|
|
240
|
-
mark_as_tracked(path);
|
|
241
|
-
|
|
242
|
-
visit(declarator, { ...state, metadata });
|
|
243
|
-
|
|
244
|
-
if (init_is_untracked && metadata.tracking) {
|
|
245
|
-
metadata.tracking = false;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
binding.transform = {
|
|
249
|
-
read: (node) => {
|
|
250
|
-
return metadata.tracking && !metadata.await
|
|
251
|
-
? b.call('$.get_derived', node)
|
|
252
|
-
: b.call('$.get_tracked', node);
|
|
253
|
-
},
|
|
254
|
-
assign: (node, value) => {
|
|
255
|
-
return b.call('$.set', node, value, b.id('__block'));
|
|
256
|
-
},
|
|
257
|
-
update: (node) => {
|
|
258
|
-
return b.call(
|
|
259
|
-
node.prefix ? '$.update_pre' : '$.update',
|
|
260
|
-
node.argument,
|
|
261
|
-
b.id('__block'),
|
|
262
|
-
node.operator === '--' && b.literal(-1),
|
|
263
|
-
);
|
|
264
|
-
},
|
|
265
|
-
};
|
|
266
|
-
} else if (binding.initial?.type !== 'Literal') {
|
|
172
|
+
if (binding.initial?.type !== 'Literal') {
|
|
267
173
|
for (const ref of binding.references) {
|
|
268
174
|
const path = ref.path;
|
|
269
175
|
const parent_node = path?.at(-1);
|
|
270
176
|
|
|
271
177
|
// We're reading a computed property, which might mean it's a reactive property
|
|
272
|
-
if (
|
|
178
|
+
if (
|
|
179
|
+
!ref.node.tracked &&
|
|
180
|
+
parent_node?.type === 'MemberExpression' &&
|
|
181
|
+
parent_node.computed
|
|
182
|
+
) {
|
|
273
183
|
binding.transform = {
|
|
274
184
|
assign: (node, value, computed) => {
|
|
275
185
|
if (!computed) {
|
|
@@ -289,9 +199,7 @@ const visitors = {
|
|
|
289
199
|
}
|
|
290
200
|
} else {
|
|
291
201
|
const paths = extract_paths(declarator.id);
|
|
292
|
-
|
|
293
|
-
(path) => path.node.type === 'Identifier' && is_tracked_name(path.node.name),
|
|
294
|
-
);
|
|
202
|
+
|
|
295
203
|
for (const path of paths) {
|
|
296
204
|
if (path.node.tracked) {
|
|
297
205
|
error(
|
|
@@ -302,68 +210,7 @@ const visitors = {
|
|
|
302
210
|
}
|
|
303
211
|
}
|
|
304
212
|
|
|
305
|
-
|
|
306
|
-
const tmp = state.scope.generate('tmp');
|
|
307
|
-
declarator.transformed = b.id(tmp);
|
|
308
|
-
|
|
309
|
-
if (declarator.init !== null) {
|
|
310
|
-
visit(declarator.init, { ...state, metadata });
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (init_is_untracked && metadata.tracking) {
|
|
314
|
-
metadata.tracking = false;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
for (const path of paths) {
|
|
318
|
-
const binding = state.scope.get(path.node.name);
|
|
319
|
-
|
|
320
|
-
binding.transform = {
|
|
321
|
-
read: (node) => {
|
|
322
|
-
const value = path.expression?.(b.id(tmp));
|
|
323
|
-
|
|
324
|
-
if (metadata.tracking && metadata.await) {
|
|
325
|
-
// TODO
|
|
326
|
-
debugger;
|
|
327
|
-
} else if (metadata.tracking && !metadata.await) {
|
|
328
|
-
if (is_tracked_name(path.node.name) && value.type === 'MemberExpression') {
|
|
329
|
-
return b.call(
|
|
330
|
-
'$.old_get_property',
|
|
331
|
-
b.call('$.get_derived', value.object),
|
|
332
|
-
value.property.type === 'Identifier'
|
|
333
|
-
? b.literal(value.property.name)
|
|
334
|
-
: value.property,
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const key =
|
|
339
|
-
value.property.type === 'Identifier'
|
|
340
|
-
? b.key(value.property.name)
|
|
341
|
-
: value.property;
|
|
342
|
-
|
|
343
|
-
return b.member(
|
|
344
|
-
b.call('$.get_derived', value.object),
|
|
345
|
-
key,
|
|
346
|
-
key.type === 'Literal',
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if (is_tracked_name(path.node.name) && value.type === 'MemberExpression') {
|
|
351
|
-
return b.call(
|
|
352
|
-
'$.old_get_property',
|
|
353
|
-
value.object,
|
|
354
|
-
value.property.type === 'Identifier'
|
|
355
|
-
? b.literal(value.property.name)
|
|
356
|
-
: value.property,
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return value;
|
|
361
|
-
},
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
} else {
|
|
365
|
-
visit(declarator, state);
|
|
366
|
-
}
|
|
213
|
+
visit(declarator, state);
|
|
367
214
|
}
|
|
368
215
|
|
|
369
216
|
declarator.metadata = metadata;
|
|
@@ -398,8 +245,8 @@ const visitors = {
|
|
|
398
245
|
|
|
399
246
|
binding.transform = {
|
|
400
247
|
read: (_) => {
|
|
401
|
-
|
|
402
|
-
|
|
248
|
+
return path.expression(b.id('__props'));
|
|
249
|
+
},
|
|
403
250
|
assign: (node, value) => {
|
|
404
251
|
return b.assignment('=', path.expression(b.id('__props')), value);
|
|
405
252
|
},
|
|
@@ -450,15 +297,91 @@ const visitors = {
|
|
|
450
297
|
has_template: false,
|
|
451
298
|
};
|
|
452
299
|
context.next();
|
|
300
|
+
|
|
453
301
|
if (!node.metadata.has_template) {
|
|
454
302
|
error(
|
|
455
|
-
'
|
|
303
|
+
'Component for...of loops must contain a template in their body. Move the for loop into an effect if it does not render anything.',
|
|
456
304
|
context.state.analysis.module.filename,
|
|
457
305
|
node,
|
|
458
306
|
);
|
|
459
307
|
}
|
|
460
308
|
},
|
|
461
309
|
|
|
310
|
+
IfStatement(node, context) {
|
|
311
|
+
if (!is_inside_component(context)) {
|
|
312
|
+
return context.next();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
node.metadata = {
|
|
316
|
+
has_template: false,
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
context.visit(node.consequent, context.state);
|
|
320
|
+
|
|
321
|
+
if (!node.metadata.has_template) {
|
|
322
|
+
error(
|
|
323
|
+
'Component if statements must contain a template in their "then" body. Move the if statement into an effect if it does not render anything.',
|
|
324
|
+
context.state.analysis.module.filename,
|
|
325
|
+
node,
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (node.alternate) {
|
|
330
|
+
node.metadata = {
|
|
331
|
+
has_template: false,
|
|
332
|
+
};
|
|
333
|
+
context.visit(node.alternate, context.state);
|
|
334
|
+
|
|
335
|
+
if (!node.metadata.has_template) {
|
|
336
|
+
error(
|
|
337
|
+
'Component if statements must contain a template in their "else" body. Move the if statement into an effect if it does not render anything.',
|
|
338
|
+
context.state.analysis.module.filename,
|
|
339
|
+
node,
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
|
|
345
|
+
TryStatement(node, context) {
|
|
346
|
+
if (!is_inside_component(context)) {
|
|
347
|
+
return context.next();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (node.pending) {
|
|
351
|
+
node.metadata = {
|
|
352
|
+
has_template: false,
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
context.visit(node.block, context.state);
|
|
356
|
+
|
|
357
|
+
if (!node.metadata.has_template) {
|
|
358
|
+
error(
|
|
359
|
+
'Component try statements must contain a template in their main body. Move the try statement into an effect if it does not render anything.',
|
|
360
|
+
context.state.analysis.module.filename,
|
|
361
|
+
node,
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
node.metadata = {
|
|
366
|
+
has_template: false,
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
context.visit(node.pending, context.state);
|
|
370
|
+
|
|
371
|
+
if (!node.metadata.has_template) {
|
|
372
|
+
error(
|
|
373
|
+
'Component try statements must contain a template in their "pending" body. Rendering a pending fallback is required to have a template.',
|
|
374
|
+
context.state.analysis.module.filename,
|
|
375
|
+
node,
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (node.finalizer) {
|
|
381
|
+
context.visit(node.finalizer, context.state);
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
|
|
462
385
|
ForInStatement(node, context) {
|
|
463
386
|
if (is_inside_component(context)) {
|
|
464
387
|
error(
|
|
@@ -486,7 +409,7 @@ const visitors = {
|
|
|
486
409
|
const is_dom_element = is_element_dom_element(node, context);
|
|
487
410
|
const attribute_names = new Set();
|
|
488
411
|
|
|
489
|
-
|
|
412
|
+
mark_control_flow_has_template(path);
|
|
490
413
|
|
|
491
414
|
if (is_dom_element) {
|
|
492
415
|
const is_void = is_void_element(node.id.name);
|
|
@@ -547,11 +470,11 @@ const visitors = {
|
|
|
547
470
|
} else if (attr.type === 'AccessorAttribute') {
|
|
548
471
|
attribute_names.add(attr.name);
|
|
549
472
|
visit(attr.get, state);
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
473
|
+
if (attr.set) {
|
|
474
|
+
visit(attr.set, state);
|
|
475
|
+
}
|
|
553
476
|
} else if (attr.type === 'SpreadAttribute') {
|
|
554
|
-
|
|
477
|
+
visit(attr.argument, state);
|
|
555
478
|
} else if (attr.type === 'RefAttribute') {
|
|
556
479
|
visit(attr.argument, state);
|
|
557
480
|
}
|
|
@@ -602,18 +525,6 @@ const visitors = {
|
|
|
602
525
|
);
|
|
603
526
|
}
|
|
604
527
|
}
|
|
605
|
-
|
|
606
|
-
if (is_tracked_name(name)) {
|
|
607
|
-
attribute_names.forEach((n) => {
|
|
608
|
-
if (n.name.slice(1) === name) {
|
|
609
|
-
error(
|
|
610
|
-
`Cannot have both ${name} and ${name.slice(1)} on the same element`,
|
|
611
|
-
state.analysis.module.filename,
|
|
612
|
-
n,
|
|
613
|
-
);
|
|
614
|
-
}
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
528
|
}
|
|
618
529
|
|
|
619
530
|
return {
|
|
@@ -623,7 +534,7 @@ const visitors = {
|
|
|
623
534
|
},
|
|
624
535
|
|
|
625
536
|
Text(node, context) {
|
|
626
|
-
|
|
537
|
+
mark_control_flow_has_template(context.path);
|
|
627
538
|
context.next();
|
|
628
539
|
},
|
|
629
540
|
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
build_hoisted_params,
|
|
10
10
|
is_event_attribute,
|
|
11
11
|
is_inside_component,
|
|
12
|
-
is_tracked_name,
|
|
13
12
|
is_passive_event,
|
|
14
13
|
build_assignment,
|
|
15
14
|
visit_assignment_expression,
|
|
@@ -24,9 +23,10 @@ import {
|
|
|
24
23
|
is_void_element,
|
|
25
24
|
is_component_level_function,
|
|
26
25
|
is_element_dom_element,
|
|
26
|
+
is_top_level_await,
|
|
27
27
|
} from '../../utils.js';
|
|
28
28
|
import is_reference from 'is-reference';
|
|
29
|
-
import {
|
|
29
|
+
import { object } from '../../../utils/ast.js';
|
|
30
30
|
import { render_stylesheets } from './stylesheet.js';
|
|
31
31
|
|
|
32
32
|
function add_ripple_internal_import(context) {
|
|
@@ -130,10 +130,7 @@ const visitors = {
|
|
|
130
130
|
if (
|
|
131
131
|
(context.state.metadata?.tracking === false ||
|
|
132
132
|
(parent.type !== 'AssignmentExpression' && parent.type !== 'UpdateExpression')) &&
|
|
133
|
-
(
|
|
134
|
-
node.tracked ||
|
|
135
|
-
binding?.kind === 'prop' ||
|
|
136
|
-
binding?.kind === 'prop_fallback') &&
|
|
133
|
+
(node.tracked || binding?.kind === 'prop' || binding?.kind === 'prop_fallback') &&
|
|
137
134
|
binding?.node !== node
|
|
138
135
|
) {
|
|
139
136
|
if (context.state.metadata?.tracking === false) {
|
|
@@ -232,7 +229,7 @@ const visitors = {
|
|
|
232
229
|
...node,
|
|
233
230
|
callee: context.visit(callee),
|
|
234
231
|
arguments: node.arguments.map((arg) => context.visit(arg)),
|
|
235
|
-
}),
|
|
232
|
+
}, context.state.metadata?.await ?? false),
|
|
236
233
|
);
|
|
237
234
|
},
|
|
238
235
|
|
|
@@ -287,10 +284,13 @@ const visitors = {
|
|
|
287
284
|
MemberExpression(node, context) {
|
|
288
285
|
const parent = context.path.at(-1);
|
|
289
286
|
|
|
287
|
+
if (context.state.metadata?.tracking === false) {
|
|
288
|
+
context.state.metadata.tracking = true;
|
|
289
|
+
}
|
|
290
|
+
|
|
290
291
|
if (node.property.type === 'Identifier' && node.property.tracked) {
|
|
291
292
|
add_ripple_internal_import(context);
|
|
292
293
|
|
|
293
|
-
context.state.metadata.tracking = true;
|
|
294
294
|
return b.call(
|
|
295
295
|
'$.get_property',
|
|
296
296
|
context.visit(node.object),
|
|
@@ -302,35 +302,19 @@ const visitors = {
|
|
|
302
302
|
if (parent.type !== 'AssignmentExpression') {
|
|
303
303
|
const object = node.object;
|
|
304
304
|
const property = node.property;
|
|
305
|
-
const tracked_name =
|
|
306
|
-
property.type === 'Identifier'
|
|
307
|
-
? is_tracked_name(property.name)
|
|
308
|
-
: property.type === 'Literal' && is_tracked_name(property.value);
|
|
309
305
|
|
|
310
306
|
// TODO should we enforce that the identifier is tracked too?
|
|
311
|
-
if (
|
|
312
|
-
(node.computed && is_tracked_computed_property(node.object, node.property, context)) ||
|
|
313
|
-
tracked_name
|
|
314
|
-
) {
|
|
307
|
+
if (node.computed && is_tracked_computed_property(node.object, node.property, context)) {
|
|
315
308
|
if (context.state.metadata?.tracking === false) {
|
|
316
309
|
context.state.metadata.tracking = true;
|
|
317
310
|
}
|
|
318
311
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
);
|
|
326
|
-
} else {
|
|
327
|
-
return b.call(
|
|
328
|
-
'$.old_get_property',
|
|
329
|
-
context.visit(object),
|
|
330
|
-
context.visit(property),
|
|
331
|
-
node.optional ? b.true : undefined,
|
|
332
|
-
);
|
|
333
|
-
}
|
|
312
|
+
return b.call(
|
|
313
|
+
'$.old_get_property',
|
|
314
|
+
context.visit(object),
|
|
315
|
+
context.visit(property),
|
|
316
|
+
node.optional ? b.true : undefined,
|
|
317
|
+
);
|
|
334
318
|
}
|
|
335
319
|
|
|
336
320
|
if (object.type === 'Identifier' && object.name === 'Object') {
|
|
@@ -417,6 +401,7 @@ const visitors = {
|
|
|
417
401
|
);
|
|
418
402
|
}
|
|
419
403
|
} else {
|
|
404
|
+
debugger;
|
|
420
405
|
// Runtime mode: full transformation
|
|
421
406
|
if (metadata.tracking && metadata.await) {
|
|
422
407
|
expression = b.call(
|
|
@@ -451,58 +436,11 @@ const visitors = {
|
|
|
451
436
|
declarations.push(context.visit(declarator));
|
|
452
437
|
}
|
|
453
438
|
} else {
|
|
454
|
-
const paths = extract_paths(declarator.id);
|
|
455
|
-
const has_tracked = paths.some(
|
|
456
|
-
(path) => path.node.type === 'Identifier' && is_tracked_name(path.node.name),
|
|
457
|
-
);
|
|
458
|
-
|
|
459
439
|
if (!context.state.to_ts) {
|
|
460
440
|
delete declarator.id.typeAnnotation;
|
|
461
441
|
}
|
|
462
442
|
|
|
463
|
-
|
|
464
|
-
declarations.push(context.visit(declarator));
|
|
465
|
-
continue;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// For TypeScript mode, we still need to transform tracked variables
|
|
469
|
-
// but use a lighter approach that maintains type information
|
|
470
|
-
if (context.state.to_ts) {
|
|
471
|
-
const transformed = declarator.transformed || declarator.id;
|
|
472
|
-
let expression;
|
|
473
|
-
|
|
474
|
-
if (metadata.tracking && !metadata.await) {
|
|
475
|
-
expression = b.call(
|
|
476
|
-
'$.derived',
|
|
477
|
-
b.thunk(context.visit(declarator.init)),
|
|
478
|
-
b.id('__block'),
|
|
479
|
-
);
|
|
480
|
-
} else {
|
|
481
|
-
// Simple tracked variable - always use $.derived for $ prefixed variables
|
|
482
|
-
expression = b.call('$.tracked', context.visit(declarator.init), b.id('__block'));
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
declarations.push(b.declarator(transformed, expression));
|
|
486
|
-
continue;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
const transformed = declarator.transformed;
|
|
490
|
-
let expression;
|
|
491
|
-
|
|
492
|
-
if (metadata.tracking && metadata.await) {
|
|
493
|
-
// TODO
|
|
494
|
-
debugger;
|
|
495
|
-
} else if (metadata.tracking && !metadata.await) {
|
|
496
|
-
expression = b.call(
|
|
497
|
-
'$.derived',
|
|
498
|
-
b.thunk(context.visit(declarator.init)),
|
|
499
|
-
b.id('__block'),
|
|
500
|
-
);
|
|
501
|
-
} else {
|
|
502
|
-
expression = context.visit(declarator.init);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
declarations.push(b.declarator(transformed, expression));
|
|
443
|
+
declarations.push(context.visit(declarator));
|
|
506
444
|
}
|
|
507
445
|
}
|
|
508
446
|
|
|
@@ -675,8 +613,8 @@ const visitors = {
|
|
|
675
613
|
const metadata = { tracking: false, await: false };
|
|
676
614
|
const expression = visit(attr.value, { ...state, metadata });
|
|
677
615
|
// All other attributes
|
|
678
|
-
if (
|
|
679
|
-
const attribute =
|
|
616
|
+
if (metadata.tracking) {
|
|
617
|
+
const attribute = name;
|
|
680
618
|
const id = state.flush_node();
|
|
681
619
|
|
|
682
620
|
if (is_dom_property(attribute)) {
|
|
@@ -762,7 +700,7 @@ const visitors = {
|
|
|
762
700
|
}
|
|
763
701
|
|
|
764
702
|
if (update.length > 0) {
|
|
765
|
-
state.init.push(b.stmt(b.call('$.render', b.thunk(b.block(update)))));
|
|
703
|
+
state.init.push(b.stmt(b.call('$.render', b.thunk(b.block(update), update.async ?? true))));
|
|
766
704
|
}
|
|
767
705
|
} else {
|
|
768
706
|
const id = state.flush_node();
|
|
@@ -885,7 +823,7 @@ const visitors = {
|
|
|
885
823
|
b.call(
|
|
886
824
|
node.id,
|
|
887
825
|
id,
|
|
888
|
-
b.call('$.tracked_spread_object', b.thunk(b.object(props))),
|
|
826
|
+
b.call('$.tracked_spread_object', b.thunk(b.object(props)), b.id('__block')),
|
|
889
827
|
b.id('$.active_block'),
|
|
890
828
|
),
|
|
891
829
|
),
|
|
@@ -1016,10 +954,7 @@ const visitors = {
|
|
|
1016
954
|
|
|
1017
955
|
if (left.type === 'MemberExpression') {
|
|
1018
956
|
// need to capture setting length of array to throw a runtime error
|
|
1019
|
-
if (
|
|
1020
|
-
left.property.type === 'Identifier' &&
|
|
1021
|
-
(is_tracked_name(left.property.name) || left.property.name === 'length')
|
|
1022
|
-
) {
|
|
957
|
+
if (left.property.type === 'Identifier' && left.property.name === 'length') {
|
|
1023
958
|
if (left.property.name !== '$length') {
|
|
1024
959
|
return b.call(
|
|
1025
960
|
'$.old_set_property',
|
|
@@ -1095,9 +1030,8 @@ const visitors = {
|
|
|
1095
1030
|
|
|
1096
1031
|
if (
|
|
1097
1032
|
argument.type === 'MemberExpression' &&
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
is_tracked_computed_property(argument.object, argument.property, context)))
|
|
1033
|
+
argument.computed &&
|
|
1034
|
+
is_tracked_computed_property(argument.object, argument.property, context)
|
|
1101
1035
|
) {
|
|
1102
1036
|
return b.call(
|
|
1103
1037
|
node.prefix ? '$.old_update_pre_property' : '$.old_update_property',
|
|
@@ -1131,78 +1065,6 @@ const visitors = {
|
|
|
1131
1065
|
context.next();
|
|
1132
1066
|
},
|
|
1133
1067
|
|
|
1134
|
-
ObjectExpression(node, context) {
|
|
1135
|
-
const properties = [];
|
|
1136
|
-
const tracked = [];
|
|
1137
|
-
|
|
1138
|
-
for (const property of node.properties) {
|
|
1139
|
-
if (
|
|
1140
|
-
property.type === 'Property' &&
|
|
1141
|
-
!property.computed &&
|
|
1142
|
-
property.key.type === 'Identifier' &&
|
|
1143
|
-
property.kind === 'init' &&
|
|
1144
|
-
is_tracked_name(property.key.name)
|
|
1145
|
-
) {
|
|
1146
|
-
tracked.push(b.literal(property.key.name));
|
|
1147
|
-
const metadata = { tracking: false, await: false };
|
|
1148
|
-
const tracked_property = context.visit(property, { ...context.state, metadata });
|
|
1149
|
-
|
|
1150
|
-
if (metadata.tracking) {
|
|
1151
|
-
properties.push({
|
|
1152
|
-
...tracked_property,
|
|
1153
|
-
value: b.call('$.computed_property', b.thunk(tracked_property.value), b.id('__block')),
|
|
1154
|
-
});
|
|
1155
|
-
} else {
|
|
1156
|
-
properties.push(tracked_property);
|
|
1157
|
-
}
|
|
1158
|
-
} else {
|
|
1159
|
-
properties.push(context.visit(property));
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
if (tracked.length > 0) {
|
|
1164
|
-
return b.call('$.tracked_object', { ...node, properties }, b.array(tracked), b.id('__block'));
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
context.next();
|
|
1168
|
-
},
|
|
1169
|
-
|
|
1170
|
-
ArrayExpression(node, context) {
|
|
1171
|
-
// TODO we can bail out of all of this if we know we're inside a computed fn expression
|
|
1172
|
-
// as the reactivity will hold from the reference of the $ binding itself
|
|
1173
|
-
const elements = [];
|
|
1174
|
-
const tracked = [];
|
|
1175
|
-
let i = 0;
|
|
1176
|
-
|
|
1177
|
-
for (const element of node.elements) {
|
|
1178
|
-
if (element === null) {
|
|
1179
|
-
elements.push(null);
|
|
1180
|
-
} else if (element.type === 'Identifier' && is_tracked_name(element.name)) {
|
|
1181
|
-
const metadata = { tracking: false, await: false };
|
|
1182
|
-
const tracked_identifier = context.visit(element, { ...context.state, metadata });
|
|
1183
|
-
|
|
1184
|
-
if (metadata.tracking) {
|
|
1185
|
-
tracked.push(b.literal(i));
|
|
1186
|
-
elements.push(
|
|
1187
|
-
b.call('$.computed_property', b.thunk(tracked_identifier), b.id('__block')),
|
|
1188
|
-
);
|
|
1189
|
-
} else {
|
|
1190
|
-
elements.push(tracked_identifier);
|
|
1191
|
-
}
|
|
1192
|
-
} else {
|
|
1193
|
-
const metadata = { tracking: false, await: false };
|
|
1194
|
-
elements.push(context.visit(element, { ...context.state, metadata }));
|
|
1195
|
-
}
|
|
1196
|
-
i++;
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
if (tracked.length > 0) {
|
|
1200
|
-
return b.call('$.tracked_object', { ...node, elements }, b.array(tracked), b.id('__block'));
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
context.next();
|
|
1204
|
-
},
|
|
1205
|
-
|
|
1206
1068
|
ForOfStatement(node, context) {
|
|
1207
1069
|
if (!is_inside_component(context)) {
|
|
1208
1070
|
context.next();
|
|
@@ -1323,7 +1185,7 @@ const visitors = {
|
|
|
1323
1185
|
state: { ...context.state, metadata },
|
|
1324
1186
|
});
|
|
1325
1187
|
|
|
1326
|
-
if (metadata.
|
|
1188
|
+
if (metadata.pending) {
|
|
1327
1189
|
body = [b.stmt(b.call('$.async', b.thunk(b.block(body), true)))];
|
|
1328
1190
|
}
|
|
1329
1191
|
|
|
@@ -1339,24 +1201,24 @@ const visitors = {
|
|
|
1339
1201
|
[b.id('__anchor'), ...(node.handler.param ? [node.handler.param] : [])],
|
|
1340
1202
|
b.block(transform_body(node.handler.body.body, context)),
|
|
1341
1203
|
),
|
|
1342
|
-
node.
|
|
1204
|
+
node.pending === null
|
|
1343
1205
|
? undefined
|
|
1344
|
-
: b.arrow([b.id('__anchor')], b.block(transform_body(node.
|
|
1206
|
+
: b.arrow([b.id('__anchor')], b.block(transform_body(node.pending.body, context))),
|
|
1345
1207
|
),
|
|
1346
1208
|
),
|
|
1347
1209
|
);
|
|
1348
1210
|
},
|
|
1349
1211
|
|
|
1350
1212
|
AwaitExpression(node, context) {
|
|
1351
|
-
if (!
|
|
1352
|
-
context.next();
|
|
1213
|
+
if (!is_top_level_await(context) || context.state.to_ts) {
|
|
1214
|
+
return context.next();
|
|
1353
1215
|
}
|
|
1354
1216
|
|
|
1355
1217
|
if (context.state.metadata?.await === false) {
|
|
1356
1218
|
context.state.metadata.await = true;
|
|
1357
1219
|
}
|
|
1358
1220
|
|
|
1359
|
-
return b.call(b.await(b.call('$.
|
|
1221
|
+
return b.call(b.await(b.call('$.maybe_tracked', context.visit(node.argument))));
|
|
1360
1222
|
},
|
|
1361
1223
|
|
|
1362
1224
|
BinaryExpression(node, context) {
|
|
@@ -1372,24 +1234,6 @@ const visitors = {
|
|
|
1372
1234
|
return b.template(node.quasis, expressions);
|
|
1373
1235
|
},
|
|
1374
1236
|
|
|
1375
|
-
RenderFragment(node, context) {
|
|
1376
|
-
const identifer = node.expression.callee;
|
|
1377
|
-
|
|
1378
|
-
context.state.template.push('<!>');
|
|
1379
|
-
|
|
1380
|
-
const id = context.state.flush_node();
|
|
1381
|
-
|
|
1382
|
-
context.state.init.push(
|
|
1383
|
-
b.stmt(
|
|
1384
|
-
b.call(
|
|
1385
|
-
context.visit(identifer),
|
|
1386
|
-
id,
|
|
1387
|
-
...node.expression.arguments.map((arg) => context.visit(arg, context.state)),
|
|
1388
|
-
),
|
|
1389
|
-
),
|
|
1390
|
-
);
|
|
1391
|
-
},
|
|
1392
|
-
|
|
1393
1237
|
BlockStatement(node, context) {
|
|
1394
1238
|
const statements = [];
|
|
1395
1239
|
|
|
@@ -1630,17 +1474,6 @@ function transform_ts_child(node, context) {
|
|
|
1630
1474
|
}
|
|
1631
1475
|
|
|
1632
1476
|
state.init.push(b.try(try_body, catch_handler, finally_block));
|
|
1633
|
-
} else if (node.type === 'RenderFragment') {
|
|
1634
|
-
const identifer = node.expression.callee;
|
|
1635
|
-
|
|
1636
|
-
state.init.push(
|
|
1637
|
-
b.stmt(
|
|
1638
|
-
b.call(
|
|
1639
|
-
context.visit(identifer),
|
|
1640
|
-
...node.expression.arguments.map((arg) => context.visit(arg, context.state)),
|
|
1641
|
-
),
|
|
1642
|
-
),
|
|
1643
|
-
);
|
|
1644
1477
|
} else if (node.type === 'Component') {
|
|
1645
1478
|
const component = visit(node, context.state);
|
|
1646
1479
|
|
|
@@ -1665,7 +1498,6 @@ function transform_children(children, context) {
|
|
|
1665
1498
|
node.type === 'IfStatement' ||
|
|
1666
1499
|
node.type === 'TryStatement' ||
|
|
1667
1500
|
node.type === 'ForOfStatement' ||
|
|
1668
|
-
node.type === 'RenderFragment' ||
|
|
1669
1501
|
(node.type === 'Element' &&
|
|
1670
1502
|
(node.id.type !== 'Identifier' || !is_element_dom_element(node, context))),
|
|
1671
1503
|
) ||
|
|
@@ -1711,6 +1543,7 @@ function transform_children(children, context) {
|
|
|
1711
1543
|
if (
|
|
1712
1544
|
node.type === 'VariableDeclaration' ||
|
|
1713
1545
|
node.type === 'ExpressionStatement' ||
|
|
1546
|
+
node.type === 'ThrowStatement' ||
|
|
1714
1547
|
node.type === 'FunctionDeclaration' ||
|
|
1715
1548
|
node.type === 'DebuggerStatement' ||
|
|
1716
1549
|
node.type === 'ClassDeclaration'
|
|
@@ -1774,6 +1607,9 @@ function transform_children(children, context) {
|
|
|
1774
1607
|
state.template.push(' ');
|
|
1775
1608
|
const id = flush_node();
|
|
1776
1609
|
state.update.push(b.stmt(b.call('$.set_text', id, expression)));
|
|
1610
|
+
if (metadata.await) {
|
|
1611
|
+
state.update.async = true;
|
|
1612
|
+
}
|
|
1777
1613
|
} else if (normalized.length === 1) {
|
|
1778
1614
|
if (expression.type === 'Literal') {
|
|
1779
1615
|
state.template.push(escape_html(expression.value));
|
|
@@ -1789,6 +1625,9 @@ function transform_children(children, context) {
|
|
|
1789
1625
|
state.template.push(' ');
|
|
1790
1626
|
const id = flush_node();
|
|
1791
1627
|
state.update.push(b.stmt(b.call('$.set_text', id, expression)));
|
|
1628
|
+
if (metadata.await) {
|
|
1629
|
+
state.update.async = true;
|
|
1630
|
+
}
|
|
1792
1631
|
}
|
|
1793
1632
|
} else if (node.type === 'ForOfStatement') {
|
|
1794
1633
|
const is_controlled = normalized.length === 1;
|
|
@@ -1802,10 +1641,6 @@ function transform_children(children, context) {
|
|
|
1802
1641
|
const is_controlled = normalized.length === 1;
|
|
1803
1642
|
node.is_controlled = is_controlled;
|
|
1804
1643
|
visit(node, { ...state, flush_node });
|
|
1805
|
-
} else if (node.type === 'RenderFragment') {
|
|
1806
|
-
const is_controlled = normalized.length === 1;
|
|
1807
|
-
node.is_controlled = is_controlled;
|
|
1808
|
-
visit(node, { ...state, flush_node });
|
|
1809
1644
|
} else {
|
|
1810
1645
|
debugger;
|
|
1811
1646
|
}
|
package/src/compiler/utils.js
CHANGED
|
@@ -354,6 +354,30 @@ export function build_hoisted_params(node, context) {
|
|
|
354
354
|
return params;
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
+
export function is_top_level_await(context) {
|
|
358
|
+
if (!is_inside_component(context)) {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
for (let i = context.path.length - 1; i >= 0; i -= 1) {
|
|
363
|
+
const context_node = context.path[i];
|
|
364
|
+
const type = context_node.type;
|
|
365
|
+
|
|
366
|
+
if (type === 'Component') {
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (
|
|
371
|
+
type === 'FunctionExpression' ||
|
|
372
|
+
type === 'ArrowFunctionExpression' ||
|
|
373
|
+
type === 'FunctionDeclaration'
|
|
374
|
+
) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
|
|
357
381
|
export function is_inside_component(context, includes_functions = false) {
|
|
358
382
|
for (let i = context.path.length - 1; i >= 0; i -= 1) {
|
|
359
383
|
const context_node = context.path[i];
|
|
@@ -413,10 +437,6 @@ export function is_inside_call_expression(context) {
|
|
|
413
437
|
return false;
|
|
414
438
|
}
|
|
415
439
|
|
|
416
|
-
export function is_tracked_name(name) {
|
|
417
|
-
return typeof name === 'string' && name.startsWith('$') && name.length > 1 && name[1] !== '$';
|
|
418
|
-
}
|
|
419
|
-
|
|
420
440
|
export function is_value_static(node) {
|
|
421
441
|
if (node.type === 'Literal') {
|
|
422
442
|
return true;
|
|
@@ -449,13 +469,6 @@ export function is_tracked_computed_property(object, property, context) {
|
|
|
449
469
|
if (property.type === 'Identifier') {
|
|
450
470
|
return true;
|
|
451
471
|
}
|
|
452
|
-
if (
|
|
453
|
-
property.type === 'Literal' &&
|
|
454
|
-
typeof property.value === 'string' &&
|
|
455
|
-
is_tracked_name(property.value)
|
|
456
|
-
) {
|
|
457
|
-
return true;
|
|
458
|
-
}
|
|
459
472
|
|
|
460
473
|
// TODO: do we need to handle more logic here? default to false for now
|
|
461
474
|
return true;
|
|
@@ -21,7 +21,6 @@ export var LOGIC_BLOCK = FOR_BLOCK | IF_BLOCK | TRY_BLOCK;
|
|
|
21
21
|
export var UNINITIALIZED = Symbol();
|
|
22
22
|
/** @type {unique symbol} */
|
|
23
23
|
export const TRACKED_OBJECT = Symbol();
|
|
24
|
-
export var SPREAD_OBJECT = Symbol();
|
|
25
24
|
export var COMPUTED_PROPERTY = Symbol();
|
|
26
25
|
export var REF_PROP = 'ref';
|
|
27
26
|
/** @type {unique symbol} */
|
|
@@ -50,12 +50,13 @@ export {
|
|
|
50
50
|
fallback,
|
|
51
51
|
exclude_from_object,
|
|
52
52
|
derived,
|
|
53
|
+
maybe_tracked,
|
|
53
54
|
} from './runtime.js';
|
|
54
55
|
|
|
55
56
|
export { for_block as for } from './for.js';
|
|
56
57
|
|
|
57
58
|
export { if_block as if } from './if.js';
|
|
58
59
|
|
|
59
|
-
export { try_block as try,
|
|
60
|
+
export { try_block as try, aborted } from './try.js';
|
|
60
61
|
|
|
61
62
|
export { template, append } from './template.js';
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
EFFECT_BLOCK,
|
|
21
21
|
PAUSED,
|
|
22
22
|
ROOT_BLOCK,
|
|
23
|
-
SPREAD_OBJECT,
|
|
24
23
|
TRACKED,
|
|
25
24
|
TRACKED_OBJECT,
|
|
26
25
|
TRY_BLOCK,
|
|
@@ -79,7 +78,7 @@ export function set_active_block(block) {
|
|
|
79
78
|
}
|
|
80
79
|
|
|
81
80
|
/**
|
|
82
|
-
* @param {Block | null} reaction
|
|
81
|
+
* @param {Block | Derived | null} reaction
|
|
83
82
|
*/
|
|
84
83
|
export function set_active_reaction(reaction) {
|
|
85
84
|
active_reaction = reaction;
|
|
@@ -279,7 +278,7 @@ export function derived(fn, block) {
|
|
|
279
278
|
b: block,
|
|
280
279
|
blocks: null,
|
|
281
280
|
c: 0,
|
|
282
|
-
|
|
281
|
+
co: active_component,
|
|
283
282
|
d: null,
|
|
284
283
|
f: TRACKED | DERIVED,
|
|
285
284
|
fn,
|
|
@@ -781,17 +780,18 @@ export function flush_sync(fn) {
|
|
|
781
780
|
|
|
782
781
|
/**
|
|
783
782
|
* @param {() => Object} fn
|
|
783
|
+
* @param {Block} block
|
|
784
784
|
* @returns {Object}
|
|
785
785
|
*/
|
|
786
|
-
export function tracked_spread_object(fn) {
|
|
787
|
-
|
|
786
|
+
export function tracked_spread_object(fn, block) {
|
|
787
|
+
let computed = derived(fn, block);
|
|
788
788
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
789
|
+
return new Proxy({}, {
|
|
790
|
+
get(target, property) {
|
|
791
|
+
const obj = get_derived(computed);
|
|
792
|
+
return obj[property];
|
|
793
|
+
}
|
|
792
794
|
});
|
|
793
|
-
|
|
794
|
-
return obj;
|
|
795
795
|
}
|
|
796
796
|
|
|
797
797
|
/**
|
|
@@ -894,7 +894,7 @@ export function get_property(obj, property, chain = false) {
|
|
|
894
894
|
}
|
|
895
895
|
var tracked = obj[property];
|
|
896
896
|
if (tracked == null) {
|
|
897
|
-
|
|
897
|
+
return tracked;
|
|
898
898
|
}
|
|
899
899
|
return get(tracked);
|
|
900
900
|
}
|
|
@@ -918,10 +918,6 @@ export function old_get_property(obj, property, chain = false) {
|
|
|
918
918
|
if (obj[property] !== value) {
|
|
919
919
|
obj[property] = value;
|
|
920
920
|
}
|
|
921
|
-
} else if (SPREAD_OBJECT in obj) {
|
|
922
|
-
var spread_fn = obj[SPREAD_OBJECT];
|
|
923
|
-
var properties = spread_fn();
|
|
924
|
-
return old_get_property(properties, property, chain);
|
|
925
921
|
} else if (is_ripple_array(obj)) {
|
|
926
922
|
obj.$length;
|
|
927
923
|
}
|
|
@@ -1266,3 +1262,23 @@ export function exclude_from_object(obj, keys) {
|
|
|
1266
1262
|
}
|
|
1267
1263
|
return obj;
|
|
1268
1264
|
}
|
|
1265
|
+
|
|
1266
|
+
export async function maybe_tracked(v) {
|
|
1267
|
+
var restore = capture();
|
|
1268
|
+
let value;
|
|
1269
|
+
|
|
1270
|
+
if (typeof v === 'object' && v !== null && typeof v.f === 'number') {
|
|
1271
|
+
if ((v.f & DERIVED) !== 0) {
|
|
1272
|
+
value = await async_computed(v.fn, v.b);
|
|
1273
|
+
} else {
|
|
1274
|
+
value = await get_tracked(v);
|
|
1275
|
+
}
|
|
1276
|
+
} else {
|
|
1277
|
+
value = await v;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
return () => {
|
|
1281
|
+
restore();
|
|
1282
|
+
return value;
|
|
1283
|
+
};
|
|
1284
|
+
}
|
package/tests/array.test.ripple
CHANGED
|
@@ -514,11 +514,11 @@ describe('RippleArray', () => {
|
|
|
514
514
|
it('handles array slice method with reactivity', () => {
|
|
515
515
|
component ArrayTest() {
|
|
516
516
|
let items = new RippleArray(1, 2, 3, 4, 5);
|
|
517
|
-
let
|
|
517
|
+
let sliced = track(() => items.slice(1, 4));
|
|
518
518
|
|
|
519
519
|
<button onClick={() => items[2] = 30}>{'change middle'}</button>
|
|
520
520
|
<pre>{JSON.stringify(items)}</pre>
|
|
521
|
-
<pre>{JSON.stringify(
|
|
521
|
+
<pre>{JSON.stringify(@sliced)}</pre>
|
|
522
522
|
}
|
|
523
523
|
|
|
524
524
|
render(ArrayTest);
|
|
@@ -1249,7 +1249,7 @@ describe('RippleArray', () => {
|
|
|
1249
1249
|
component Parent() {
|
|
1250
1250
|
try {
|
|
1251
1251
|
<ArrayTest />
|
|
1252
|
-
}
|
|
1252
|
+
} pending {
|
|
1253
1253
|
<div>{'Loading placeholder...'}</div>
|
|
1254
1254
|
}
|
|
1255
1255
|
}
|
|
@@ -1284,7 +1284,7 @@ describe('RippleArray', () => {
|
|
|
1284
1284
|
component Parent() {
|
|
1285
1285
|
try {
|
|
1286
1286
|
<ArrayTest />
|
|
1287
|
-
}
|
|
1287
|
+
} pending {
|
|
1288
1288
|
<div>{'Loading placeholder...'}</div>
|
|
1289
1289
|
}
|
|
1290
1290
|
}
|
|
@@ -1316,21 +1316,32 @@ describe('RippleArray', () => {
|
|
|
1316
1316
|
expect(container.querySelector('pre').textContent).toBe('[2,4,6,8]');
|
|
1317
1317
|
});
|
|
1318
1318
|
|
|
1319
|
-
|
|
1319
|
+
// TODO: Fix this test case, needs some async love around try statements being using in a not template way
|
|
1320
|
+
it.skip('handles error in fromAsync method', async () => {
|
|
1320
1321
|
component Parent() {
|
|
1321
1322
|
try {
|
|
1322
1323
|
<ArrayTest />
|
|
1323
|
-
}
|
|
1324
|
+
} pending {
|
|
1324
1325
|
<div>{'Loading placeholder...'}</div>
|
|
1325
1326
|
}
|
|
1326
1327
|
}
|
|
1327
1328
|
|
|
1329
|
+
component ArrayTest() {
|
|
1330
|
+
let items = null;
|
|
1331
|
+
let error = null;
|
|
1332
|
+
|
|
1333
|
+
// try {
|
|
1334
|
+
// items = await RippleArray.fromAsync(Promise.reject(new Error('Async error')));
|
|
1335
|
+
// } catch (e) {
|
|
1336
|
+
// }
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1328
1339
|
component ArrayTest() {
|
|
1329
1340
|
let items = null;
|
|
1330
1341
|
let error = null;
|
|
1331
1342
|
|
|
1332
1343
|
try {
|
|
1333
|
-
items = await RippleArray.fromAsync(Promise.reject(new Error('Async error')));
|
|
1344
|
+
// items = await RippleArray.fromAsync(Promise.reject(new Error('Async error')));
|
|
1334
1345
|
} catch (e) {
|
|
1335
1346
|
error = e.message;
|
|
1336
1347
|
}
|
|
@@ -1530,13 +1541,13 @@ describe('RippleArray', () => {
|
|
|
1530
1541
|
component Parent() {
|
|
1531
1542
|
try {
|
|
1532
1543
|
<ArrayTest />
|
|
1533
|
-
}
|
|
1544
|
+
} pending {
|
|
1534
1545
|
<div>{'Loading placeholder...'}</div>
|
|
1535
1546
|
}
|
|
1536
1547
|
}
|
|
1537
1548
|
|
|
1538
1549
|
component ArrayTest() {
|
|
1539
|
-
const items = await RippleArray.fromAsync([6]);
|
|
1550
|
+
//const items = await RippleArray.fromAsync([6]);
|
|
1540
1551
|
|
|
1541
1552
|
<pre>{items ? JSON.stringify(items) : 'Loading...'}</pre>
|
|
1542
1553
|
<pre>{items ? items.$length : ''}</pre>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { mount, flushSync, track, RippleArray } from 'ripple';
|
|
2
|
+
import { mount, flushSync, track, RippleArray, effect } from 'ripple';
|
|
3
3
|
|
|
4
4
|
describe('composite components', () => {
|
|
5
5
|
let container;
|
|
@@ -295,4 +295,45 @@ describe('composite components', () => {
|
|
|
295
295
|
|
|
296
296
|
render(ArrayTest);
|
|
297
297
|
});
|
|
298
|
+
|
|
299
|
+
it('handles spreading of props', () => {
|
|
300
|
+
let logs = [];
|
|
301
|
+
|
|
302
|
+
component App() {
|
|
303
|
+
const a = track(1);
|
|
304
|
+
const b = track(2);
|
|
305
|
+
const c = track(3);
|
|
306
|
+
|
|
307
|
+
const obj = track(() => ({
|
|
308
|
+
@a,
|
|
309
|
+
@b,
|
|
310
|
+
@c,
|
|
311
|
+
}));
|
|
312
|
+
|
|
313
|
+
<Child {...@obj} />
|
|
314
|
+
|
|
315
|
+
<button onClick={() => { @a++; @b++; @c++; }}>{"Increment all"}</button>
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
component Child({ a, b, c }) {
|
|
319
|
+
effect(() => {
|
|
320
|
+
logs.push(`Child effect: ${a}, ${b}, ${c}`);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
<div>{a + ' ' + b + ' ' + c}</div>
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
render(App);
|
|
327
|
+
flushSync();
|
|
328
|
+
|
|
329
|
+
expect(container.querySelector('div').textContent).toBe('1 2 3');
|
|
330
|
+
expect(logs).toEqual(['Child effect: 1, 2, 3']);
|
|
331
|
+
|
|
332
|
+
const button = container.querySelector('button');
|
|
333
|
+
button.click();
|
|
334
|
+
flushSync();
|
|
335
|
+
|
|
336
|
+
expect(container.querySelector('div').textContent).toBe('2 3 4');
|
|
337
|
+
expect(logs).toEqual(['Child effect: 1, 2, 3', 'Child effect: 2, 3, 4']);
|
|
338
|
+
});
|
|
298
339
|
});
|