ripple 0.2.151 → 0.2.153
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/README.md +3 -3
- package/package.json +5 -5
- package/src/compiler/phases/1-parse/index.js +1 -1
- package/src/compiler/phases/3-transform/client/index.js +37 -16
- package/src/compiler/phases/3-transform/server/index.js +43 -25
- package/src/runtime/internal/client/events.js +5 -1
- package/src/runtime/internal/client/index.js +2 -1
- package/src/runtime/internal/client/render.js +18 -15
- package/src/runtime/internal/client/runtime.js +78 -10
- package/src/runtime/internal/server/index.js +51 -11
- package/src/server/index.js +1 -1
- package/tests/client/array/array.derived.test.ripple +61 -33
- package/tests/client/array/array.iteration.test.ripple +3 -1
- package/tests/client/array/array.mutations.test.ripple +19 -15
- package/tests/client/array/array.static.test.ripple +115 -104
- package/tests/client/array/array.to-methods.test.ripple +3 -3
- package/tests/client/basic/basic.attributes.test.ripple +110 -57
- package/tests/client/basic/basic.collections.test.ripple +41 -22
- package/tests/client/basic/basic.errors.test.ripple +12 -6
- package/tests/client/basic/basic.events.test.ripple +51 -33
- package/tests/client/basic/basic.reactivity.test.ripple +120 -56
- package/tests/client/basic/basic.rendering.test.ripple +49 -19
- package/tests/client/basic/basic.styling.test.ripple +2 -2
- package/tests/client/basic/basic.utilities.test.ripple +1 -1
- package/tests/client/boundaries.test.ripple +70 -58
- package/tests/client/compiler/compiler.assignments.test.ripple +32 -4
- package/tests/client/compiler/compiler.attributes.test.ripple +46 -46
- package/tests/client/compiler/compiler.basic.test.ripple +18 -15
- package/tests/client/compiler/compiler.tracked-access.test.ripple +53 -42
- package/tests/client/compiler/compiler.typescript.test.ripple +1 -2
- package/tests/client/composite/composite.dynamic-components.test.ripple +6 -6
- package/tests/client/composite/composite.generics.test.ripple +39 -36
- package/tests/client/composite/composite.props.test.ripple +4 -3
- package/tests/client/composite/composite.reactivity.test.ripple +112 -27
- package/tests/client/composite/composite.render.test.ripple +9 -8
- package/tests/client/computed-properties.test.ripple +24 -24
- package/tests/client/context.test.ripple +11 -9
- package/tests/client/date.test.ripple +3 -1
- package/tests/client/dynamic-elements.test.ripple +103 -78
- package/tests/client/for.test.ripple +27 -17
- package/tests/client/head.test.ripple +42 -6
- package/tests/client/html.test.ripple +42 -32
- package/tests/client/input-value.test.ripple +4 -4
- package/tests/client/map.test.ripple +140 -141
- package/tests/client/media-query.test.ripple +31 -31
- package/tests/client/object.test.ripple +148 -112
- package/tests/client/portal.test.ripple +29 -15
- package/tests/client/ref.test.ripple +9 -3
- package/tests/client/set.test.ripple +111 -111
- package/tests/client/tracked-expression.test.ripple +16 -17
- package/tests/client/url/url.derived.test.ripple +19 -9
- package/tests/client/url/url.parsing.test.ripple +24 -8
- package/tests/client/url/url.partial-removal.test.ripple +12 -4
- package/tests/client/url/url.reactivity.test.ripple +63 -25
- package/tests/client/url/url.serialization.test.ripple +18 -6
- package/tests/client/url-search-params/url-search-params.derived.test.ripple +10 -6
- package/tests/client/url-search-params/url-search-params.iteration.test.ripple +3 -1
- package/tests/client/url-search-params/url-search-params.mutation.test.ripple +26 -14
- package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +3 -1
- package/tests/server/await.test.ripple +23 -22
- package/tests/server/basic.test.ripple +1 -1
- package/tests/server/compiler.test.ripple +3 -7
- package/tests/server/composite.test.ripple +38 -36
- package/tests/server/for.test.ripple +9 -5
- package/tests/server/if.test.ripple +1 -1
- package/tests/server/streaming-ssr.test.ripple +67 -0
- package/types/server.d.ts +5 -4
|
@@ -2,163 +2,199 @@ import { flushSync, TrackedObject } from 'ripple';
|
|
|
2
2
|
import { TRACKED_OBJECT } from '../../src/runtime/internal/client/constants.js';
|
|
3
3
|
|
|
4
4
|
describe('TrackedObject', () => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
it('makes new properties reactive', () => {
|
|
6
|
+
component ObjectTest() {
|
|
7
|
+
const obj = new TrackedObject<{ a: number }>({});
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
obj.a = 0;
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
<pre>{obj.a}</pre>
|
|
12
|
+
<button
|
|
13
|
+
onClick={() => {
|
|
14
|
+
obj.a++;
|
|
15
|
+
}}
|
|
16
|
+
>
|
|
17
|
+
{'Increment A'}
|
|
18
|
+
</button>
|
|
19
|
+
}
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
render(ObjectTest);
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
const pre1 = container.querySelectorAll('pre')[0];
|
|
24
|
+
const button = container.querySelectorAll('button')[0];
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
expect(pre1.textContent).toBe('0');
|
|
21
27
|
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
button.click();
|
|
29
|
+
flushSync();
|
|
24
30
|
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
expect(pre1.textContent).toBe('1');
|
|
32
|
+
});
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
it('makes existing object properties reactive', () => {
|
|
35
|
+
component ObjectTest() {
|
|
36
|
+
const obj = new TrackedObject({ a: 0 });
|
|
31
37
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
<pre>{obj.a}</pre>
|
|
39
|
+
<button
|
|
40
|
+
onClick={() => {
|
|
41
|
+
obj.a++;
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
{'Increment A'}
|
|
45
|
+
</button>
|
|
46
|
+
}
|
|
35
47
|
|
|
36
|
-
|
|
48
|
+
render(ObjectTest);
|
|
37
49
|
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
const pre1 = container.querySelectorAll('pre')[0];
|
|
51
|
+
const button = container.querySelectorAll('button')[0];
|
|
40
52
|
|
|
41
|
-
|
|
53
|
+
expect(pre1.textContent).toBe('0');
|
|
42
54
|
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
button.click();
|
|
56
|
+
flushSync();
|
|
45
57
|
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
expect(pre1.textContent).toBe('1');
|
|
59
|
+
});
|
|
48
60
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
it('checks if property exists via the has trap', () => {
|
|
62
|
+
component ObjectTest() {
|
|
63
|
+
const obj = new TrackedObject<{ a: number; b: number }>({ b: 1 });
|
|
52
64
|
|
|
53
|
-
|
|
65
|
+
obj.a = 0;
|
|
54
66
|
|
|
55
|
-
|
|
56
|
-
|
|
67
|
+
<pre>{'a' in obj && 'b' in obj}</pre>
|
|
68
|
+
}
|
|
57
69
|
|
|
58
|
-
|
|
70
|
+
render(ObjectTest);
|
|
59
71
|
|
|
60
|
-
|
|
72
|
+
const pre1 = container.querySelectorAll('pre')[0];
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
expect(pre1.textContent).toBe('true');
|
|
75
|
+
});
|
|
64
76
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
it('deletes properties via the delete trap', () => {
|
|
78
|
+
component ObjectTest() {
|
|
79
|
+
const obj = new TrackedObject<{ a?: number; b: number }>({ a: 0, b: 1 });
|
|
68
80
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
<pre>{String(obj.a)}</pre>
|
|
82
|
+
<button
|
|
83
|
+
onClick={() => {
|
|
84
|
+
delete obj.a;
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
{'Delete A'}
|
|
88
|
+
</button>
|
|
89
|
+
}
|
|
72
90
|
|
|
73
|
-
|
|
91
|
+
render(ObjectTest);
|
|
74
92
|
|
|
75
|
-
|
|
76
|
-
|
|
93
|
+
const pre1 = container.querySelectorAll('pre')[0];
|
|
94
|
+
const button = container.querySelectorAll('button')[0];
|
|
77
95
|
|
|
78
|
-
|
|
96
|
+
expect(pre1.textContent).toBe('0');
|
|
79
97
|
|
|
80
|
-
|
|
81
|
-
|
|
98
|
+
button.click();
|
|
99
|
+
flushSync();
|
|
82
100
|
|
|
83
|
-
|
|
84
|
-
|
|
101
|
+
expect(pre1.textContent).toBe('undefined');
|
|
102
|
+
});
|
|
85
103
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
104
|
+
it('checks if non-existent property is reactive when added later', () => {
|
|
105
|
+
component ObjectTest() {
|
|
106
|
+
const obj = new TrackedObject<{ a?: number }>({});
|
|
89
107
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
108
|
+
<pre>{String(obj.a)}</pre>
|
|
109
|
+
<button
|
|
110
|
+
onClick={() => {
|
|
111
|
+
obj.a = 1;
|
|
112
|
+
}}
|
|
113
|
+
>
|
|
114
|
+
{'Add A'}
|
|
115
|
+
</button>
|
|
116
|
+
}
|
|
93
117
|
|
|
94
|
-
|
|
118
|
+
render(ObjectTest);
|
|
95
119
|
|
|
96
|
-
|
|
97
|
-
|
|
120
|
+
const pre1 = container.querySelectorAll('pre')[0];
|
|
121
|
+
const button = container.querySelectorAll('button')[0];
|
|
98
122
|
|
|
99
|
-
|
|
123
|
+
expect(pre1.textContent).toBe('undefined');
|
|
100
124
|
|
|
101
|
-
|
|
102
|
-
|
|
125
|
+
button.click();
|
|
126
|
+
flushSync();
|
|
103
127
|
|
|
104
|
-
|
|
105
|
-
|
|
128
|
+
expect(pre1.textContent).toBe('1');
|
|
129
|
+
});
|
|
106
130
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
131
|
+
it('checks that deeply nested objects are not proxied or reactive', () => {
|
|
132
|
+
component ObjectTest() {
|
|
133
|
+
const obj = new TrackedObject({ a: { b: 1 } });
|
|
110
134
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
135
|
+
<pre>{String(obj.a.b)}</pre>
|
|
136
|
+
<button
|
|
137
|
+
onClick={() => {
|
|
138
|
+
obj.a.b++;
|
|
139
|
+
}}
|
|
140
|
+
>
|
|
141
|
+
{'Increment B'}
|
|
142
|
+
</button>
|
|
143
|
+
}
|
|
114
144
|
|
|
115
|
-
|
|
145
|
+
render(ObjectTest);
|
|
116
146
|
|
|
117
|
-
|
|
118
|
-
|
|
147
|
+
const pre1 = container.querySelectorAll('pre')[0];
|
|
148
|
+
const button = container.querySelectorAll('button')[0];
|
|
119
149
|
|
|
120
|
-
|
|
150
|
+
expect(pre1.textContent).toBe('1');
|
|
121
151
|
|
|
122
|
-
|
|
123
|
-
|
|
152
|
+
button.click();
|
|
153
|
+
flushSync();
|
|
124
154
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
155
|
+
// remains unchanged
|
|
156
|
+
expect(pre1.textContent).toBe('1');
|
|
157
|
+
});
|
|
128
158
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
159
|
+
it('checks if TRACKED_OBJECT symbol is present on TrackedObject instances', () => {
|
|
160
|
+
component ObjectTest() {
|
|
161
|
+
const obj = new TrackedObject({ a: 0 });
|
|
132
162
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
163
|
+
expect(obj[TRACKED_OBJECT]).toBe(true);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
136
166
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
167
|
+
it('uses the hash syntax for creating TrackedObject', () => {
|
|
168
|
+
component ObjectTest() {
|
|
169
|
+
const obj = #{ a: 0, b: 1, c: { d: { e: 8 } } };
|
|
140
170
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
171
|
+
<pre>{obj.a}</pre>
|
|
172
|
+
<pre>{TRACKED_OBJECT in obj}</pre>
|
|
173
|
+
<pre>{JSON.stringify(obj)}</pre>
|
|
174
|
+
<button
|
|
175
|
+
onClick={() => {
|
|
176
|
+
obj.a++;
|
|
177
|
+
}}
|
|
178
|
+
>
|
|
179
|
+
{'Increment A'}
|
|
180
|
+
</button>
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
render(ObjectTest);
|
|
184
|
+
|
|
185
|
+
const pre1 = container.querySelectorAll('pre')[0];
|
|
186
|
+
const pre2 = container.querySelectorAll('pre')[1];
|
|
187
|
+
const pre3 = container.querySelectorAll('pre')[2];
|
|
188
|
+
const button = container.querySelectorAll('button')[0];
|
|
146
189
|
|
|
147
|
-
|
|
190
|
+
expect(pre1.textContent).toBe('0');
|
|
191
|
+
expect(pre2.textContent).toBe('true');
|
|
192
|
+
expect(pre3.textContent).toBe('{"a":0,"b":1,"c":{"d":{"e":8}}}');
|
|
148
193
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
expect(pre2.textContent).toBe('true');
|
|
156
|
-
expect(pre3.textContent).toBe('{"a":0,"b":1,"c":{"d":{"e":8}}}');
|
|
157
|
-
|
|
158
|
-
button.click();
|
|
159
|
-
flushSync();
|
|
160
|
-
|
|
161
|
-
expect(pre1.textContent).toBe('1');
|
|
162
|
-
expect(pre3.textContent).toBe('{"a":1,"b":1,"c":{"d":{"e":8}}}');
|
|
163
|
-
})
|
|
194
|
+
button.click();
|
|
195
|
+
flushSync();
|
|
196
|
+
|
|
197
|
+
expect(pre1.textContent).toBe('1');
|
|
198
|
+
expect(pre3.textContent).toBe('{"a":1,"b":1,"c":{"d":{"e":8}}}');
|
|
199
|
+
});
|
|
164
200
|
});
|
|
@@ -4,7 +4,7 @@ describe('Portal', () => {
|
|
|
4
4
|
afterEach(() => {
|
|
5
5
|
// Clean up any leftover portal content from document.body
|
|
6
6
|
const portals = document.body.querySelectorAll('.test-portal');
|
|
7
|
-
portals.forEach(el => el.remove());
|
|
7
|
+
portals.forEach((el) => el.remove());
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
it('renders portal content to target element', () => {
|
|
@@ -12,7 +12,9 @@ describe('Portal', () => {
|
|
|
12
12
|
document.body.appendChild(target);
|
|
13
13
|
|
|
14
14
|
component TestPortal() {
|
|
15
|
-
<Portal
|
|
15
|
+
<Portal {target}>
|
|
16
|
+
<div class="test-portal">{'Portal works!'}</div>
|
|
17
|
+
</Portal>
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
render(TestPortal);
|
|
@@ -27,7 +29,9 @@ describe('Portal', () => {
|
|
|
27
29
|
|
|
28
30
|
it('renders portal content to document.body', () => {
|
|
29
31
|
component TestPortal() {
|
|
30
|
-
<Portal target={document.body}
|
|
32
|
+
<Portal target={document.body}>
|
|
33
|
+
<div class="test-portal">{'In document.body!'}</div>
|
|
34
|
+
</Portal>
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
render(TestPortal);
|
|
@@ -45,7 +49,9 @@ describe('Portal', () => {
|
|
|
45
49
|
let open = track(true);
|
|
46
50
|
|
|
47
51
|
if (@open) {
|
|
48
|
-
<Portal target={document.body}
|
|
52
|
+
<Portal target={document.body}>
|
|
53
|
+
<div class="test-portal">{'Conditional content'}</div>
|
|
54
|
+
</Portal>
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
<button onClick={() => @open = false}>{'Close'}</button>
|
|
@@ -69,10 +75,12 @@ describe('Portal', () => {
|
|
|
69
75
|
let open = track(false);
|
|
70
76
|
|
|
71
77
|
if (@open) {
|
|
72
|
-
<Portal target={document.body}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
<Portal target={document.body}>
|
|
79
|
+
<div class="test-portal">
|
|
80
|
+
{'Content'}
|
|
81
|
+
<button onClick={() => @open = false}>{'Close'}</button>
|
|
82
|
+
</div>
|
|
83
|
+
</Portal>
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
if (!@open) {
|
|
@@ -106,9 +114,13 @@ describe('Portal', () => {
|
|
|
106
114
|
document.body.appendChild(target2);
|
|
107
115
|
|
|
108
116
|
component TestMultiPortal() {
|
|
109
|
-
<Portal target={target1}
|
|
117
|
+
<Portal target={target1}>
|
|
118
|
+
<div class="test-portal">{'Portal 1 content'}</div>
|
|
119
|
+
</Portal>
|
|
110
120
|
|
|
111
|
-
<Portal target={target2}
|
|
121
|
+
<Portal target={target2}>
|
|
122
|
+
<div class="test-portal">{'Portal 2 content'}</div>
|
|
123
|
+
</Portal>
|
|
112
124
|
}
|
|
113
125
|
|
|
114
126
|
render(TestMultiPortal);
|
|
@@ -128,11 +140,13 @@ describe('Portal', () => {
|
|
|
128
140
|
component TestReactivePortal() {
|
|
129
141
|
let count = track(0);
|
|
130
142
|
|
|
131
|
-
<Portal target={document.body}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
143
|
+
<Portal target={document.body}>
|
|
144
|
+
<div class="test-portal">
|
|
145
|
+
{'Count: '}
|
|
146
|
+
{String(@count)}
|
|
147
|
+
<button onClick={() => @count++}>{'Increment'}</button>
|
|
148
|
+
</div>
|
|
149
|
+
</Portal>
|
|
136
150
|
}
|
|
137
151
|
|
|
138
152
|
render(TestReactivePortal);
|
|
@@ -6,7 +6,13 @@ describe('refs', () => {
|
|
|
6
6
|
let div: HTMLDivElement | undefined;
|
|
7
7
|
|
|
8
8
|
component Component() {
|
|
9
|
-
<div
|
|
9
|
+
<div
|
|
10
|
+
{ref (node: HTMLDivElement) => {
|
|
11
|
+
div = node;
|
|
12
|
+
}}
|
|
13
|
+
>
|
|
14
|
+
{'Hello World'}
|
|
15
|
+
</div>
|
|
10
16
|
}
|
|
11
17
|
render(Component);
|
|
12
18
|
flushSync();
|
|
@@ -49,9 +55,9 @@ describe('refs', () => {
|
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
const props = {
|
|
52
|
-
id:
|
|
58
|
+
id: 'example',
|
|
53
59
|
@value,
|
|
54
|
-
[createRefKey()]: inputRef
|
|
60
|
+
[createRefKey()]: inputRef,
|
|
55
61
|
};
|
|
56
62
|
|
|
57
63
|
<input type="text" {...props} />
|