ripple 0.2.208 → 0.2.210
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 +43 -0
- package/README.md +2 -1
- package/package.json +2 -6
- package/shims/rollup-estree-types.d.ts +1 -1
- package/src/compiler/index.d.ts +1 -0
- package/src/compiler/index.js +7 -1
- package/src/compiler/phases/1-parse/index.js +15 -6
- package/src/compiler/phases/2-analyze/css-analyze.js +100 -104
- package/src/compiler/phases/2-analyze/index.js +215 -2
- package/src/compiler/phases/3-transform/client/index.js +388 -50
- package/src/compiler/phases/3-transform/segments.js +123 -39
- package/src/compiler/phases/3-transform/server/index.js +266 -13
- package/src/compiler/types/index.d.ts +16 -3
- package/src/compiler/utils.js +1 -15
- package/src/constants.js +0 -2
- package/src/helpers.d.ts +4 -0
- package/src/html-tree-validation.js +211 -0
- package/src/jsx-runtime.d.ts +260 -259
- package/src/jsx-runtime.js +12 -12
- package/src/runtime/array.js +17 -17
- package/src/runtime/create-subscriber.js +1 -1
- package/src/runtime/index-client.js +1 -5
- package/src/runtime/index-server.js +15 -0
- package/src/runtime/internal/client/compat.js +3 -3
- package/src/runtime/internal/client/composite.js +6 -1
- package/src/runtime/internal/client/head.js +50 -4
- package/src/runtime/internal/client/html.js +73 -12
- package/src/runtime/internal/client/hydration.js +12 -0
- package/src/runtime/internal/client/index.js +1 -1
- package/src/runtime/internal/client/portal.js +54 -29
- package/src/runtime/internal/client/rpc.js +3 -1
- package/src/runtime/internal/client/switch.js +5 -0
- package/src/runtime/internal/client/template.js +117 -11
- package/src/runtime/internal/client/try.js +1 -0
- package/src/runtime/internal/server/index.js +113 -1
- package/src/runtime/internal/server/rpc.js +4 -4
- package/src/runtime/map.js +2 -2
- package/src/runtime/object.js +6 -6
- package/src/runtime/proxy.js +12 -11
- package/src/runtime/reactive-value.js +9 -1
- package/src/runtime/set.js +12 -7
- package/src/runtime/url-search-params.js +0 -1
- package/src/server/index.js +4 -0
- package/src/utils/hashing.js +15 -0
- package/src/utils/normalize_css_property_name.js +1 -1
- package/tests/client/array/array.mutations.test.ripple +8 -8
- package/tests/client/basic/basic.errors.test.ripple +28 -0
- package/tests/client/basic/basic.events.test.ripple +6 -3
- package/tests/client/basic/basic.utilities.test.ripple +1 -1
- package/tests/client/compiler/compiler.regex.test.ripple +10 -8
- package/tests/client/composite/composite.generics.test.ripple +5 -2
- package/tests/client/dynamic-elements.test.ripple +30 -1
- package/tests/client/function-overload-import.ripple +6 -7
- package/tests/client/html.test.ripple +0 -1
- package/tests/client/object.test.ripple +2 -2
- package/tests/client/portal.test.ripple +3 -3
- package/tests/client/return.test.ripple +2500 -0
- package/tests/client/try.test.ripple +69 -0
- package/tests/client/typescript-generics.test.ripple +1 -1
- package/tests/client/url/url.derived.test.ripple +1 -1
- package/tests/client/url/url.parsing.test.ripple +3 -3
- package/tests/client/url/url.partial-removal.test.ripple +7 -7
- package/tests/client/url/url.reactivity.test.ripple +15 -15
- package/tests/client/url/url.serialization.test.ripple +2 -2
- package/tests/hydration/basic.test.js +23 -0
- package/tests/hydration/build-components.js +10 -4
- package/tests/hydration/compiled/client/basic.js +165 -3
- package/tests/hydration/compiled/client/for.js +1140 -23
- package/tests/hydration/compiled/client/head.js +234 -0
- package/tests/hydration/compiled/client/html.js +135 -0
- package/tests/hydration/compiled/client/portal.js +172 -0
- package/tests/hydration/compiled/client/reactivity.js +3 -1
- package/tests/hydration/compiled/client/return.js +1976 -0
- package/tests/hydration/compiled/client/switch.js +162 -0
- package/tests/hydration/compiled/server/basic.js +249 -0
- package/tests/hydration/compiled/server/events.js +1 -1
- package/tests/hydration/compiled/server/for.js +891 -1
- package/tests/hydration/compiled/server/head.js +291 -0
- package/tests/hydration/compiled/server/html.js +133 -0
- package/tests/hydration/compiled/server/if.js +1 -1
- package/tests/hydration/compiled/server/portal.js +250 -0
- package/tests/hydration/compiled/server/reactivity.js +1 -1
- package/tests/hydration/compiled/server/return.js +1969 -0
- package/tests/hydration/compiled/server/switch.js +130 -0
- package/tests/hydration/components/basic.ripple +55 -0
- package/tests/hydration/components/for.ripple +403 -0
- package/tests/hydration/components/head.ripple +111 -0
- package/tests/hydration/components/html.ripple +38 -0
- package/tests/hydration/components/portal.ripple +49 -0
- package/tests/hydration/components/return.ripple +564 -0
- package/tests/hydration/components/switch.ripple +51 -0
- package/tests/hydration/for.test.js +363 -0
- package/tests/hydration/head.test.js +105 -0
- package/tests/hydration/html.test.js +46 -0
- package/tests/hydration/portal.test.js +71 -0
- package/tests/hydration/return.test.js +544 -0
- package/tests/hydration/switch.test.js +42 -0
- package/tests/server/basic.attributes.test.ripple +1 -1
- package/tests/server/compiler.test.ripple +22 -0
- package/tests/server/composite.test.ripple +5 -2
- package/tests/server/html-nesting-validation.test.ripple +237 -0
- package/tests/server/return.test.ripple +1379 -0
- package/tests/setup-hydration.js +6 -1
- package/tests/utils/escaping.test.js +3 -1
- package/tests/utils/normalize_css_property_name.test.js +0 -1
- package/tests/utils/patterns.test.js +6 -2
- package/tests/utils/sanitize_template_string.test.js +3 -2
- package/types/server.d.ts +16 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { flushSync, track, TrackedArray } from 'ripple';
|
|
2
|
+
|
|
3
|
+
describe('try block', () => {
|
|
4
|
+
it('does not crash when async component is used inside try/pending', async () => {
|
|
5
|
+
component App() {
|
|
6
|
+
try {
|
|
7
|
+
<AsyncChild />
|
|
8
|
+
} pending {
|
|
9
|
+
<p>{'loading...'}</p>
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
component AsyncChild() {
|
|
14
|
+
let data = await Promise.resolve(['a', 'b', 'c']);
|
|
15
|
+
|
|
16
|
+
<ul>
|
|
17
|
+
for (let item of data) {
|
|
18
|
+
<li>{item}</li>
|
|
19
|
+
}
|
|
20
|
+
</ul>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
render(App);
|
|
24
|
+
|
|
25
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
26
|
+
flushSync();
|
|
27
|
+
|
|
28
|
+
const items = container.querySelectorAll('li');
|
|
29
|
+
expect(items.length).toBe(3);
|
|
30
|
+
expect(items[0].textContent).toBe('a');
|
|
31
|
+
expect(items[1].textContent).toBe('b');
|
|
32
|
+
expect(items[2].textContent).toBe('c');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it(
|
|
36
|
+
'does not crash when async component with tracked state is used inside try/pending',
|
|
37
|
+
async () => {
|
|
38
|
+
component App() {
|
|
39
|
+
let query = track('');
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
<FilteredList {query} />
|
|
43
|
+
} pending {
|
|
44
|
+
<p>{'loading...'}</p>
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
component FilteredList({ query }: { query: any }) {
|
|
49
|
+
let items = await Promise.resolve(['apple', 'banana', 'cherry']);
|
|
50
|
+
let list = TrackedArray.from(items);
|
|
51
|
+
let filtered = track(() => list.filter((item: string) => item.includes(@query)));
|
|
52
|
+
|
|
53
|
+
<ul>
|
|
54
|
+
for (let item of @filtered) {
|
|
55
|
+
<li>{item}</li>
|
|
56
|
+
}
|
|
57
|
+
</ul>
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
render(App);
|
|
61
|
+
|
|
62
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
63
|
+
flushSync();
|
|
64
|
+
|
|
65
|
+
const listItems = container.querySelectorAll('li');
|
|
66
|
+
expect(listItems.length).toBe(3);
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
});
|
|
@@ -120,7 +120,7 @@ describe('generic patterns', () => {
|
|
|
120
120
|
component App() {
|
|
121
121
|
function getBuilder() {
|
|
122
122
|
return <T,>() => ({
|
|
123
|
-
build<V,T,U>(): { build: () => V; data: T; key: U } {
|
|
123
|
+
build<V, T, U>(): { build: () => V; data: T; key: U } {
|
|
124
124
|
return {
|
|
125
125
|
build(): V {
|
|
126
126
|
return 'test' as V;
|
|
@@ -7,7 +7,7 @@ describe('TrackedURL > derived', () => {
|
|
|
7
7
|
let userId = track(() => url.pathname.split('/').pop());
|
|
8
8
|
let activeTab = track(() => url.searchParams.get('tab'));
|
|
9
9
|
|
|
10
|
-
<button onClick={() => url.pathname = '/users/456'}>{'Change User'}</button>
|
|
10
|
+
<button onClick={() => (url.pathname = '/users/456')}>{'Change User'}</button>
|
|
11
11
|
<button onClick={() => url.searchParams.set('tab', 'settings')}>{'Change Tab'}</button>
|
|
12
12
|
<pre>{`User ID: ${@userId}`}</pre>
|
|
13
13
|
<pre>{`Active Tab: ${@activeTab}`}</pre>
|
|
@@ -85,7 +85,7 @@ describe('TrackedURL > parsing', () => {
|
|
|
85
85
|
component URLTest() {
|
|
86
86
|
const url = new TrackedURL('https://192.168.1.1:8080/path');
|
|
87
87
|
|
|
88
|
-
<button onClick={() => url.hostname = '10.0.0.1'}>{'Change IP'}</button>
|
|
88
|
+
<button onClick={() => (url.hostname = '10.0.0.1')}>{'Change IP'}</button>
|
|
89
89
|
<pre>{url.href}</pre>
|
|
90
90
|
<pre>{url.hostname}</pre>
|
|
91
91
|
}
|
|
@@ -110,7 +110,7 @@ describe('TrackedURL > parsing', () => {
|
|
|
110
110
|
component URLTest() {
|
|
111
111
|
const url = new TrackedURL('http://localhost:3000/api/data');
|
|
112
112
|
|
|
113
|
-
<button onClick={() => url.port = '8080'}>{'Change Port'}</button>
|
|
113
|
+
<button onClick={() => (url.port = '8080')}>{'Change Port'}</button>
|
|
114
114
|
<pre>{url.href}</pre>
|
|
115
115
|
<pre>{url.hostname}</pre>
|
|
116
116
|
<pre>{url.port}</pre>
|
|
@@ -138,7 +138,7 @@ describe('TrackedURL > parsing', () => {
|
|
|
138
138
|
component URLTest() {
|
|
139
139
|
const url = new TrackedURL('https://example.com/api/v1/users/123/profile');
|
|
140
140
|
|
|
141
|
-
<button onClick={() => url.pathname = '/api/v2/users/456/settings'}>{'Change Path'}</button>
|
|
141
|
+
<button onClick={() => (url.pathname = '/api/v2/users/456/settings')}>{'Change Path'}</button>
|
|
142
142
|
<pre>{url.pathname}</pre>
|
|
143
143
|
<pre>{url.href}</pre>
|
|
144
144
|
}
|
|
@@ -7,7 +7,7 @@ describe('TrackedURL > partials/removal', () => {
|
|
|
7
7
|
|
|
8
8
|
<pre>{url.port}</pre>
|
|
9
9
|
<pre>{url.host}</pre>
|
|
10
|
-
<button onClick={() => url.port = '8080'}>{'Add Port'}</button>
|
|
10
|
+
<button onClick={() => (url.port = '8080')}>{'Add Port'}</button>
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
render(URLTest);
|
|
@@ -56,7 +56,7 @@ describe('TrackedURL > partials/removal', () => {
|
|
|
56
56
|
const url = new TrackedURL('https://example.com/path');
|
|
57
57
|
|
|
58
58
|
<pre>{url.hash}</pre>
|
|
59
|
-
<button onClick={() => url.hash = '#section'}>{'Add Hash'}</button>
|
|
59
|
+
<button onClick={() => (url.hash = '#section')}>{'Add Hash'}</button>
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
render(URLTest);
|
|
@@ -77,7 +77,7 @@ describe('TrackedURL > partials/removal', () => {
|
|
|
77
77
|
component URLTest() {
|
|
78
78
|
const url = new TrackedURL('https://example.com:8080/path');
|
|
79
79
|
|
|
80
|
-
<button onClick={() => url.port = ''}>{'Remove Port'}</button>
|
|
80
|
+
<button onClick={() => (url.port = '')}>{'Remove Port'}</button>
|
|
81
81
|
<pre>{url.href}</pre>
|
|
82
82
|
<pre>{url.port}</pre>
|
|
83
83
|
}
|
|
@@ -102,7 +102,7 @@ describe('TrackedURL > partials/removal', () => {
|
|
|
102
102
|
component URLTest() {
|
|
103
103
|
const url = new TrackedURL('https://example.com/path#section');
|
|
104
104
|
|
|
105
|
-
<button onClick={() => url.hash = ''}>{'Remove Hash'}</button>
|
|
105
|
+
<button onClick={() => (url.hash = '')}>{'Remove Hash'}</button>
|
|
106
106
|
<pre>{url.href}</pre>
|
|
107
107
|
<pre>{url.hash}</pre>
|
|
108
108
|
}
|
|
@@ -129,7 +129,7 @@ describe('TrackedURL > partials/removal', () => {
|
|
|
129
129
|
component URLTest() {
|
|
130
130
|
const url = new TrackedURL('https://example.com/path?foo=bar');
|
|
131
131
|
|
|
132
|
-
<button onClick={() => url.search = ''}>{'Remove Search'}</button>
|
|
132
|
+
<button onClick={() => (url.search = '')}>{'Remove Search'}</button>
|
|
133
133
|
<pre>{url.href}</pre>
|
|
134
134
|
<pre>{url.search}</pre>
|
|
135
135
|
<pre>{url.searchParams.size}</pre>
|
|
@@ -159,7 +159,7 @@ describe('TrackedURL > partials/removal', () => {
|
|
|
159
159
|
component URLTest() {
|
|
160
160
|
const url = new TrackedURL('https://example.com/path');
|
|
161
161
|
|
|
162
|
-
<button onClick={() => url.hash = 'section'}>{'Set Hash'}</button>
|
|
162
|
+
<button onClick={() => (url.hash = 'section')}>{'Set Hash'}</button>
|
|
163
163
|
<pre>{url.hash}</pre>
|
|
164
164
|
<pre>{url.href}</pre>
|
|
165
165
|
}
|
|
@@ -185,7 +185,7 @@ describe('TrackedURL > partials/removal', () => {
|
|
|
185
185
|
component URLTest() {
|
|
186
186
|
const url = new TrackedURL('https://example.com/path');
|
|
187
187
|
|
|
188
|
-
<button onClick={() => url.search = 'foo=bar'}>{'Set Search'}</button>
|
|
188
|
+
<button onClick={() => (url.search = 'foo=bar')}>{'Set Search'}</button>
|
|
189
189
|
<pre>{url.search}</pre>
|
|
190
190
|
<pre>{url.href}</pre>
|
|
191
191
|
}
|
|
@@ -5,7 +5,7 @@ describe('TrackedURL > reactivity', () => {
|
|
|
5
5
|
component URLTest() {
|
|
6
6
|
const url = new TrackedURL('https://example.com/path');
|
|
7
7
|
|
|
8
|
-
<button onClick={() => url.protocol = 'http:'}>{'Change Protocol'}</button>
|
|
8
|
+
<button onClick={() => (url.protocol = 'http:')}>{'Change Protocol'}</button>
|
|
9
9
|
<pre>{url.href}</pre>
|
|
10
10
|
<pre>{url.protocol}</pre>
|
|
11
11
|
<pre>{url.origin}</pre>
|
|
@@ -33,7 +33,7 @@ describe('TrackedURL > reactivity', () => {
|
|
|
33
33
|
component URLTest() {
|
|
34
34
|
const url = new TrackedURL('https://example.com/path');
|
|
35
35
|
|
|
36
|
-
<button onClick={() => url.hostname = 'newdomain.com'}>{'Change Hostname'}</button>
|
|
36
|
+
<button onClick={() => (url.hostname = 'newdomain.com')}>{'Change Hostname'}</button>
|
|
37
37
|
<pre>{url.href}</pre>
|
|
38
38
|
<pre>{url.hostname}</pre>
|
|
39
39
|
<pre>{url.host}</pre>
|
|
@@ -61,7 +61,7 @@ describe('TrackedURL > reactivity', () => {
|
|
|
61
61
|
component URLTest() {
|
|
62
62
|
const url = new TrackedURL('https://example.com:8080/path');
|
|
63
63
|
|
|
64
|
-
<button onClick={() => url.port = '9090'}>{'Change Port'}</button>
|
|
64
|
+
<button onClick={() => (url.port = '9090')}>{'Change Port'}</button>
|
|
65
65
|
<pre>{url.href}</pre>
|
|
66
66
|
<pre>{url.port}</pre>
|
|
67
67
|
<pre>{url.host}</pre>
|
|
@@ -89,7 +89,7 @@ describe('TrackedURL > reactivity', () => {
|
|
|
89
89
|
component URLTest() {
|
|
90
90
|
const url = new TrackedURL('https://example.com:8080/path');
|
|
91
91
|
|
|
92
|
-
<button onClick={() => url.host = 'newdomain.com:9090'}>{'Change Host'}</button>
|
|
92
|
+
<button onClick={() => (url.host = 'newdomain.com:9090')}>{'Change Host'}</button>
|
|
93
93
|
<pre>{url.href}</pre>
|
|
94
94
|
<pre>{url.host}</pre>
|
|
95
95
|
<pre>{url.hostname}</pre>
|
|
@@ -122,7 +122,7 @@ describe('TrackedURL > reactivity', () => {
|
|
|
122
122
|
component URLTest() {
|
|
123
123
|
const url = new TrackedURL('https://example.com/old-path');
|
|
124
124
|
|
|
125
|
-
<button onClick={() => url.pathname = '/new-path'}>{'Change Pathname'}</button>
|
|
125
|
+
<button onClick={() => (url.pathname = '/new-path')}>{'Change Pathname'}</button>
|
|
126
126
|
<pre>{url.href}</pre>
|
|
127
127
|
<pre>{url.pathname}</pre>
|
|
128
128
|
}
|
|
@@ -147,7 +147,7 @@ describe('TrackedURL > reactivity', () => {
|
|
|
147
147
|
component URLTest() {
|
|
148
148
|
const url = new TrackedURL('https://example.com/path?foo=bar');
|
|
149
149
|
|
|
150
|
-
<button onClick={() => url.search = '?baz=qux'}>{'Change Search'}</button>
|
|
150
|
+
<button onClick={() => (url.search = '?baz=qux')}>{'Change Search'}</button>
|
|
151
151
|
<pre>{url.href}</pre>
|
|
152
152
|
<pre>{url.search}</pre>
|
|
153
153
|
}
|
|
@@ -176,7 +176,7 @@ describe('TrackedURL > reactivity', () => {
|
|
|
176
176
|
component URLTest() {
|
|
177
177
|
const url = new TrackedURL('https://example.com/path#section1');
|
|
178
178
|
|
|
179
|
-
<button onClick={() => url.hash = '#section2'}>{'Change Hash'}</button>
|
|
179
|
+
<button onClick={() => (url.hash = '#section2')}>{'Change Hash'}</button>
|
|
180
180
|
<pre>{url.href}</pre>
|
|
181
181
|
<pre>{url.hash}</pre>
|
|
182
182
|
}
|
|
@@ -205,7 +205,7 @@ describe('TrackedURL > reactivity', () => {
|
|
|
205
205
|
component URLTest() {
|
|
206
206
|
const url = new TrackedURL('https://user:pass@example.com/path');
|
|
207
207
|
|
|
208
|
-
<button onClick={() => url.username = 'newuser'}>{'Change Username'}</button>
|
|
208
|
+
<button onClick={() => (url.username = 'newuser')}>{'Change Username'}</button>
|
|
209
209
|
<pre>{url.href}</pre>
|
|
210
210
|
<pre>{url.username}</pre>
|
|
211
211
|
}
|
|
@@ -234,7 +234,7 @@ describe('TrackedURL > reactivity', () => {
|
|
|
234
234
|
component URLTest() {
|
|
235
235
|
const url = new TrackedURL('https://user:pass@example.com/path');
|
|
236
236
|
|
|
237
|
-
<button onClick={() => url.password = 'newpass'}>{'Change Password'}</button>
|
|
237
|
+
<button onClick={() => (url.password = 'newpass')}>{'Change Password'}</button>
|
|
238
238
|
<pre>{url.href}</pre>
|
|
239
239
|
<pre>{url.password}</pre>
|
|
240
240
|
}
|
|
@@ -263,7 +263,7 @@ describe('TrackedURL > reactivity', () => {
|
|
|
263
263
|
component URLTest() {
|
|
264
264
|
const url = new TrackedURL('https://example.com/path?foo=bar#section');
|
|
265
265
|
|
|
266
|
-
<button onClick={() => url.href = 'https://newdomain.com:9090/newpath?baz=qux#newsection'}>
|
|
266
|
+
<button onClick={() => (url.href = 'https://newdomain.com:9090/newpath?baz=qux#newsection')}>
|
|
267
267
|
{'Change Href'}
|
|
268
268
|
</button>
|
|
269
269
|
<pre>{url.href}</pre>
|
|
@@ -309,9 +309,9 @@ describe('TrackedURL > reactivity', () => {
|
|
|
309
309
|
component URLTest() {
|
|
310
310
|
const url = new TrackedURL('https://example.com:8080/path');
|
|
311
311
|
|
|
312
|
-
<button onClick={() => url.protocol = 'http:'}>{'Change Protocol'}</button>
|
|
313
|
-
<button onClick={() => url.hostname = 'newdomain.com'}>{'Change Hostname'}</button>
|
|
314
|
-
<button onClick={() => url.port = '9090'}>{'Change Port'}</button>
|
|
312
|
+
<button onClick={() => (url.protocol = 'http:')}>{'Change Protocol'}</button>
|
|
313
|
+
<button onClick={() => (url.hostname = 'newdomain.com')}>{'Change Hostname'}</button>
|
|
314
|
+
<button onClick={() => (url.port = '9090')}>{'Change Port'}</button>
|
|
315
315
|
<pre>{url.origin}</pre>
|
|
316
316
|
}
|
|
317
317
|
|
|
@@ -386,7 +386,7 @@ describe('TrackedURL > reactivity', () => {
|
|
|
386
386
|
const url = new TrackedURL('https://example.com/path?foo=bar');
|
|
387
387
|
const params = url.searchParams;
|
|
388
388
|
|
|
389
|
-
<button onClick={() => url.search = '?baz=qux&test=value'}>{'Change Search'}</button>
|
|
389
|
+
<button onClick={() => (url.search = '?baz=qux&test=value')}>{'Change Search'}</button>
|
|
390
390
|
<pre>{url.search}</pre>
|
|
391
391
|
<pre>{params.get('foo')}</pre>
|
|
392
392
|
<pre>{params.get('baz')}</pre>
|
|
@@ -456,7 +456,7 @@ describe('TrackedURL > reactivity', () => {
|
|
|
456
456
|
const url = new TrackedURL('https://old.com/old?foo=bar#old');
|
|
457
457
|
const params = url.searchParams;
|
|
458
458
|
|
|
459
|
-
<button onClick={() => url.href = 'https://new.com:9090/new?baz=qux#new'}>
|
|
459
|
+
<button onClick={() => (url.href = 'https://new.com:9090/new?baz=qux#new')}>
|
|
460
460
|
{'Change Href'}
|
|
461
461
|
</button>
|
|
462
462
|
<pre>{params.get('foo')}</pre>
|
|
@@ -5,7 +5,7 @@ describe('TrackedURL > serialization', () => {
|
|
|
5
5
|
component URLTest() {
|
|
6
6
|
const url = new TrackedURL('https://example.com/path?foo=bar#section');
|
|
7
7
|
|
|
8
|
-
<button onClick={() => url.pathname = '/newpath'}>{'Change Pathname'}</button>
|
|
8
|
+
<button onClick={() => (url.pathname = '/newpath')}>{'Change Pathname'}</button>
|
|
9
9
|
<pre>{url.toString()}</pre>
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -31,7 +31,7 @@ describe('TrackedURL > serialization', () => {
|
|
|
31
31
|
component URLTest() {
|
|
32
32
|
const url = new TrackedURL('https://example.com/path?foo=bar');
|
|
33
33
|
|
|
34
|
-
<button onClick={() => url.pathname = '/api'}>{'Change Pathname'}</button>
|
|
34
|
+
<button onClick={() => (url.pathname = '/api')}>{'Change Pathname'}</button>
|
|
35
35
|
<pre>{url.toJSON()}</pre>
|
|
36
36
|
<pre>{JSON.stringify({ url: url.toJSON() })}</pre>
|
|
37
37
|
}
|
|
@@ -58,4 +58,27 @@ describe('hydration > basic', () => {
|
|
|
58
58
|
await hydrateComponent(ServerComponents.ExpressionContent, ClientComponents.ExpressionContent);
|
|
59
59
|
expect(container.innerHTML).toBeHtml('<div>42</div><span>COMPUTED</span>');
|
|
60
60
|
});
|
|
61
|
+
|
|
62
|
+
it('hydrates static child component followed by sibling content', async () => {
|
|
63
|
+
await hydrateComponent(
|
|
64
|
+
ServerComponents.StaticChildWithSiblings,
|
|
65
|
+
ClientComponents.StaticChildWithSiblings,
|
|
66
|
+
);
|
|
67
|
+
expect(container.querySelector('.sr-only')?.textContent).toBe('heading');
|
|
68
|
+
expect(container.querySelectorAll('.subtitle').length).toBe(2);
|
|
69
|
+
expect(container.querySelector('.sibling1')?.textContent).toBe('bar');
|
|
70
|
+
expect(container.querySelector('.sibling2')?.textContent).toBe('bar');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('hydrates website-like component structure', async () => {
|
|
74
|
+
await hydrateComponent(ServerComponents.WebsiteIndex, ClientComponents.WebsiteIndex);
|
|
75
|
+
expect(container.querySelector('.sr-only')?.textContent).toBe('Ripple');
|
|
76
|
+
expect(container.querySelector('.logo')).toBeTruthy();
|
|
77
|
+
expect(container.querySelector('.subtitle')?.textContent).toBe(
|
|
78
|
+
'the elegant TypeScript UI framework',
|
|
79
|
+
);
|
|
80
|
+
expect(container.querySelectorAll('.social-links').length).toBe(2);
|
|
81
|
+
expect(container.querySelector('.playground-link')?.textContent).toBe('Playground');
|
|
82
|
+
expect(container.querySelector('.content')).toBeTruthy();
|
|
83
|
+
});
|
|
61
84
|
});
|
|
@@ -20,18 +20,24 @@ const serverOutDir = join(__dirname, 'compiled', 'server');
|
|
|
20
20
|
/**
|
|
21
21
|
* Transform server-compiled code to use server runtime imports.
|
|
22
22
|
* This is necessary because vitest runs with browser conditions, but
|
|
23
|
-
* server-compiled code needs server's track() which
|
|
23
|
+
* server-compiled code needs server's track() and Portal which have different internals.
|
|
24
24
|
* @param {string} code - The compiled server code
|
|
25
25
|
* @returns {string} - Transformed code with server-compatible imports
|
|
26
26
|
*/
|
|
27
27
|
function transformServerImports(code) {
|
|
28
28
|
// Replace `import { track } from 'ripple'` with server version
|
|
29
|
-
//
|
|
29
|
+
// Replace `import { Portal } from 'ripple'` with server version
|
|
30
|
+
// Use 'ripple/server' which always points to the server runtime,
|
|
30
31
|
// bypassing the browser/default condition resolution
|
|
31
|
-
|
|
32
|
+
let transformed = code.replace(
|
|
32
33
|
/import\s*\{\s*track\s*\}\s*from\s*['"]ripple['"]/g,
|
|
33
|
-
"import { track } from 'ripple/
|
|
34
|
+
"import { track } from 'ripple/server'",
|
|
34
35
|
);
|
|
36
|
+
transformed = transformed.replace(
|
|
37
|
+
/import\s*\{\s*Portal\s*\}\s*from\s*['"]ripple['"]/g,
|
|
38
|
+
"import { Portal } from 'ripple/server'",
|
|
39
|
+
);
|
|
40
|
+
return transformed;
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
function buildComponents() {
|
|
@@ -12,6 +12,15 @@ var root_8 = _$_.template(`<!><!>`, 1);
|
|
|
12
12
|
var root_9 = _$_.template(`<div> </div>`, 0);
|
|
13
13
|
var root_10 = _$_.template(`<!>`, 1);
|
|
14
14
|
var root_11 = _$_.template(`<div> </div><span> </span>`, 1);
|
|
15
|
+
var root_12 = _$_.template(`<h1 class="sr-only">heading</h1><p class="subtitle">first paragraph</p><p class="subtitle">second paragraph</p>`, 1);
|
|
16
|
+
var root_13 = _$_.template(`<!><span class="sibling1"> </span><span class="sibling2"> </span>`, 1);
|
|
17
|
+
var root_14 = _$_.template(`<h1 class="sr-only">Ripple</h1><img src="/images/logo.png" alt="Logo" class="logo"><p class="subtitle">the elegant TypeScript UI framework</p>`, 1);
|
|
18
|
+
var root_16 = _$_.template(`<a href="/playground" class="playground-link">Playground</a>`, 0);
|
|
19
|
+
var root_15 = _$_.template(`<div class="social-links"><a href="https://github.com" class="github-link">GitHub</a><a href="https://discord.com" class="discord-link">Discord</a><!></div>`, 0);
|
|
20
|
+
var root_17 = _$_.template(`<main><div class="container"><!></div></main>`, 0);
|
|
21
|
+
var root_18 = _$_.template(`<div class="content"><p>Some content here</p></div>`, 0);
|
|
22
|
+
var root_20 = _$_.template(`<!><!><!><!>`, 1);
|
|
23
|
+
var root_19 = _$_.template(`<!>`, 1);
|
|
15
24
|
|
|
16
25
|
export function StaticText(__anchor, _, __block) {
|
|
17
26
|
_$_.push_component();
|
|
@@ -27,7 +36,8 @@ export function MultipleElements(__anchor, _, __block) {
|
|
|
27
36
|
|
|
28
37
|
var fragment = root_1();
|
|
29
38
|
|
|
30
|
-
_$_.
|
|
39
|
+
_$_.next(2);
|
|
40
|
+
_$_.append(__anchor, fragment, true);
|
|
31
41
|
_$_.pop_component();
|
|
32
42
|
}
|
|
33
43
|
|
|
@@ -45,7 +55,8 @@ export function WithAttributes(__anchor, _, __block) {
|
|
|
45
55
|
|
|
46
56
|
var fragment_1 = root_3();
|
|
47
57
|
|
|
48
|
-
_$_.
|
|
58
|
+
_$_.next();
|
|
59
|
+
_$_.append(__anchor, fragment_1, true);
|
|
49
60
|
_$_.pop_component();
|
|
50
61
|
}
|
|
51
62
|
|
|
@@ -160,10 +171,161 @@ export function ExpressionContent(__anchor, _, __block) {
|
|
|
160
171
|
_$_.pop(span_2);
|
|
161
172
|
}
|
|
162
173
|
|
|
174
|
+
_$_.next();
|
|
175
|
+
|
|
163
176
|
_$_.render(() => {
|
|
164
177
|
_$_.set_text(text_3, _$_.with_scope(__block, () => text.toUpperCase()));
|
|
165
178
|
});
|
|
166
179
|
|
|
167
|
-
_$_.append(__anchor, fragment_4);
|
|
180
|
+
_$_.append(__anchor, fragment_4, true);
|
|
181
|
+
_$_.pop_component();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function StaticHeader(__anchor, _, __block) {
|
|
185
|
+
_$_.push_component();
|
|
186
|
+
|
|
187
|
+
var fragment_5 = root_12();
|
|
188
|
+
|
|
189
|
+
_$_.next(2);
|
|
190
|
+
_$_.append(__anchor, fragment_5, true);
|
|
191
|
+
_$_.pop_component();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function StaticChildWithSiblings(__anchor, _, __block) {
|
|
195
|
+
_$_.push_component();
|
|
196
|
+
|
|
197
|
+
const foo = 'bar';
|
|
198
|
+
var fragment_6 = root_13();
|
|
199
|
+
var node_4 = _$_.first_child_frag(fragment_6);
|
|
200
|
+
|
|
201
|
+
StaticHeader(node_4, {}, _$_.active_block);
|
|
202
|
+
|
|
203
|
+
var span_3 = _$_.sibling(node_4);
|
|
204
|
+
|
|
205
|
+
{
|
|
206
|
+
var text_4 = _$_.child(span_3, true);
|
|
207
|
+
|
|
208
|
+
text_4.nodeValue = foo;
|
|
209
|
+
_$_.pop(span_3);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
var span_4 = _$_.sibling(span_3);
|
|
213
|
+
|
|
214
|
+
{
|
|
215
|
+
var text_5 = _$_.child(span_4, true);
|
|
216
|
+
|
|
217
|
+
text_5.nodeValue = foo;
|
|
218
|
+
_$_.pop(span_4);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
_$_.next();
|
|
222
|
+
_$_.append(__anchor, fragment_6, true);
|
|
223
|
+
_$_.pop_component();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function Header(__anchor, _, __block) {
|
|
227
|
+
_$_.push_component();
|
|
228
|
+
|
|
229
|
+
var fragment_7 = root_14();
|
|
230
|
+
|
|
231
|
+
_$_.next(2);
|
|
232
|
+
_$_.append(__anchor, fragment_7, true);
|
|
233
|
+
_$_.pop_component();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function Actions(__anchor, __props, __block) {
|
|
237
|
+
_$_.push_component();
|
|
238
|
+
|
|
239
|
+
var div_8 = root_15();
|
|
240
|
+
|
|
241
|
+
{
|
|
242
|
+
var a_2 = _$_.child(div_8);
|
|
243
|
+
var a_1 = _$_.sibling(a_2);
|
|
244
|
+
var node_5 = _$_.sibling(a_1);
|
|
245
|
+
|
|
246
|
+
{
|
|
247
|
+
var consequent = (__anchor) => {
|
|
248
|
+
var a_3 = root_16();
|
|
249
|
+
|
|
250
|
+
_$_.append(__anchor, a_3);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
_$_.if(node_5, (__render) => {
|
|
254
|
+
if (_$_.get(_$_.fallback(__props.playgroundVisible, false))) __render(consequent);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
_$_.pop(div_8);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
_$_.append(__anchor, div_8);
|
|
262
|
+
_$_.pop_component();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function Layout(__anchor, __props, __block) {
|
|
266
|
+
_$_.push_component();
|
|
267
|
+
|
|
268
|
+
var main_1 = root_17();
|
|
269
|
+
|
|
270
|
+
{
|
|
271
|
+
var div_9 = _$_.child(main_1);
|
|
272
|
+
|
|
273
|
+
{
|
|
274
|
+
var node_6 = _$_.child(div_9);
|
|
275
|
+
|
|
276
|
+
_$_.composite(() => __props.children, node_6, {});
|
|
277
|
+
_$_.pop(div_9);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
_$_.append(__anchor, main_1);
|
|
282
|
+
_$_.pop_component();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function Content(__anchor, _, __block) {
|
|
286
|
+
_$_.push_component();
|
|
287
|
+
|
|
288
|
+
var div_10 = root_18();
|
|
289
|
+
|
|
290
|
+
_$_.append(__anchor, div_10);
|
|
291
|
+
_$_.pop_component();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function WebsiteIndex(__anchor, _, __block) {
|
|
295
|
+
_$_.push_component();
|
|
296
|
+
|
|
297
|
+
var fragment_8 = root_19();
|
|
298
|
+
var node_7 = _$_.first_child_frag(fragment_8);
|
|
299
|
+
|
|
300
|
+
Layout(
|
|
301
|
+
node_7,
|
|
302
|
+
{
|
|
303
|
+
children(__anchor, _, __block) {
|
|
304
|
+
_$_.push_component();
|
|
305
|
+
|
|
306
|
+
var fragment_9 = root_20();
|
|
307
|
+
var node_8 = _$_.first_child_frag(fragment_9);
|
|
308
|
+
|
|
309
|
+
Header(node_8, {}, _$_.active_block);
|
|
310
|
+
|
|
311
|
+
var node_9 = _$_.sibling(node_8);
|
|
312
|
+
|
|
313
|
+
Actions(node_9, { playgroundVisible: true }, _$_.active_block);
|
|
314
|
+
|
|
315
|
+
var node_10 = _$_.sibling(node_9);
|
|
316
|
+
|
|
317
|
+
Content(node_10, {}, _$_.active_block);
|
|
318
|
+
|
|
319
|
+
var node_11 = _$_.sibling(node_10);
|
|
320
|
+
|
|
321
|
+
Actions(node_11, { playgroundVisible: false }, _$_.active_block);
|
|
322
|
+
_$_.append(__anchor, fragment_9);
|
|
323
|
+
_$_.pop_component();
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
_$_.active_block
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
_$_.append(__anchor, fragment_8);
|
|
168
330
|
_$_.pop_component();
|
|
169
331
|
}
|