ripple 0.2.121 → 0.2.125
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/index.js +1 -1
- package/src/compiler/phases/1-parse/index.js +1 -0
- package/src/compiler/phases/2-analyze/index.js +66 -32
- package/src/compiler/phases/3-transform/client/index.js +19 -0
- package/src/compiler/phases/3-transform/server/index.js +50 -37
- package/src/runtime/internal/client/composite.js +2 -4
- package/src/runtime/internal/client/script.js +1 -1
- package/src/runtime/internal/server/index.js +22 -13
- package/src/utils/builders.js +2 -2
- package/tests/client/__snapshots__/for.test.ripple.snap +0 -80
- package/tests/client/basic/__snapshots__/basic.rendering.test.ripple.snap +0 -48
- package/tests/client/basic/basic.errors.test.ripple +16 -0
- package/tests/client/basic/basic.get-set.test.ripple +291 -0
- package/tests/client/dynamic-elements.test.ripple +210 -8
- package/tests/server/await.test.ripple +61 -0
- package/tests/server/for.test.ripple +44 -0
- package/tests/server/if.test.ripple +21 -1
- package/tests/utils/escaping.test.js +102 -0
- package/tests/utils/events.test.js +147 -0
- package/tests/utils/normalize_css_property_name.test.js +43 -0
- package/tests/utils/patterns.test.js +382 -0
- package/tests/utils/sanitize_template_string.test.js +51 -0
- package/types/server.d.ts +25 -3
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Ripple is an elegant TypeScript UI framework",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Dominic Gannaway",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.125",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"module": "src/runtime/index-client.js",
|
|
9
9
|
"main": "src/runtime/index-client.js",
|
package/src/compiler/index.js
CHANGED
|
@@ -24,7 +24,7 @@ export function parse(source) {
|
|
|
24
24
|
*/
|
|
25
25
|
export function compile(source, filename, options = {}) {
|
|
26
26
|
const ast = parse_module(source);
|
|
27
|
-
const analysis = analyze(ast, filename);
|
|
27
|
+
const analysis = analyze(ast, filename, options);
|
|
28
28
|
const result = options.mode === 'server'
|
|
29
29
|
? transform_server(filename, source, analysis)
|
|
30
30
|
: transform_client(filename, source, analysis, false);
|
|
@@ -1392,6 +1392,7 @@ function RipplePlugin(config) {
|
|
|
1392
1392
|
this.next();
|
|
1393
1393
|
this.enterScope(0);
|
|
1394
1394
|
node.id = this.parseIdent();
|
|
1395
|
+
this.declareName(node.id.name, 'var', node.id.start);
|
|
1395
1396
|
this.parseFunctionParams(node);
|
|
1396
1397
|
this.eat(tt.braceL);
|
|
1397
1398
|
node.body = [];
|
|
@@ -111,7 +111,8 @@ const visitors = {
|
|
|
111
111
|
if (
|
|
112
112
|
is_reference(node, /** @type {Node} */ (parent)) &&
|
|
113
113
|
binding &&
|
|
114
|
-
context.state.inside_server_block
|
|
114
|
+
context.state.inside_server_block &&
|
|
115
|
+
context.state.scope.server_block
|
|
115
116
|
) {
|
|
116
117
|
let current_scope = binding.scope;
|
|
117
118
|
|
|
@@ -349,13 +350,14 @@ const visitors = {
|
|
|
349
350
|
node.metadata = {
|
|
350
351
|
...node.metadata,
|
|
351
352
|
has_template: false,
|
|
353
|
+
has_await: false,
|
|
352
354
|
};
|
|
353
355
|
|
|
354
356
|
context.visit(switch_case, context.state);
|
|
355
357
|
|
|
356
|
-
if (!node.metadata.has_template) {
|
|
358
|
+
if (!node.metadata.has_template && !node.metadata.has_await) {
|
|
357
359
|
error(
|
|
358
|
-
'Component switch statements must contain a template in each of their cases. Move the switch statement into an effect if it does not render anything.',
|
|
360
|
+
'Component switch statements must contain a template or an await expression in each of their cases. Move the switch statement into an effect if it does not render anything.',
|
|
359
361
|
context.state.analysis.module.filename,
|
|
360
362
|
node,
|
|
361
363
|
);
|
|
@@ -416,15 +418,16 @@ const visitors = {
|
|
|
416
418
|
node.metadata = {
|
|
417
419
|
...node.metadata,
|
|
418
420
|
has_template: false,
|
|
421
|
+
has_await: false,
|
|
419
422
|
};
|
|
420
423
|
context.next();
|
|
421
424
|
|
|
422
|
-
if (!node.metadata.has_template) {
|
|
425
|
+
if (!node.metadata.has_template && !node.metadata.has_await) {
|
|
423
426
|
error(
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
427
|
+
'Component for...of loops must contain a template or an await expression in their body. Move the for loop into an effect if it does not render anything.',
|
|
428
|
+
context.state.analysis.module.filename,
|
|
429
|
+
node,
|
|
430
|
+
);
|
|
428
431
|
}
|
|
429
432
|
},
|
|
430
433
|
|
|
@@ -437,9 +440,14 @@ const visitors = {
|
|
|
437
440
|
|
|
438
441
|
if (declaration && declaration.type === 'FunctionDeclaration') {
|
|
439
442
|
server_block.metadata.exports.push(declaration.id.name);
|
|
443
|
+
} else if (declaration && declaration.type === 'Component') {
|
|
444
|
+
// Handle exported components in server blocks
|
|
445
|
+
if (server_block) {
|
|
446
|
+
server_block.metadata.exports.push(declaration.id.name);
|
|
447
|
+
}
|
|
440
448
|
} else {
|
|
441
449
|
// TODO
|
|
442
|
-
throw new Error('Not implemented');
|
|
450
|
+
throw new Error('Not implemented: Exported declaration type not supported in server blocks.');
|
|
443
451
|
}
|
|
444
452
|
|
|
445
453
|
return context.next();
|
|
@@ -462,35 +470,39 @@ const visitors = {
|
|
|
462
470
|
node.metadata = {
|
|
463
471
|
...node.metadata,
|
|
464
472
|
has_template: false,
|
|
473
|
+
has_await: false,
|
|
465
474
|
};
|
|
466
475
|
|
|
467
476
|
context.visit(node.consequent, context.state);
|
|
468
477
|
|
|
469
|
-
if (!node.metadata.has_template) {
|
|
478
|
+
if (!node.metadata.has_template && !node.metadata.has_await) {
|
|
470
479
|
error(
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
480
|
+
'Component if statements must contain a template or an await expression in their "then" body. Move the if statement into an effect if it does not render anything.',
|
|
481
|
+
context.state.analysis.module.filename,
|
|
482
|
+
node,
|
|
483
|
+
);
|
|
475
484
|
}
|
|
476
485
|
|
|
477
486
|
if (node.alternate) {
|
|
478
|
-
node.metadata =
|
|
479
|
-
|
|
480
|
-
has_template: false,
|
|
481
|
-
};
|
|
487
|
+
node.metadata.has_template = false;
|
|
488
|
+
node.metadata.has_await = false;
|
|
482
489
|
context.visit(node.alternate, context.state);
|
|
483
490
|
|
|
484
|
-
if (!node.metadata.has_template) {
|
|
491
|
+
if (!node.metadata.has_template && !node.metadata.has_await) {
|
|
485
492
|
error(
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
493
|
+
'Component if statements must contain a template or an await expression in their "else" body. Move the if statement into an effect if it does not render anything.',
|
|
494
|
+
context.state.analysis.module.filename,
|
|
495
|
+
node,
|
|
496
|
+
);
|
|
490
497
|
}
|
|
491
498
|
}
|
|
492
499
|
},
|
|
493
|
-
|
|
500
|
+
/**
|
|
501
|
+
*
|
|
502
|
+
* @param {any} node
|
|
503
|
+
* @param {any} context
|
|
504
|
+
* @returns
|
|
505
|
+
*/
|
|
494
506
|
TryStatement(node, context) {
|
|
495
507
|
if (!is_inside_component(context)) {
|
|
496
508
|
return context.next();
|
|
@@ -582,6 +594,14 @@ const visitors = {
|
|
|
582
594
|
}
|
|
583
595
|
binding.metadata.is_dynamic_component = true;
|
|
584
596
|
}
|
|
597
|
+
|
|
598
|
+
if (!is_dom_element && state.elements) {
|
|
599
|
+
state.elements.push(node);
|
|
600
|
+
// Mark dynamic elements as scoped by default since we can't match CSS at compile time
|
|
601
|
+
if (state.component?.css) {
|
|
602
|
+
node.metadata.scoped = true;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
585
605
|
}
|
|
586
606
|
|
|
587
607
|
if (is_dom_element) {
|
|
@@ -736,7 +756,12 @@ const visitors = {
|
|
|
736
756
|
mark_control_flow_has_template(context.path);
|
|
737
757
|
context.next();
|
|
738
758
|
},
|
|
739
|
-
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
*
|
|
762
|
+
* @param {any} node
|
|
763
|
+
* @param {any} context
|
|
764
|
+
*/
|
|
740
765
|
AwaitExpression(node, context) {
|
|
741
766
|
if (is_inside_component(context)) {
|
|
742
767
|
if (context.state.metadata?.await === false) {
|
|
@@ -746,18 +771,27 @@ const visitors = {
|
|
|
746
771
|
const parent_block = get_parent_block_node(context);
|
|
747
772
|
|
|
748
773
|
if (parent_block !== null && parent_block.type !== 'Component') {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
774
|
+
if (context.state.inside_server_block === false) {
|
|
775
|
+
error(
|
|
776
|
+
'`await` is not allowed in client-side control-flow statements',
|
|
777
|
+
context.state.analysis.module.filename,
|
|
778
|
+
node
|
|
779
|
+
);
|
|
780
|
+
}
|
|
754
781
|
}
|
|
755
782
|
|
|
783
|
+
if (parent_block) {
|
|
784
|
+
if (!parent_block.metadata) {
|
|
785
|
+
parent_block.metadata = {};
|
|
786
|
+
}
|
|
787
|
+
parent_block.metadata.has_await = true;
|
|
788
|
+
}
|
|
789
|
+
|
|
756
790
|
context.next();
|
|
757
791
|
},
|
|
758
792
|
};
|
|
759
793
|
|
|
760
|
-
export function analyze(ast, filename) {
|
|
794
|
+
export function analyze(ast, filename, options = {}) {
|
|
761
795
|
const scope_root = new ScopeRoot();
|
|
762
796
|
|
|
763
797
|
const { scope, scopes } = create_scopes(ast, scope_root, null);
|
|
@@ -777,7 +811,7 @@ export function analyze(ast, filename) {
|
|
|
777
811
|
scopes,
|
|
778
812
|
analysis,
|
|
779
813
|
inside_head: false,
|
|
780
|
-
inside_server_block:
|
|
814
|
+
inside_server_block: options.mode === 'server',
|
|
781
815
|
},
|
|
782
816
|
visitors,
|
|
783
817
|
);
|
|
@@ -840,6 +840,14 @@ const visitors = {
|
|
|
840
840
|
let property =
|
|
841
841
|
attr.value === null ? b.literal(true) : visit(attr.value, { ...state, metadata });
|
|
842
842
|
|
|
843
|
+
if (attr.name.name === 'class' && node.metadata.scoped && state.component.css) {
|
|
844
|
+
if (property.type === 'Literal') {
|
|
845
|
+
property = b.literal(`${state.component.css.hash} ${property.value}`);
|
|
846
|
+
} else {
|
|
847
|
+
property = b.array([property, b.literal(state.component.css.hash)]);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
843
851
|
if (metadata.tracking || attr.name.tracked) {
|
|
844
852
|
if (attr.name.name === 'children') {
|
|
845
853
|
children_prop = b.thunk(property);
|
|
@@ -872,6 +880,17 @@ const visitors = {
|
|
|
872
880
|
}
|
|
873
881
|
}
|
|
874
882
|
|
|
883
|
+
if (node.metadata.scoped && state.component.css) {
|
|
884
|
+
const hasClassAttr = node.attributes.some(attr =>
|
|
885
|
+
attr.type === 'Attribute' && attr.name.type === 'Identifier' && attr.name.name === 'class'
|
|
886
|
+
);
|
|
887
|
+
if (!hasClassAttr) {
|
|
888
|
+
const name = is_spreading ? '#class' : 'class';
|
|
889
|
+
const value = state.component.css.hash;
|
|
890
|
+
props.push(b.prop('init', b.key(name), b.literal(value)));
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
875
894
|
const children_filtered = [];
|
|
876
895
|
|
|
877
896
|
for (const child of node.children) {
|
|
@@ -120,7 +120,18 @@ const visitors = {
|
|
|
120
120
|
component_fn = b.async(component_fn);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
const declaration = b.function_declaration(node.id, component_fn.params, component_fn.body, component_fn.async);
|
|
124
|
+
|
|
125
|
+
if (metadata.await) {
|
|
126
|
+
const parent = context.path.at(-1);
|
|
127
|
+
if (parent.type === 'Program' || parent.type === 'BlockStatement') {
|
|
128
|
+
const body = parent.body;
|
|
129
|
+
const index = body.indexOf(node);
|
|
130
|
+
body.splice(index + 1, 0, b.stmt(b.assignment('=', b.member(node.id, b.id('async')), b.true)));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return declaration;
|
|
124
135
|
},
|
|
125
136
|
|
|
126
137
|
CallExpression(node, context) {
|
|
@@ -270,11 +281,10 @@ const visitors = {
|
|
|
270
281
|
let class_attribute = null;
|
|
271
282
|
|
|
272
283
|
const handle_static_attr = (name, value) => {
|
|
273
|
-
const attr_str = ` ${name}${
|
|
274
|
-
is_boolean_attribute(name) && value === true
|
|
284
|
+
const attr_str = ` ${name}${is_boolean_attribute(name) && value === true
|
|
275
285
|
? ''
|
|
276
286
|
: `="${value === true ? '' : escape_html(value, true)}"`
|
|
277
|
-
|
|
287
|
+
}`;
|
|
278
288
|
|
|
279
289
|
if (is_spreading) {
|
|
280
290
|
// For spread attributes, store just the actual value, not the full attribute string
|
|
@@ -549,15 +559,22 @@ const visitors = {
|
|
|
549
559
|
context.state.init.push(b.if(context.visit(node.test), consequent, alternate));
|
|
550
560
|
},
|
|
551
561
|
|
|
552
|
-
|
|
553
|
-
const
|
|
562
|
+
AssignmentExpression(node, context) {
|
|
563
|
+
const left = node.left;
|
|
554
564
|
|
|
555
|
-
if (
|
|
556
|
-
|
|
557
|
-
|
|
565
|
+
if (left.type === 'Identifier' && left.tracked) {
|
|
566
|
+
return b.call(
|
|
567
|
+
'set',
|
|
568
|
+
context.visit(left, { ...context.state, metadata: { tracking: false } }),
|
|
569
|
+
context.visit(node.right)
|
|
570
|
+
);
|
|
558
571
|
}
|
|
572
|
+
|
|
573
|
+
return context.next();
|
|
559
574
|
},
|
|
560
575
|
|
|
576
|
+
|
|
577
|
+
|
|
561
578
|
ServerIdentifier(node, context) {
|
|
562
579
|
return b.id('_$_server_$_');
|
|
563
580
|
},
|
|
@@ -605,22 +622,22 @@ const visitors = {
|
|
|
605
622
|
const try_statements =
|
|
606
623
|
node.handler !== null
|
|
607
624
|
? [
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
),
|
|
625
|
+
b.try(
|
|
626
|
+
b.block(body),
|
|
627
|
+
b.catch_clause(
|
|
628
|
+
node.handler.param || b.id('error'),
|
|
629
|
+
b.block(
|
|
630
|
+
transform_body(node.handler.body.body, {
|
|
631
|
+
...context,
|
|
632
|
+
state: {
|
|
633
|
+
...context.state,
|
|
634
|
+
scope: context.state.scopes.get(node.handler.body),
|
|
635
|
+
},
|
|
636
|
+
}),
|
|
621
637
|
),
|
|
622
638
|
),
|
|
623
|
-
|
|
639
|
+
),
|
|
640
|
+
]
|
|
624
641
|
: body;
|
|
625
642
|
|
|
626
643
|
context.state.init.push(
|
|
@@ -647,6 +664,8 @@ const visitors = {
|
|
|
647
664
|
},
|
|
648
665
|
|
|
649
666
|
AwaitExpression(node, context) {
|
|
667
|
+
context.state.scope.server_block = true
|
|
668
|
+
context.inside_server_block = true
|
|
650
669
|
if (context.state.to_ts) {
|
|
651
670
|
return context.next();
|
|
652
671
|
}
|
|
@@ -672,10 +691,8 @@ const visitors = {
|
|
|
672
691
|
const parent = context.path.at(-1);
|
|
673
692
|
|
|
674
693
|
if (node.tracked || (node.property.type === 'Identifier' && node.property.tracked)) {
|
|
675
|
-
add_ripple_internal_import(context);
|
|
676
|
-
|
|
677
694
|
return b.call(
|
|
678
|
-
'
|
|
695
|
+
'get',
|
|
679
696
|
b.member(
|
|
680
697
|
context.visit(node.object),
|
|
681
698
|
node.computed ? context.visit(node.property) : node.property,
|
|
@@ -690,13 +707,15 @@ const visitors = {
|
|
|
690
707
|
|
|
691
708
|
Text(node, { visit, state }) {
|
|
692
709
|
const metadata = { await: false };
|
|
693
|
-
|
|
710
|
+
let expression = visit(node.expression, { ...state, metadata });
|
|
711
|
+
|
|
712
|
+
if (expression.type === 'Identifier' && expression.tracked) {
|
|
713
|
+
expression = b.call('get', expression);
|
|
714
|
+
}
|
|
694
715
|
|
|
695
716
|
if (expression.type === 'Literal') {
|
|
696
717
|
state.init.push(
|
|
697
|
-
b.stmt(
|
|
698
|
-
b.call(b.member(b.id('__output'), b.id('push')), b.literal(escape(expression.value))),
|
|
699
|
-
),
|
|
718
|
+
b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(escape(expression.value)))),
|
|
700
719
|
);
|
|
701
720
|
} else {
|
|
702
721
|
state.init.push(
|
|
@@ -791,13 +810,7 @@ export function transform_server(filename, source, analysis) {
|
|
|
791
810
|
}
|
|
792
811
|
|
|
793
812
|
// Add async property to component functions
|
|
794
|
-
|
|
795
|
-
if (metadata.async) {
|
|
796
|
-
program.body.push(
|
|
797
|
-
b.stmt(b.assignment('=', b.member(b.id(metadata.id), b.id('async')), b.true)),
|
|
798
|
-
);
|
|
799
|
-
}
|
|
800
|
-
}
|
|
813
|
+
|
|
801
814
|
|
|
802
815
|
for (const import_node of state.imports) {
|
|
803
816
|
program.body.unshift(b.stmt(b.id(import_node)));
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/** @import { Block, Component } from '#client' */
|
|
2
2
|
|
|
3
|
-
import { branch, destroy_block, render } from './blocks.js';
|
|
3
|
+
import { branch, destroy_block, render, render_spread } from './blocks.js';
|
|
4
4
|
import { COMPOSITE_BLOCK } from './constants.js';
|
|
5
|
-
import { apply_element_spread } from './render';
|
|
6
5
|
import { active_block } from './runtime.js';
|
|
7
6
|
|
|
8
7
|
/**
|
|
@@ -48,8 +47,7 @@ export function composite(get_component, node, props) {
|
|
|
48
47
|
};
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
spread_fn();
|
|
50
|
+
render_spread(element, () => props || {});
|
|
53
51
|
|
|
54
52
|
if (typeof props?.children === 'function') {
|
|
55
53
|
var child_anchor = document.createComment('');
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** @import { Component, Derived } from '#server' */
|
|
2
|
+
/** @import { render } from 'ripple/server'*/
|
|
2
3
|
import { DERIVED, UNINITIALIZED } from '../client/constants.js';
|
|
3
4
|
import { is_tracked_object } from '../client/utils.js';
|
|
4
5
|
import { escape } from '../../../utils/escaping.js';
|
|
@@ -29,6 +30,8 @@ class Output {
|
|
|
29
30
|
body = '';
|
|
30
31
|
/** @type {Set<string>} */
|
|
31
32
|
css = new Set();
|
|
33
|
+
/** @type {Promise<any>[]} */
|
|
34
|
+
promises = [];
|
|
32
35
|
/** @type {Output | null} */
|
|
33
36
|
#parent = null;
|
|
34
37
|
|
|
@@ -56,24 +59,30 @@ class Output {
|
|
|
56
59
|
}
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
/**
|
|
60
|
-
* @param {((output: Output, props: Record<string, any>) => void | Promise<void>) & { async?: boolean }} component
|
|
61
|
-
* @returns {Promise<{head: string, body: string, css: Set<string>}>}
|
|
62
|
-
*/
|
|
62
|
+
/** @type {render} */
|
|
63
63
|
export async function render(component) {
|
|
64
64
|
const output = new Output(null);
|
|
65
|
+
let head, body, css;
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
try {
|
|
68
|
+
if (component.async) {
|
|
69
|
+
await component(output, {});
|
|
70
|
+
} else {
|
|
71
|
+
component(output, {});
|
|
72
|
+
}
|
|
73
|
+
if (output.promises.length > 0) {
|
|
74
|
+
await Promise.all(output.promises);
|
|
75
|
+
}
|
|
73
76
|
|
|
74
|
-
|
|
77
|
+
head = output.head
|
|
78
|
+
body = output.body
|
|
79
|
+
css = output.css
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.log(error)
|
|
83
|
+
}
|
|
84
|
+
return { head, body, css }
|
|
75
85
|
}
|
|
76
|
-
|
|
77
86
|
/**
|
|
78
87
|
* @returns {void}
|
|
79
88
|
*/
|
package/src/utils/builders.js
CHANGED
|
@@ -237,14 +237,14 @@ export function export_builder(declaration, specifiers = [], attributes = [], so
|
|
|
237
237
|
* @param {ESTree.BlockStatement} body
|
|
238
238
|
* @returns {ESTree.FunctionDeclaration}
|
|
239
239
|
*/
|
|
240
|
-
export function function_declaration(id, params, body) {
|
|
240
|
+
export function function_declaration(id, params, body, async = false) {
|
|
241
241
|
return {
|
|
242
242
|
type: 'FunctionDeclaration',
|
|
243
243
|
id,
|
|
244
244
|
params,
|
|
245
245
|
body,
|
|
246
246
|
generator: false,
|
|
247
|
-
async
|
|
247
|
+
async,
|
|
248
248
|
metadata: /** @type {any} */ (null), // should not be used by codegen
|
|
249
249
|
};
|
|
250
250
|
}
|
|
@@ -238,86 +238,6 @@ exports[`for statements > handles updating with new objects with same key 2`] =
|
|
|
238
238
|
</div>
|
|
239
239
|
`;
|
|
240
240
|
|
|
241
|
-
exports[`for statements > render a simple dynamic array 1`] = `
|
|
242
|
-
<div>
|
|
243
|
-
<!---->
|
|
244
|
-
<div
|
|
245
|
-
class="Item 1"
|
|
246
|
-
>
|
|
247
|
-
Item 1
|
|
248
|
-
</div>
|
|
249
|
-
<div
|
|
250
|
-
class="Item 2"
|
|
251
|
-
>
|
|
252
|
-
Item 2
|
|
253
|
-
</div>
|
|
254
|
-
<div
|
|
255
|
-
class="Item 3"
|
|
256
|
-
>
|
|
257
|
-
Item 3
|
|
258
|
-
</div>
|
|
259
|
-
<!---->
|
|
260
|
-
<button>
|
|
261
|
-
Add Item
|
|
262
|
-
</button>
|
|
263
|
-
|
|
264
|
-
</div>
|
|
265
|
-
`;
|
|
266
|
-
|
|
267
|
-
exports[`for statements > render a simple dynamic array 2`] = `
|
|
268
|
-
<div>
|
|
269
|
-
<!---->
|
|
270
|
-
<div
|
|
271
|
-
class="Item 1"
|
|
272
|
-
>
|
|
273
|
-
Item 1
|
|
274
|
-
</div>
|
|
275
|
-
<div
|
|
276
|
-
class="Item 2"
|
|
277
|
-
>
|
|
278
|
-
Item 2
|
|
279
|
-
</div>
|
|
280
|
-
<div
|
|
281
|
-
class="Item 3"
|
|
282
|
-
>
|
|
283
|
-
Item 3
|
|
284
|
-
</div>
|
|
285
|
-
<div
|
|
286
|
-
class="Item 4"
|
|
287
|
-
>
|
|
288
|
-
Item 4
|
|
289
|
-
</div>
|
|
290
|
-
<!---->
|
|
291
|
-
<button>
|
|
292
|
-
Add Item
|
|
293
|
-
</button>
|
|
294
|
-
|
|
295
|
-
</div>
|
|
296
|
-
`;
|
|
297
|
-
|
|
298
|
-
exports[`for statements > render a simple static array 1`] = `
|
|
299
|
-
<div>
|
|
300
|
-
<!---->
|
|
301
|
-
<div
|
|
302
|
-
class="Item 1"
|
|
303
|
-
>
|
|
304
|
-
Item 1
|
|
305
|
-
</div>
|
|
306
|
-
<div
|
|
307
|
-
class="Item 2"
|
|
308
|
-
>
|
|
309
|
-
Item 2
|
|
310
|
-
</div>
|
|
311
|
-
<div
|
|
312
|
-
class="Item 3"
|
|
313
|
-
>
|
|
314
|
-
Item 3
|
|
315
|
-
</div>
|
|
316
|
-
<!---->
|
|
317
|
-
|
|
318
|
-
</div>
|
|
319
|
-
`;
|
|
320
|
-
|
|
321
241
|
exports[`for statements > renders a simple dynamic array 1`] = `
|
|
322
242
|
<div>
|
|
323
243
|
<!---->
|
|
@@ -56,51 +56,3 @@ exports[`basic client > rendering & text > should handle lexical scopes correctl
|
|
|
56
56
|
|
|
57
57
|
</div>
|
|
58
58
|
`;
|
|
59
|
-
|
|
60
|
-
exports[`basic client > text rendering > basic operations 1`] = `
|
|
61
|
-
<div>
|
|
62
|
-
<div>
|
|
63
|
-
0
|
|
64
|
-
</div>
|
|
65
|
-
<div>
|
|
66
|
-
2
|
|
67
|
-
</div>
|
|
68
|
-
<div>
|
|
69
|
-
5
|
|
70
|
-
</div>
|
|
71
|
-
<div>
|
|
72
|
-
2
|
|
73
|
-
</div>
|
|
74
|
-
|
|
75
|
-
</div>
|
|
76
|
-
`;
|
|
77
|
-
|
|
78
|
-
exports[`basic client > text rendering > renders semi-dynamic text 1`] = `
|
|
79
|
-
<div>
|
|
80
|
-
<div>
|
|
81
|
-
Hello World
|
|
82
|
-
</div>
|
|
83
|
-
|
|
84
|
-
</div>
|
|
85
|
-
`;
|
|
86
|
-
|
|
87
|
-
exports[`basic client > text rendering > renders simple JS expression logic correctly 1`] = `
|
|
88
|
-
<div>
|
|
89
|
-
<div>
|
|
90
|
-
{"0":"Test"}
|
|
91
|
-
</div>
|
|
92
|
-
<div>
|
|
93
|
-
1
|
|
94
|
-
</div>
|
|
95
|
-
|
|
96
|
-
</div>
|
|
97
|
-
`;
|
|
98
|
-
|
|
99
|
-
exports[`basic client > text rendering > renders static text 1`] = `
|
|
100
|
-
<div>
|
|
101
|
-
<div>
|
|
102
|
-
Hello World
|
|
103
|
-
</div>
|
|
104
|
-
|
|
105
|
-
</div>
|
|
106
|
-
`;
|
|
@@ -123,4 +123,20 @@ describe('basic client > errors', () => {
|
|
|
123
123
|
|
|
124
124
|
expect(error).toBe('Assignments or updates to tracked values are not allowed during computed "track(() => ...)" evaluation');
|
|
125
125
|
});
|
|
126
|
+
|
|
127
|
+
it('should throw error for await in client-side control-flow statements', () => {
|
|
128
|
+
const code = `
|
|
129
|
+
export default component App() {
|
|
130
|
+
let data = 'initial';
|
|
131
|
+
if (true) {
|
|
132
|
+
await new Promise(r => setTimeout(r, 100));
|
|
133
|
+
data = 'loaded';
|
|
134
|
+
}
|
|
135
|
+
<div>{data}</div>
|
|
136
|
+
}
|
|
137
|
+
`;
|
|
138
|
+
expect(() => {
|
|
139
|
+
compile(code, 'test.ripple', { mode: 'client' });
|
|
140
|
+
}).toThrow('`await` is not allowed in client-side control-flow statements');
|
|
141
|
+
});
|
|
126
142
|
});
|