ripple 0.3.8 → 0.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/package.json +2 -2
- package/src/compiler/phases/1-parse/index.js +38 -172
- package/src/compiler/phases/2-analyze/index.js +308 -115
- package/src/compiler/phases/2-analyze/prune.js +13 -5
- package/src/compiler/phases/3-transform/client/index.js +197 -213
- package/src/compiler/phases/3-transform/segments.js +0 -7
- package/src/compiler/phases/3-transform/server/index.js +77 -170
- package/src/compiler/types/acorn.d.ts +1 -1
- package/src/compiler/types/estree.d.ts +1 -1
- package/src/compiler/types/import.d.ts +0 -2
- package/src/compiler/types/index.d.ts +14 -18
- package/src/compiler/types/parse.d.ts +3 -9
- package/src/compiler/utils.js +154 -21
- package/src/runtime/element.js +39 -0
- package/src/runtime/index-client.js +2 -13
- package/src/runtime/index-server.js +2 -2
- package/src/runtime/internal/client/bindings.js +3 -1
- package/src/runtime/internal/client/composite.js +11 -6
- package/src/runtime/internal/client/events.js +1 -1
- package/src/runtime/internal/client/expression.js +218 -0
- package/src/runtime/internal/client/head.js +3 -4
- package/src/runtime/internal/client/index.js +4 -1
- package/src/runtime/internal/client/portal.js +12 -6
- package/src/runtime/internal/client/runtime.js +0 -52
- package/src/runtime/internal/server/index.js +57 -56
- package/tests/client/basic/basic.components.test.ripple +85 -87
- package/tests/client/basic/basic.errors.test.ripple +28 -4
- package/tests/client/basic/basic.reactivity.test.ripple +10 -155
- package/tests/client/basic/basic.rendering.test.ripple +23 -8
- package/tests/client/capture-error.js +12 -0
- package/tests/client/compiler/compiler.basic.test.ripple +107 -18
- package/tests/client/composite/composite.props.test.ripple +5 -9
- package/tests/client/composite/composite.reactivity.test.ripple +35 -36
- package/tests/client/composite/composite.render.test.ripple +45 -13
- package/tests/client/css/global-additional-cases.test.ripple +3 -3
- package/tests/client/dynamic-elements.test.ripple +3 -4
- package/tests/client/lazy-destructuring.test.ripple +69 -12
- package/tests/client/svg.test.ripple +4 -4
- package/tests/hydration/basic.test.js +23 -0
- package/tests/hydration/compiled/client/basic.js +118 -66
- package/tests/hydration/compiled/client/composite.js +90 -37
- package/tests/hydration/compiled/client/events.js +18 -18
- package/tests/hydration/compiled/client/for.js +62 -62
- package/tests/hydration/compiled/client/head.js +10 -10
- package/tests/hydration/compiled/client/hmr.js +13 -10
- package/tests/hydration/compiled/client/html.js +274 -236
- package/tests/hydration/compiled/client/if-children.js +41 -35
- package/tests/hydration/compiled/client/if.js +2 -2
- package/tests/hydration/compiled/client/mixed-control-flow.js +12 -12
- package/tests/hydration/compiled/client/nested-control-flow.js +46 -46
- package/tests/hydration/compiled/client/portal.js +8 -8
- package/tests/hydration/compiled/client/reactivity.js +14 -14
- package/tests/hydration/compiled/client/return.js +2 -2
- package/tests/hydration/compiled/client/try.js +4 -4
- package/tests/hydration/compiled/server/basic.js +64 -31
- package/tests/hydration/compiled/server/composite.js +62 -29
- package/tests/hydration/compiled/server/hmr.js +24 -37
- package/tests/hydration/compiled/server/html.js +472 -611
- package/tests/hydration/compiled/server/if-children.js +77 -103
- package/tests/hydration/compiled/server/portal.js +8 -8
- package/tests/hydration/components/basic.ripple +15 -5
- package/tests/hydration/components/composite.ripple +13 -1
- package/tests/hydration/components/hmr.ripple +1 -3
- package/tests/hydration/components/html.ripple +13 -35
- package/tests/hydration/components/if-children.ripple +4 -8
- package/tests/hydration/composite.test.js +11 -0
- package/tests/server/basic.attributes.test.ripple +50 -0
- package/tests/server/basic.components.test.ripple +22 -28
- package/tests/server/basic.test.ripple +12 -0
- package/tests/server/compiler.test.ripple +43 -4
- package/tests/server/composite.props.test.ripple +5 -9
- package/tests/server/dynamic-elements.test.ripple +3 -4
- package/tests/server/lazy-destructuring.test.ripple +68 -12
- package/tests/server/style-identifier.test.ripple +2 -4
- package/tsconfig.typecheck.json +4 -0
- package/types/index.d.ts +9 -21
- package/tests/client/__snapshots__/tracked-expression.test.ripple.snap +0 -34
- package/tests/client/tracked-expression.test.ripple +0 -26
|
@@ -426,58 +426,6 @@ export function track(v, get, set, b) {
|
|
|
426
426
|
return tracked(v, b, get, set);
|
|
427
427
|
}
|
|
428
428
|
|
|
429
|
-
/**
|
|
430
|
-
* @param {Record<string|symbol, any>} v
|
|
431
|
-
* @param {(symbol | string)[]} l
|
|
432
|
-
* @param {Block} b
|
|
433
|
-
* @returns {Tracked[]}
|
|
434
|
-
*/
|
|
435
|
-
export function track_split(v, l, b) {
|
|
436
|
-
var is_tracked = is_ripple_object(v);
|
|
437
|
-
|
|
438
|
-
if (is_tracked || typeof v !== 'object' || v === null || is_array(v)) {
|
|
439
|
-
throw new TypeError('Invalid value: expected a non-tracked object');
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/** @type {Tracked[]} */
|
|
443
|
-
var out = [];
|
|
444
|
-
/** @type {Record<string|symbol, any>} */
|
|
445
|
-
var rest = {};
|
|
446
|
-
/** @type {Record<PropertyKey, 1>} */
|
|
447
|
-
var done = {};
|
|
448
|
-
var props = Reflect.ownKeys(v);
|
|
449
|
-
|
|
450
|
-
for (let i = 0, key, t; i < l.length; i++) {
|
|
451
|
-
key = l[i];
|
|
452
|
-
|
|
453
|
-
if (props.includes(key)) {
|
|
454
|
-
if (is_ripple_object(v[key])) {
|
|
455
|
-
t = v[key];
|
|
456
|
-
} else {
|
|
457
|
-
t = tracked(undefined, b);
|
|
458
|
-
t = define_property(t, '__v', /** @type {PropertyDescriptor} */ (get_descriptor(v, key)));
|
|
459
|
-
}
|
|
460
|
-
} else {
|
|
461
|
-
t = tracked(undefined, b);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
out[i] = t;
|
|
465
|
-
done[key] = 1;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
for (let i = 0, key; i < props.length; i++) {
|
|
469
|
-
key = props[i];
|
|
470
|
-
if (done[key]) {
|
|
471
|
-
continue;
|
|
472
|
-
}
|
|
473
|
-
define_property(rest, key, /** @type {PropertyDescriptor} */ (get_descriptor(v, key)));
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
out.push(tracked(rest, b));
|
|
477
|
-
|
|
478
|
-
return out;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
429
|
/**
|
|
482
430
|
* @param {Tracked | Derived} tracked
|
|
483
431
|
* @returns {Dependency}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
@import { Component, Dependency, Derived, Tracked } from '#server';
|
|
3
|
-
@import { SSRComponent
|
|
3
|
+
@import { SSRComponent } from 'ripple/server';
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { Readable } from 'stream';
|
|
@@ -17,6 +17,7 @@ import { is_boolean_attribute } from '../../../compiler/utils.js';
|
|
|
17
17
|
import { clsx } from 'clsx';
|
|
18
18
|
import { normalize_css_property_name } from '../../../utils/normalize_css_property_name.js';
|
|
19
19
|
import { BLOCK_CLOSE, BLOCK_OPEN } from '../../../constants.js';
|
|
20
|
+
import { is_ripple_element, normalize_children, ripple_element } from '../../element.js';
|
|
20
21
|
import {
|
|
21
22
|
is_tag_valid_with_parent,
|
|
22
23
|
is_tag_valid_with_ancestor,
|
|
@@ -27,6 +28,30 @@ export { register_component_css as register_css } from './css-registry.js';
|
|
|
27
28
|
export { hash } from '../../../utils/hashing.js';
|
|
28
29
|
export { context } from './context.js';
|
|
29
30
|
export { array_slice };
|
|
31
|
+
export { ripple_element, normalize_children };
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {Output} output
|
|
35
|
+
* @param {any} value
|
|
36
|
+
* @returns {void}
|
|
37
|
+
*/
|
|
38
|
+
export function render_expression(output, value) {
|
|
39
|
+
output.push(BLOCK_OPEN);
|
|
40
|
+
|
|
41
|
+
if (is_ripple_element(value)) {
|
|
42
|
+
var result = value.render(output, {});
|
|
43
|
+
|
|
44
|
+
if (result && typeof result.then === 'function') {
|
|
45
|
+
return result.then(() => {
|
|
46
|
+
output.push(BLOCK_CLOSE);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
output.push(escape(value ?? ''));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
output.push(BLOCK_CLOSE);
|
|
54
|
+
}
|
|
30
55
|
|
|
31
56
|
/** @type {null | Component} */
|
|
32
57
|
export let active_component = null;
|
|
@@ -231,12 +256,12 @@ class Output {
|
|
|
231
256
|
}
|
|
232
257
|
}
|
|
233
258
|
|
|
234
|
-
/** @type {render} */
|
|
259
|
+
/** @type {import('ripple/server').render} */
|
|
235
260
|
export async function render(component) {
|
|
236
261
|
const output = new Output(null, null);
|
|
237
262
|
let head = '';
|
|
238
263
|
let body = '';
|
|
239
|
-
let css = new Set();
|
|
264
|
+
let css = /** @type {Set<string>} */ (new Set());
|
|
240
265
|
|
|
241
266
|
// Reset dev-mode element tracking state at the start of each render
|
|
242
267
|
reset_element_state();
|
|
@@ -262,7 +287,7 @@ export async function render(component) {
|
|
|
262
287
|
return { head, body, css };
|
|
263
288
|
}
|
|
264
289
|
|
|
265
|
-
/** @type {renderToStream} */
|
|
290
|
+
/** @type {import('ripple/server').renderToStream} */
|
|
266
291
|
export function renderToStream(component) {
|
|
267
292
|
const stream = new Readable({
|
|
268
293
|
read() {},
|
|
@@ -596,7 +621,7 @@ export function spread_attrs(attrs, css_hash) {
|
|
|
596
621
|
for (name in attrs) {
|
|
597
622
|
var value = attrs[name];
|
|
598
623
|
|
|
599
|
-
if (typeof value === 'function') continue;
|
|
624
|
+
if (name === 'children' || typeof value === 'function' || is_ripple_element(value)) continue;
|
|
600
625
|
|
|
601
626
|
if (is_ripple_object(value)) {
|
|
602
627
|
value = get(value);
|
|
@@ -702,6 +727,24 @@ function tracked(v, get, set) {
|
|
|
702
727
|
return /** @type {Tracked} */ (new TrackedValue(v, get || set ? { get, set } : empty_get_set));
|
|
703
728
|
}
|
|
704
729
|
|
|
730
|
+
/**
|
|
731
|
+
* @param {Record<string, unknown>} obj
|
|
732
|
+
* @param {string[]} exclude_keys
|
|
733
|
+
* @returns {Record<string, unknown>}
|
|
734
|
+
*/
|
|
735
|
+
export function exclude_from_object(obj, exclude_keys) {
|
|
736
|
+
/** @type {Record<string, unknown>} */
|
|
737
|
+
var new_obj = {};
|
|
738
|
+
|
|
739
|
+
for (const key of Object.keys(obj)) {
|
|
740
|
+
if (!exclude_keys.includes(key)) {
|
|
741
|
+
new_obj[key] = obj[key];
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
return new_obj;
|
|
746
|
+
}
|
|
747
|
+
|
|
705
748
|
/**
|
|
706
749
|
* @param {any} v
|
|
707
750
|
* @param {(value: any) => any} [get]
|
|
@@ -722,57 +765,6 @@ export function track(v, get, set) {
|
|
|
722
765
|
return tracked(v, get, set);
|
|
723
766
|
}
|
|
724
767
|
|
|
725
|
-
/**
|
|
726
|
-
* @param {Record<string|symbol, any>} v
|
|
727
|
-
* @param {(symbol | string)[]} l
|
|
728
|
-
* @returns {Tracked[]}
|
|
729
|
-
*/
|
|
730
|
-
export function track_split(v, l) {
|
|
731
|
-
var is_tracked = is_ripple_object(v);
|
|
732
|
-
|
|
733
|
-
if (is_tracked || typeof v !== 'object' || v === null || is_array(v)) {
|
|
734
|
-
throw new TypeError('Invalid value: expected a non-tracked object');
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
/** @type {Tracked[]} */
|
|
738
|
-
var out = [];
|
|
739
|
-
/** @type {Record<string|symbol, any>} */
|
|
740
|
-
var rest = {};
|
|
741
|
-
/** @type {Record<PropertyKey, 1>} */
|
|
742
|
-
var done = {};
|
|
743
|
-
var props = Reflect.ownKeys(v);
|
|
744
|
-
|
|
745
|
-
for (let i = 0, key, t; i < l.length; i++) {
|
|
746
|
-
key = l[i];
|
|
747
|
-
|
|
748
|
-
if (props.includes(key)) {
|
|
749
|
-
if (is_ripple_object(v[key])) {
|
|
750
|
-
t = v[key];
|
|
751
|
-
} else {
|
|
752
|
-
t = tracked(undefined);
|
|
753
|
-
t = define_property(t, 'v', /** @type {PropertyDescriptor} */ (get_descriptor(v, key)));
|
|
754
|
-
}
|
|
755
|
-
} else {
|
|
756
|
-
t = tracked(undefined);
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
out[i] = t;
|
|
760
|
-
done[key] = 1;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
for (let i = 0, key; i < props.length; i++) {
|
|
764
|
-
key = props[i];
|
|
765
|
-
if (done[key]) {
|
|
766
|
-
continue;
|
|
767
|
-
}
|
|
768
|
-
define_property(rest, key, /** @type {PropertyDescriptor} */ (get_descriptor(v, key)));
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
out.push(tracked(rest));
|
|
772
|
-
|
|
773
|
-
return out;
|
|
774
|
-
}
|
|
775
|
-
|
|
776
768
|
/**
|
|
777
769
|
* @param {any} _
|
|
778
770
|
* @param {ConstructorParameters<typeof URL>} params
|
|
@@ -865,6 +857,15 @@ export function ripple_object(obj) {
|
|
|
865
857
|
return obj;
|
|
866
858
|
}
|
|
867
859
|
|
|
860
|
+
/**
|
|
861
|
+
* @template K, V
|
|
862
|
+
* @param {Iterable<readonly [K, V]>} [iterable]
|
|
863
|
+
* @returns {Map<K, V>}
|
|
864
|
+
*/
|
|
865
|
+
export function ripple_map(iterable) {
|
|
866
|
+
return new Map(iterable);
|
|
867
|
+
}
|
|
868
|
+
|
|
868
869
|
/**
|
|
869
870
|
* Returns the fallback value if the given value is undefined.
|
|
870
871
|
* @template T
|
|
@@ -7,21 +7,20 @@ import type {
|
|
|
7
7
|
PropsWithChildrenOptional,
|
|
8
8
|
} from 'ripple';
|
|
9
9
|
import { flushSync, track } from 'ripple';
|
|
10
|
+
import { did_error } from '../capture-error.js';
|
|
10
11
|
|
|
11
12
|
describe('basic client > components & composition', () => {
|
|
12
13
|
it('renders with component composition and children', () => {
|
|
13
14
|
component Card(props: PropsWithChildren<{}>) {
|
|
14
|
-
<div class="card">
|
|
15
|
-
<props.children />
|
|
16
|
-
</div>
|
|
15
|
+
<div class="card">{props.children}</div>
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
component Basic() {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
component children() {
|
|
20
|
+
<p>{'Card content here'}</p>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
<Card {children} />
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
render(Basic);
|
|
@@ -37,17 +36,17 @@ describe('basic client > components & composition', () => {
|
|
|
37
36
|
component Card(props: PropsWithChildrenOptional<{ test?: Component }>) {
|
|
38
37
|
<div class="card">
|
|
39
38
|
if (props.children) {
|
|
40
|
-
|
|
39
|
+
{props.children}
|
|
41
40
|
}
|
|
42
41
|
</div>
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
component Basic() {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
component test() {
|
|
46
|
+
<p>{'Card content here'}</p>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
<Card {test} />
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
render(Basic);
|
|
@@ -59,22 +58,23 @@ describe('basic client > components & composition', () => {
|
|
|
59
58
|
expect(paragraph).toBeFalsy();
|
|
60
59
|
});
|
|
61
60
|
|
|
62
|
-
it('allows tracked
|
|
61
|
+
it('allows tracked variables alongside explicit component props', () => {
|
|
63
62
|
component Card(props: PropsWithChildrenOptional<{ test?: Component }>) {
|
|
64
63
|
<div class="card">
|
|
65
64
|
if (props.children) {
|
|
66
|
-
|
|
65
|
+
{props.children}
|
|
67
66
|
}
|
|
68
67
|
</div>
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
component Basic() {
|
|
72
71
|
let &[test] = track(false);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
|
|
73
|
+
component TestSlot() {
|
|
74
|
+
<p>{'Card content here'}</p>
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
<Card test={TestSlot} />
|
|
78
78
|
<div>{test ? 'yes' : 'no'}</div>
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -90,9 +90,7 @@ describe('basic client > components & composition', () => {
|
|
|
90
90
|
|
|
91
91
|
it('renders a component when children is set a component prop', () => {
|
|
92
92
|
component Card(props: PropsWithChildren<{}>) {
|
|
93
|
-
<div class="card">
|
|
94
|
-
<props.children />
|
|
95
|
-
</div>
|
|
93
|
+
<div class="card">{props.children}</div>
|
|
96
94
|
}
|
|
97
95
|
|
|
98
96
|
component Basic() {
|
|
@@ -211,6 +209,28 @@ describe('basic client > components & composition', () => {
|
|
|
211
209
|
expect(countDiv.textContent).toBe('3');
|
|
212
210
|
});
|
|
213
211
|
|
|
212
|
+
it('updates explicit text children props reactively', () => {
|
|
213
|
+
component TextProp(&{ children }: PropsWithChildren<{}>) {
|
|
214
|
+
<div class="text-prop">{children}</div>
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
component Basic() {
|
|
218
|
+
let &[show] = track(false);
|
|
219
|
+
|
|
220
|
+
<TextProp children={show ? 'hello' : ''} />
|
|
221
|
+
<button class="show-text" onClick={() => (show = true)}>{'Show'}</button>
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
render(Basic);
|
|
225
|
+
|
|
226
|
+
expect(container.querySelector('.text-prop')?.textContent).toBe('');
|
|
227
|
+
|
|
228
|
+
container.querySelector('.show-text')?.click();
|
|
229
|
+
flushSync();
|
|
230
|
+
|
|
231
|
+
expect(container.querySelector('.text-prop')?.textContent).toBe('hello');
|
|
232
|
+
});
|
|
233
|
+
|
|
214
234
|
it('it retains this context with bracketed prop functions and keeps original chaining', () => {
|
|
215
235
|
component App() {
|
|
216
236
|
const SYMBOL_PROP = Symbol();
|
|
@@ -233,60 +253,40 @@ describe('basic client > components & composition', () => {
|
|
|
233
253
|
|
|
234
254
|
const obj2 = null;
|
|
235
255
|
|
|
256
|
+
function trigger_nonexistent() {
|
|
257
|
+
hasError = did_error(() => {
|
|
258
|
+
// @ts-ignore
|
|
259
|
+
obj['nonexistent']();
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function trigger_nonexistent_chaining() {
|
|
264
|
+
hasError = did_error(() => {
|
|
265
|
+
// @ts-ignore
|
|
266
|
+
obj['nonexistent']?.();
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function trigger_object_null() {
|
|
271
|
+
hasError = did_error(() => {
|
|
272
|
+
// @ts-ignore
|
|
273
|
+
obj2['nonexistent']();
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function trigger_object_null_chained() {
|
|
278
|
+
hasError = did_error(() => {
|
|
279
|
+
// @ts-ignore
|
|
280
|
+
obj2?.['nonexistent']?.();
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
236
284
|
<button onClick={() => obj['increment']()}>{'Increment'}</button>
|
|
237
285
|
<button onClick={() => obj[SYMBOL_PROP]()}>{'Increment'}</button>
|
|
238
|
-
<button
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
// @ts-ignore
|
|
243
|
-
obj['nonexistent']();
|
|
244
|
-
} catch {
|
|
245
|
-
hasError = true;
|
|
246
|
-
}
|
|
247
|
-
}}
|
|
248
|
-
>
|
|
249
|
-
{'Nonexistent'}
|
|
250
|
-
</button>
|
|
251
|
-
<button
|
|
252
|
-
onClick={() => {
|
|
253
|
-
hasError = false;
|
|
254
|
-
try {
|
|
255
|
-
// @ts-ignore
|
|
256
|
-
obj['nonexistent']?.();
|
|
257
|
-
} catch {
|
|
258
|
-
hasError = true;
|
|
259
|
-
}
|
|
260
|
-
}}
|
|
261
|
-
>
|
|
262
|
-
{'Nonexistent chaining'}
|
|
263
|
-
</button>
|
|
264
|
-
<button
|
|
265
|
-
onClick={() => {
|
|
266
|
-
hasError = false;
|
|
267
|
-
try {
|
|
268
|
-
// @ts-ignore
|
|
269
|
-
obj2['nonexistent']();
|
|
270
|
-
} catch {
|
|
271
|
-
hasError = true;
|
|
272
|
-
}
|
|
273
|
-
}}
|
|
274
|
-
>
|
|
275
|
-
{'Object null'}
|
|
276
|
-
</button>
|
|
277
|
-
<button
|
|
278
|
-
onClick={() => {
|
|
279
|
-
hasError = false;
|
|
280
|
-
try {
|
|
281
|
-
// @ts-ignore
|
|
282
|
-
obj2?.['nonexistent']?.();
|
|
283
|
-
} catch {
|
|
284
|
-
hasError = true;
|
|
285
|
-
}
|
|
286
|
-
}}
|
|
287
|
-
>
|
|
288
|
-
{'Object null chained'}
|
|
289
|
-
</button>
|
|
286
|
+
<button onClick={trigger_nonexistent}>{'Nonexistent'}</button>
|
|
287
|
+
<button onClick={trigger_nonexistent_chaining}>{'Nonexistent chaining'}</button>
|
|
288
|
+
<button onClick={trigger_object_null}>{'Object null'}</button>
|
|
289
|
+
<button onClick={trigger_object_null_chained}>{'Object null chained'}</button>
|
|
290
290
|
<button onClick={() => obj.arr[obj.arr.length - 1]()}>{'BinaryExpression prop'}</button>
|
|
291
291
|
|
|
292
292
|
<span>{obj.count.value}</span>
|
|
@@ -346,21 +346,19 @@ describe('basic client > components & composition', () => {
|
|
|
346
346
|
<span>{'Hello from Span'}</span>
|
|
347
347
|
},
|
|
348
348
|
button: component({ children }: PropsWithChildren<{}>) {
|
|
349
|
-
<button>
|
|
350
|
-
<children />
|
|
351
|
-
</button>
|
|
349
|
+
<button>{children}</button>
|
|
352
350
|
},
|
|
353
351
|
};
|
|
354
352
|
|
|
355
353
|
component App() {
|
|
354
|
+
component children() {
|
|
355
|
+
<span>{'Click me!'}</span>
|
|
356
|
+
}
|
|
357
|
+
|
|
356
358
|
<div>
|
|
357
359
|
<h1>{'Component as Property Test'}</h1>
|
|
358
360
|
<UI.span />
|
|
359
|
-
<UI.button
|
|
360
|
-
component children() {
|
|
361
|
-
<span>{'Click me!'}</span>
|
|
362
|
-
}
|
|
363
|
-
</UI.button>
|
|
361
|
+
<UI.button {children} />
|
|
364
362
|
</div>
|
|
365
363
|
}
|
|
366
364
|
|
|
@@ -378,13 +376,13 @@ describe('basic client > components & composition', () => {
|
|
|
378
376
|
|
|
379
377
|
it('handles empty string children', () => {
|
|
380
378
|
component Button({ children }: PropsWithChildren<{}>) {
|
|
381
|
-
|
|
379
|
+
{children}
|
|
382
380
|
}
|
|
383
381
|
|
|
384
382
|
component App() {
|
|
385
|
-
let
|
|
383
|
+
let content = '';
|
|
386
384
|
<Button>{''}</Button>
|
|
387
|
-
<Button>{
|
|
385
|
+
<Button>{content}</Button>
|
|
388
386
|
}
|
|
389
387
|
|
|
390
388
|
expect(() => {
|
|
@@ -70,9 +70,7 @@ describe('basic client > errors', () => {
|
|
|
70
70
|
|
|
71
71
|
expect(() => {
|
|
72
72
|
compile(code, 'test.ripple');
|
|
73
|
-
}).toThrow(
|
|
74
|
-
'`children` cannot be rendered using text interpolation. Use `<children />` instead.',
|
|
75
|
-
);
|
|
73
|
+
}).not.toThrow();
|
|
76
74
|
});
|
|
77
75
|
|
|
78
76
|
it('should throw error for interpolating props.children as text', () => {
|
|
@@ -82,10 +80,36 @@ describe('basic client > errors', () => {
|
|
|
82
80
|
}
|
|
83
81
|
`;
|
|
84
82
|
|
|
83
|
+
expect(() => {
|
|
84
|
+
compile(code, 'test.ripple');
|
|
85
|
+
}).not.toThrow();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should throw error for calling children as a function', () => {
|
|
89
|
+
const code = `
|
|
90
|
+
export component Layout({ children }) {
|
|
91
|
+
{children()}
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
expect(() => {
|
|
96
|
+
compile(code, 'test.ripple');
|
|
97
|
+
}).toThrow(
|
|
98
|
+
'`children` cannot be called like a regular function. Render it with `{children}` or `{props.children}` instead.',
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should throw error for calling props.children as a function', () => {
|
|
103
|
+
const code = `
|
|
104
|
+
export component Layout(props) {
|
|
105
|
+
{props.children()}
|
|
106
|
+
}
|
|
107
|
+
`;
|
|
108
|
+
|
|
85
109
|
expect(() => {
|
|
86
110
|
compile(code, 'test.ripple');
|
|
87
111
|
}).toThrow(
|
|
88
|
-
'`children` cannot be
|
|
112
|
+
'`children` cannot be called like a regular function. Render it with `{children}` or `{props.children}` instead.',
|
|
89
113
|
);
|
|
90
114
|
});
|
|
91
115
|
|