ripple 0.3.127 → 0.4.0
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 +293 -0
- package/package.json +10 -4
- package/src/runtime/element.js +23 -6
- package/src/runtime/index-client.js +81 -41
- package/src/runtime/index-server.js +1 -0
- package/src/runtime/internal/client/blocks.js +192 -114
- package/src/runtime/internal/client/component.js +4 -30
- package/src/runtime/internal/client/composite.js +12 -13
- package/src/runtime/internal/client/constants.js +14 -5
- package/src/runtime/internal/client/context.js +12 -19
- package/src/runtime/internal/client/css.js +7 -9
- package/src/runtime/internal/client/events.js +178 -90
- package/src/runtime/internal/client/expression.js +185 -124
- package/src/runtime/internal/client/for.js +616 -264
- package/src/runtime/internal/client/head.js +10 -5
- package/src/runtime/internal/client/html.js +1 -0
- package/src/runtime/internal/client/hydration.js +152 -10
- package/src/runtime/internal/client/if.js +239 -50
- package/src/runtime/internal/client/index.js +9 -13
- package/src/runtime/internal/client/operations.js +44 -51
- package/src/runtime/internal/client/portal.js +163 -85
- package/src/runtime/internal/client/render.js +44 -25
- package/src/runtime/internal/client/rpc.js +3 -3
- package/src/runtime/internal/client/runtime.js +799 -576
- package/src/runtime/internal/client/selector.js +77 -0
- package/src/runtime/internal/client/template.js +133 -158
- package/src/runtime/internal/client/transport.js +38 -0
- package/src/runtime/internal/client/try.js +465 -426
- package/src/runtime/internal/client/types.d.ts +65 -21
- package/src/runtime/internal/client/utils.js +1 -3
- package/src/runtime/internal/server/dynamic.js +20 -21
- package/src/runtime/internal/server/index.js +327 -275
- package/src/runtime/internal/server/rpc.js +3 -2
- package/src/runtime/internal/server/transport.js +16 -0
- package/src/runtime/transport.js +21 -0
- package/tests/client/__snapshots__/for.test.tsrx.snap +0 -5
- package/tests/client/array/array.copy-within.test.tsrx +12 -12
- package/tests/client/array/array.derived.test.tsrx +46 -46
- package/tests/client/array/array.iteration.test.tsrx +10 -10
- package/tests/client/array/array.mutations.test.tsrx +20 -20
- package/tests/client/array/array.static.test.tsrx +11 -11
- package/tests/client/array/array.to-methods.test.tsrx +6 -6
- package/tests/client/async-suspend.test.tsrx +74 -75
- package/tests/client/basic/basic.attributes.test.tsrx +86 -83
- package/tests/client/basic/basic.collections.test.tsrx +12 -12
- package/tests/client/basic/basic.components.test.tsrx +99 -25
- package/tests/client/basic/basic.errors.test.tsrx +18 -18
- package/tests/client/basic/basic.events.test.tsrx +42 -42
- package/tests/client/basic/basic.get-set.test.tsrx +8 -8
- package/tests/client/basic/basic.reactivity.test.tsrx +97 -29
- package/tests/client/basic/basic.rendering.test.tsrx +25 -25
- package/tests/client/basic/basic.utilities.test.tsrx +3 -3
- package/tests/client/boundaries.test.tsrx +8 -8
- package/tests/client/boxed-locals.test.tsrx +452 -0
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.tsrx.snap +6 -6
- package/tests/client/compiler/compiler.assignments.test.tsrx +9 -73
- package/tests/client/compiler/compiler.attributes.test.tsrx +23 -23
- package/tests/client/compiler/compiler.basic.test.tsrx +15 -60
- package/tests/client/compiler/compiler.tracked-access.test.tsrx +18 -43
- package/tests/client/compiler/compiler.try-in-function.test.tsrx +2 -2
- package/tests/client/composite/__snapshots__/composite.render.test.tsrx.snap +0 -1
- package/tests/client/composite/composite.dynamic-components.test.tsrx +77 -6
- package/tests/client/composite/composite.props.test.tsrx +149 -12
- package/tests/client/composite/composite.reactivity.test.tsrx +51 -52
- package/tests/client/composite/composite.render.test.tsrx +26 -9
- package/tests/client/css/scoped-styles.test.tsrx +8 -8
- package/tests/client/date.test.tsrx +42 -42
- package/tests/client/derived-release.test.tsrx +370 -0
- package/tests/client/dynamic-elements.test.tsrx +45 -45
- package/tests/client/events.test.tsrx +97 -71
- package/tests/client/for-selector.test.tsrx +221 -0
- package/tests/client/for-single-root-items.test.tsrx +118 -3
- package/tests/client/for.test.tsrx +332 -31
- package/tests/client/head.test.tsrx +19 -19
- package/tests/client/html.test.tsrx +11 -9
- package/tests/client/if-block.test.tsrx +80 -0
- package/tests/client/map.test.tsrx +16 -16
- package/tests/client/media-query.test.tsrx +7 -7
- package/tests/client/portal.test.tsrx +224 -26
- package/tests/client/primitive-text.test.tsrx +50 -0
- package/tests/client/prop-kind-switch.test.tsrx +141 -0
- package/tests/client/ref.test.tsrx +17 -17
- package/tests/client/return.test.tsrx +26 -3
- package/tests/client/root-boundary.test.tsrx +42 -0
- package/tests/client/scoped-flush.test.tsrx +8 -8
- package/tests/client/set.test.tsrx +6 -6
- package/tests/client/svg.test.tsrx +11 -11
- package/tests/client/switch.test.tsrx +44 -44
- package/tests/client/track-async-hydration.test.tsrx +4 -4
- package/tests/client/try.test.tsrx +151 -83
- package/tests/client/tsx.test.tsrx +43 -40
- package/tests/client/url/url.derived.test.tsrx +6 -6
- package/tests/client/url-search-params/url-search-params.derived.test.tsrx +8 -8
- package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +10 -10
- package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +10 -10
- package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +18 -18
- package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +2 -2
- package/tests/hydration/basic.test.js +74 -1
- package/tests/hydration/build-components.js +22 -1
- package/tests/hydration/compiled/client/basic.js +1142 -713
- package/tests/hydration/compiled/client/composite.js +151 -100
- package/tests/hydration/compiled/client/events.js +190 -150
- package/tests/hydration/compiled/client/for.js +1472 -1126
- package/tests/hydration/compiled/client/fragment-cursor.js +2341 -0
- package/tests/hydration/compiled/client/head.js +255 -199
- package/tests/hydration/compiled/client/hmr.js +54 -41
- package/tests/hydration/compiled/client/html-in-template.js +54 -39
- package/tests/hydration/compiled/client/html.js +1633 -1263
- package/tests/hydration/compiled/client/if-children.js +420 -270
- package/tests/hydration/compiled/client/if-fragment-controlflow.js +363 -317
- package/tests/hydration/compiled/client/if.js +229 -219
- package/tests/hydration/compiled/client/mixed-control-flow.js +420 -400
- package/tests/hydration/compiled/client/nested-control-flow.js +1326 -1255
- package/tests/hydration/compiled/client/portal.js +87 -95
- package/tests/hydration/compiled/client/reactivity.js +140 -83
- package/tests/hydration/compiled/client/return.js +28 -19
- package/tests/hydration/compiled/client/streaming.js +390 -306
- package/tests/hydration/compiled/client/switch.js +258 -266
- package/tests/hydration/compiled/client/track-async-serialization.js +448 -280
- package/tests/hydration/compiled/client/try.js +173 -129
- package/tests/hydration/compiled/client/typed-text.js +71 -0
- package/tests/hydration/compiled/fixtures/money.js +10 -0
- package/tests/hydration/compiled/server/basic.js +123 -6
- package/tests/hydration/compiled/server/composite.js +16 -30
- package/tests/hydration/compiled/server/events.js +19 -19
- package/tests/hydration/compiled/server/for.js +109 -49
- package/tests/hydration/compiled/server/fragment-cursor.js +1687 -0
- package/tests/hydration/compiled/server/head.js +20 -28
- package/tests/hydration/compiled/server/hmr.js +2 -2
- package/tests/hydration/compiled/server/html.js +71 -247
- package/tests/hydration/compiled/server/if-children.js +107 -15
- package/tests/hydration/compiled/server/if.js +11 -15
- package/tests/hydration/compiled/server/mixed-control-flow.js +8 -8
- package/tests/hydration/compiled/server/portal.js +14 -22
- package/tests/hydration/compiled/server/reactivity.js +14 -46
- package/tests/hydration/compiled/server/streaming.js +19 -19
- package/tests/hydration/compiled/server/switch.js +8 -8
- package/tests/hydration/compiled/server/track-async-serialization.js +150 -23
- package/tests/hydration/compiled/server/try.js +8 -8
- package/tests/hydration/compiled/server/typed-text.js +17 -0
- package/tests/hydration/components/basic.tsrx +44 -8
- package/tests/hydration/components/composite.tsrx +2 -2
- package/tests/hydration/components/events.tsrx +25 -25
- package/tests/hydration/components/for.tsrx +115 -67
- package/tests/hydration/components/fragment-cursor.tsrx +650 -0
- package/tests/hydration/components/head.tsrx +16 -16
- package/tests/hydration/components/hmr.tsrx +2 -2
- package/tests/hydration/components/html.tsrx +7 -7
- package/tests/hydration/components/if-children.tsrx +72 -25
- package/tests/hydration/components/if.tsrx +18 -18
- package/tests/hydration/components/mixed-control-flow.tsrx +11 -11
- package/tests/hydration/components/portal.tsrx +3 -3
- package/tests/hydration/components/reactivity.tsrx +16 -16
- package/tests/hydration/components/streaming.tsrx +20 -20
- package/tests/hydration/components/switch.tsrx +20 -20
- package/tests/hydration/components/text-types.ts +4 -0
- package/tests/hydration/components/track-async-serialization.tsrx +80 -26
- package/tests/hydration/components/try.tsrx +8 -8
- package/tests/hydration/components/typed-text.tsrx +22 -0
- package/tests/hydration/fixtures/money.js +10 -0
- package/tests/hydration/for.test.js +36 -0
- package/tests/hydration/fragment-cursor.test.js +111 -0
- package/tests/hydration/if-children.test.js +24 -0
- package/tests/hydration/streaming.test.js +19 -0
- package/tests/hydration/track-async-serialization.test.js +87 -2
- package/tests/hydration/tsconfig.text-types.json +10 -0
- package/tests/hydration/typed-text.test.js +34 -0
- package/tests/server/await.test.tsrx +3 -3
- package/tests/server/basic.attributes.test.tsrx +42 -42
- package/tests/server/basic.components.test.tsrx +64 -8
- package/tests/server/basic.test.tsrx +34 -32
- package/tests/server/compiler.test.tsrx +1 -70
- package/tests/server/composite.props.test.tsrx +6 -6
- package/tests/server/dynamic-elements.test.tsrx +31 -8
- package/tests/server/for.test.tsrx +94 -0
- package/tests/server/head.test.tsrx +11 -11
- package/tests/server/scoped-styles.test.tsrx +2 -2
- package/tests/server/streaming-ssr.test.tsrx +48 -43
- package/tests/server/track-async-serialization.test.tsrx +204 -21
- package/tests/server/try.test.tsrx +48 -48
- package/tests/types/transport.ts +40 -0
- package/tests/utils/vite-plugin-config.test.js +23 -0
- package/types/index.d.ts +48 -7
- package/types/server.d.ts +3 -0
- package/types/transport.d.ts +15 -0
- package/src/runtime/internal/client/switch.js +0 -113
- package/src/utils/errors.js +0 -13
- package/tests/client/lazy-array.test.tsrx +0 -31
- package/tests/client/lazy-destructuring.test.tsrx +0 -438
- package/tests/client/tracked-index-access.test.tsrx +0 -113
- package/tests/server/lazy-destructuring.test.tsrx +0 -595
- package/tests/server/tracked-index-access.test.tsrx +0 -76
|
@@ -162,13 +162,13 @@ describe('tsx expression', () => {
|
|
|
162
162
|
|
|
163
163
|
it('renders a tsx element with reactive expressions', () => {
|
|
164
164
|
function App() @{
|
|
165
|
-
|
|
165
|
+
const count = track(0);
|
|
166
166
|
const el = <>
|
|
167
|
-
<div>{'count: ' + count}</div>
|
|
167
|
+
<div>{'count: ' + count.value}</div>
|
|
168
168
|
</>;
|
|
169
169
|
<>
|
|
170
170
|
{el}
|
|
171
|
-
<button onClick={() => count++}>{'increment'}</button>
|
|
171
|
+
<button onClick={() => count.value++}>{'increment'}</button>
|
|
172
172
|
</>
|
|
173
173
|
}
|
|
174
174
|
render(App);
|
|
@@ -181,15 +181,15 @@ describe('tsx expression', () => {
|
|
|
181
181
|
|
|
182
182
|
it('conditionally renders tsx elements', () => {
|
|
183
183
|
function App() @{
|
|
184
|
-
|
|
184
|
+
const show = track(true);
|
|
185
185
|
const el = <div class="tsx-content">{'content'}</div>;
|
|
186
186
|
<>
|
|
187
|
-
@if (show) {
|
|
187
|
+
@if (show.value) {
|
|
188
188
|
<>
|
|
189
189
|
{el}
|
|
190
190
|
</>
|
|
191
191
|
}
|
|
192
|
-
<button onClick={() => (show = !show)}>{'toggle'}</button>
|
|
192
|
+
<button onClick={() => (show.value = !show.value)}>{'toggle'}</button>
|
|
193
193
|
</>
|
|
194
194
|
}
|
|
195
195
|
render(App);
|
|
@@ -205,7 +205,7 @@ describe('tsx expression', () => {
|
|
|
205
205
|
});
|
|
206
206
|
|
|
207
207
|
it('renders tsx element passed as children prop', () => {
|
|
208
|
-
function Child(
|
|
208
|
+
function Child({ children }) @{
|
|
209
209
|
<div class="wrapper">{children}</div>
|
|
210
210
|
}
|
|
211
211
|
|
|
@@ -252,11 +252,11 @@ describe('tsx expression', () => {
|
|
|
252
252
|
|
|
253
253
|
it('renders tsx element with dynamic attribute values', () => {
|
|
254
254
|
function App() @{
|
|
255
|
-
|
|
256
|
-
const el = <div id={name} class={'cls-' + name}>{'content'}</div>;
|
|
255
|
+
const name = track('initial');
|
|
256
|
+
const el = <div id={name.value} class={'cls-' + name.value}>{'content'}</div>;
|
|
257
257
|
<>
|
|
258
258
|
{el}
|
|
259
|
-
<button onClick={() => (name = 'updated')}>{'update'}</button>
|
|
259
|
+
<button onClick={() => (name.value = 'updated')}>{'update'}</button>
|
|
260
260
|
</>
|
|
261
261
|
}
|
|
262
262
|
|
|
@@ -273,12 +273,12 @@ describe('tsx expression', () => {
|
|
|
273
273
|
|
|
274
274
|
it('renders tsx element with event handlers', () => {
|
|
275
275
|
function App() @{
|
|
276
|
-
|
|
277
|
-
const el = <button onClick={() => (clicked = true)}>{'click'}</button>;
|
|
276
|
+
const clicked = track(false);
|
|
277
|
+
const el = <button onClick={() => (clicked.value = true)}>{'click'}</button>;
|
|
278
278
|
<>
|
|
279
279
|
{el}
|
|
280
280
|
<div class="status">
|
|
281
|
-
{clicked ? 'clicked' : 'not clicked'}
|
|
281
|
+
{clicked.value ? 'clicked' : 'not clicked'}
|
|
282
282
|
</div>
|
|
283
283
|
</>
|
|
284
284
|
}
|
|
@@ -293,11 +293,14 @@ describe('tsx expression', () => {
|
|
|
293
293
|
|
|
294
294
|
it('renders tsx element with boolean attributes', () => {
|
|
295
295
|
function App() @{
|
|
296
|
-
|
|
297
|
-
const el = <button disabled={isDisabled}>{'submit'}</button>;
|
|
296
|
+
const isDisabled = track(true);
|
|
297
|
+
const el = <button disabled={isDisabled.value}>{'submit'}</button>;
|
|
298
298
|
<>
|
|
299
299
|
{el}
|
|
300
|
-
<button
|
|
300
|
+
<button
|
|
301
|
+
class="toggle"
|
|
302
|
+
onClick={() => (isDisabled.value = !isDisabled.value)}
|
|
303
|
+
>{'toggle'}</button>
|
|
301
304
|
</>
|
|
302
305
|
}
|
|
303
306
|
|
|
@@ -311,11 +314,11 @@ describe('tsx expression', () => {
|
|
|
311
314
|
|
|
312
315
|
it('renders tsx element with style attribute', () => {
|
|
313
316
|
function App() @{
|
|
314
|
-
|
|
315
|
-
const el = <div style={{ color }}>{'content'}</div>;
|
|
317
|
+
const color = track('red');
|
|
318
|
+
const el = <div style={{ color: color.value }}>{'content'}</div>;
|
|
316
319
|
<>
|
|
317
320
|
{el}
|
|
318
|
-
<button onClick={() => (color = 'blue')}>{'change color'}</button>
|
|
321
|
+
<button onClick={() => (color.value = 'blue')}>{'change color'}</button>
|
|
319
322
|
</>
|
|
320
323
|
}
|
|
321
324
|
|
|
@@ -329,18 +332,18 @@ describe('tsx expression', () => {
|
|
|
329
332
|
|
|
330
333
|
it('renders tsx element with multiple dynamic attributes', () => {
|
|
331
334
|
function App() @{
|
|
332
|
-
|
|
335
|
+
const index = track(0);
|
|
333
336
|
const el = <div
|
|
334
|
-
id={'item-' + index}
|
|
335
|
-
class={'item pos-' + index}
|
|
336
|
-
data-index={index}
|
|
337
|
-
title={'Item ' + index}
|
|
337
|
+
id={'item-' + index.value}
|
|
338
|
+
class={'item pos-' + index.value}
|
|
339
|
+
data-index={index.value}
|
|
340
|
+
title={'Item ' + index.value}
|
|
338
341
|
>
|
|
339
|
-
{'Item ' + index}
|
|
342
|
+
{'Item ' + index.value}
|
|
340
343
|
</div>;
|
|
341
344
|
<>
|
|
342
345
|
{el}
|
|
343
|
-
<button onClick={() => index++}>{'next'}</button>
|
|
346
|
+
<button onClick={() => index.value++}>{'next'}</button>
|
|
344
347
|
</>
|
|
345
348
|
}
|
|
346
349
|
|
|
@@ -360,7 +363,7 @@ describe('tsx expression', () => {
|
|
|
360
363
|
});
|
|
361
364
|
|
|
362
365
|
it('renders fragment shorthand passed directly as component prop', () => {
|
|
363
|
-
function Wrapper(
|
|
366
|
+
function Wrapper({ content }) @{
|
|
364
367
|
<div class="wrapper">{content}</div>
|
|
365
368
|
}
|
|
366
369
|
|
|
@@ -376,7 +379,7 @@ describe('tsx expression', () => {
|
|
|
376
379
|
});
|
|
377
380
|
|
|
378
381
|
it('renders fragment shorthand passed directly as children prop', () => {
|
|
379
|
-
function Card(
|
|
382
|
+
function Card({ title, children }) @{
|
|
380
383
|
<div class="card">
|
|
381
384
|
<h2 class="card-title">{title}</h2>
|
|
382
385
|
<div class="card-body">{children}</div>
|
|
@@ -435,7 +438,7 @@ describe('tsx expression', () => {
|
|
|
435
438
|
});
|
|
436
439
|
|
|
437
440
|
it('renders tsx from factory function passed to component', () => {
|
|
438
|
-
function List(
|
|
441
|
+
function List({ renderItem, items }) @{
|
|
439
442
|
<ul class="list">
|
|
440
443
|
@for (const item of items) {
|
|
441
444
|
<>
|
|
@@ -472,11 +475,11 @@ describe('tsx expression', () => {
|
|
|
472
475
|
}
|
|
473
476
|
|
|
474
477
|
function App() @{
|
|
475
|
-
|
|
476
|
-
const counterElement = createCounter('Count:', () => count);
|
|
478
|
+
const count = track(0);
|
|
479
|
+
const counterElement = createCounter('Count:', () => count.value);
|
|
477
480
|
<>
|
|
478
481
|
{counterElement}
|
|
479
|
-
<button onClick={() => count++}>{'increment'}</button>
|
|
482
|
+
<button onClick={() => count.value++}>{'increment'}</button>
|
|
480
483
|
</>
|
|
481
484
|
}
|
|
482
485
|
|
|
@@ -617,7 +620,7 @@ describe('tsx expression', () => {
|
|
|
617
620
|
});
|
|
618
621
|
|
|
619
622
|
it('renders tsx as prop with fallback in component', () => {
|
|
620
|
-
function Alert(
|
|
623
|
+
function Alert({ icon, message }) @{
|
|
621
624
|
<div class="alert">
|
|
622
625
|
{icon}
|
|
623
626
|
<span class="message">{message}</span>
|
|
@@ -677,11 +680,11 @@ describe('tsx expression', () => {
|
|
|
677
680
|
}
|
|
678
681
|
|
|
679
682
|
function App() @{
|
|
680
|
-
|
|
683
|
+
const status = track('default');
|
|
681
684
|
<>
|
|
682
|
-
{createContent(status)}
|
|
683
|
-
<button class="set-success" onClick={() => (status = 'success')}>{'Success'}</button>
|
|
684
|
-
<button class="set-error" onClick={() => (status = 'error')}>{'Error'}</button>
|
|
685
|
+
{createContent(status.value)}
|
|
686
|
+
<button class="set-success" onClick={() => (status.value = 'success')}>{'Success'}</button>
|
|
687
|
+
<button class="set-error" onClick={() => (status.value = 'error')}>{'Error'}</button>
|
|
685
688
|
</>
|
|
686
689
|
}
|
|
687
690
|
|
|
@@ -736,13 +739,13 @@ describe('tsrx expression', () => {
|
|
|
736
739
|
|
|
737
740
|
it('updates reactive expressions inside native fragments', () => {
|
|
738
741
|
function App() @{
|
|
739
|
-
|
|
742
|
+
const count = track(0);
|
|
740
743
|
const el = <>
|
|
741
|
-
<div>{'count: ' + count}</div>
|
|
744
|
+
<div>{'count: ' + count.value}</div>
|
|
742
745
|
</>;
|
|
743
746
|
<>
|
|
744
747
|
{el}
|
|
745
|
-
<button onClick={() => count++}>{'increment'}</button>
|
|
748
|
+
<button onClick={() => count.value++}>{'increment'}</button>
|
|
746
749
|
</>
|
|
747
750
|
}
|
|
748
751
|
|
|
@@ -4,13 +4,13 @@ describe('RippleURL > derived', () => {
|
|
|
4
4
|
it('handles reactive computed properties based on URL', () => {
|
|
5
5
|
function URLTest() @{
|
|
6
6
|
const url = RippleURL('https://example.com/users/123?tab=profile');
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const userId = track(() => url.pathname.split('/').pop());
|
|
8
|
+
const activeTab = track(() => url.searchParams.get('tab'));
|
|
9
9
|
<>
|
|
10
10
|
<button onClick={() => (url.pathname = '/users/456')}>{'Change User'}</button>
|
|
11
11
|
<button onClick={() => url.searchParams.set('tab', 'settings')}>{'Change Tab'}</button>
|
|
12
|
-
<pre>{`User ID: ${userId}`}</pre>
|
|
13
|
-
<pre>{`Active Tab: ${activeTab}`}</pre>
|
|
12
|
+
<pre>{`User ID: ${userId.value}`}</pre>
|
|
13
|
+
<pre>{`Active Tab: ${activeTab.value}`}</pre>
|
|
14
14
|
</>
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -57,10 +57,10 @@ describe('RippleURL > derived', () => {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
function ChildB({ url }: { url: RippleURL }) @{
|
|
60
|
-
|
|
60
|
+
const count = track(() => url.searchParams.get('count'));
|
|
61
61
|
<>
|
|
62
62
|
<pre>{url.href}</pre>
|
|
63
|
-
<pre>{count}</pre>
|
|
63
|
+
<pre>{count.value}</pre>
|
|
64
64
|
</>
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -4,15 +4,15 @@ describe('RippleURLSearchParams > derived', () => {
|
|
|
4
4
|
it('handles reactive computed properties based on search params', () => {
|
|
5
5
|
function URLTest() @{
|
|
6
6
|
const params = RippleURLSearchParams('page=1&limit=10');
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
const page = track(() => parseInt(params.get('page') || '1', 10));
|
|
8
|
+
const limit = track(() => parseInt(params.get('limit') || '10', 10));
|
|
9
|
+
const offset = track(() => (page.value - 1) * limit.value);
|
|
10
10
|
<>
|
|
11
11
|
<button onClick={() => params.set('page', '2')}>{'next page'}</button>
|
|
12
12
|
<button onClick={() => params.set('page', '1')}>{'first page'}</button>
|
|
13
|
-
<pre>{`Page: ${page}`}</pre>
|
|
14
|
-
<pre>{`Limit: ${limit}`}</pre>
|
|
15
|
-
<pre>{`Offset: ${offset}`}</pre>
|
|
13
|
+
<pre>{`Page: ${page.value}`}</pre>
|
|
14
|
+
<pre>{`Limit: ${limit.value}`}</pre>
|
|
15
|
+
<pre>{`Offset: ${offset.value}`}</pre>
|
|
16
16
|
</>
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -62,8 +62,8 @@ describe('RippleURLSearchParams > derived', () => {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
function ChildB({ params }: { params: RippleURLSearchParams }) @{
|
|
65
|
-
|
|
66
|
-
<pre>{count}</pre>
|
|
65
|
+
const count = track(() => params.get('count'));
|
|
66
|
+
<pre>{count.value}</pre>
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
render(ParentTest);
|
|
@@ -4,10 +4,10 @@ describe('RippleURLSearchParams > iteration', () => {
|
|
|
4
4
|
it('handles keys method with reactivity', () => {
|
|
5
5
|
function URLTest() @{
|
|
6
6
|
const params = RippleURLSearchParams('foo=bar&baz=qux');
|
|
7
|
-
|
|
7
|
+
const keys = track(() => Array.from(params.keys()));
|
|
8
8
|
<>
|
|
9
9
|
<button onClick={() => params.append('new', 'value')}>{'add param'}</button>
|
|
10
|
-
<pre>{JSON.stringify(keys)}</pre>
|
|
10
|
+
<pre>{JSON.stringify(keys.value)}</pre>
|
|
11
11
|
</>
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -28,10 +28,10 @@ describe('RippleURLSearchParams > iteration', () => {
|
|
|
28
28
|
it('handles values method with reactivity', () => {
|
|
29
29
|
function URLTest() @{
|
|
30
30
|
const params = RippleURLSearchParams('foo=bar&baz=qux');
|
|
31
|
-
|
|
31
|
+
const values = track(() => Array.from(params.values()));
|
|
32
32
|
<>
|
|
33
33
|
<button onClick={() => params.set('foo', 'updated')}>{'update foo'}</button>
|
|
34
|
-
<pre>{JSON.stringify(values)}</pre>
|
|
34
|
+
<pre>{JSON.stringify(values.value)}</pre>
|
|
35
35
|
</>
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -52,10 +52,10 @@ describe('RippleURLSearchParams > iteration', () => {
|
|
|
52
52
|
it('handles entries method with reactivity', () => {
|
|
53
53
|
function URLTest() @{
|
|
54
54
|
const params = RippleURLSearchParams('foo=bar&baz=qux');
|
|
55
|
-
|
|
55
|
+
const entries = track(() => Array.from(params.entries()));
|
|
56
56
|
<>
|
|
57
57
|
<button onClick={() => params.append('new', 'value')}>{'add param'}</button>
|
|
58
|
-
<pre>{JSON.stringify(entries)}</pre>
|
|
58
|
+
<pre>{JSON.stringify(entries.value)}</pre>
|
|
59
59
|
</>
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -78,10 +78,10 @@ describe('RippleURLSearchParams > iteration', () => {
|
|
|
78
78
|
it('handles Symbol.iterator with reactivity', () => {
|
|
79
79
|
function URLTest() @{
|
|
80
80
|
const params = RippleURLSearchParams('foo=bar&baz=qux');
|
|
81
|
-
|
|
81
|
+
const entries = track(() => Array.from(params));
|
|
82
82
|
<>
|
|
83
83
|
<button onClick={() => params.delete('foo')}>{'delete foo'}</button>
|
|
84
|
-
<pre>{JSON.stringify(entries)}</pre>
|
|
84
|
+
<pre>{JSON.stringify(entries.value)}</pre>
|
|
85
85
|
</>
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -130,7 +130,7 @@ describe('RippleURLSearchParams > iteration', () => {
|
|
|
130
130
|
it('handles forEach iteration', () => {
|
|
131
131
|
function URLTest() @{
|
|
132
132
|
const params = RippleURLSearchParams('a=1&b=2&c=3');
|
|
133
|
-
|
|
133
|
+
const sum = track(() => {
|
|
134
134
|
let total = 0;
|
|
135
135
|
// Access the params reactively through entries
|
|
136
136
|
for (const [key, value] of params.entries()) {
|
|
@@ -140,7 +140,7 @@ describe('RippleURLSearchParams > iteration', () => {
|
|
|
140
140
|
});
|
|
141
141
|
<>
|
|
142
142
|
<button onClick={() => params.append('d', '4')}>{'add d=4'}</button>
|
|
143
|
-
<pre>{sum}</pre>
|
|
143
|
+
<pre>{sum.value}</pre>
|
|
144
144
|
</>
|
|
145
145
|
}
|
|
146
146
|
|
|
@@ -30,11 +30,11 @@ describe('RippleURLSearchParams > mutation', () => {
|
|
|
30
30
|
it('handles append with multiple values for same key', () => {
|
|
31
31
|
function URLTest() @{
|
|
32
32
|
const params = RippleURLSearchParams('foo=bar');
|
|
33
|
-
|
|
33
|
+
const allFoo = track(() => params.getAll('foo'));
|
|
34
34
|
<>
|
|
35
35
|
<button onClick={() => params.append('foo', 'baz')}>{'append foo'}</button>
|
|
36
36
|
<pre>{params.toString()}</pre>
|
|
37
|
-
<pre>{JSON.stringify(allFoo)}</pre>
|
|
37
|
+
<pre>{JSON.stringify(allFoo.value)}</pre>
|
|
38
38
|
</>
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -112,11 +112,11 @@ describe('RippleURLSearchParams > mutation', () => {
|
|
|
112
112
|
it('handles delete when key does not exist', () => {
|
|
113
113
|
function URLTest() @{
|
|
114
114
|
const params = RippleURLSearchParams('foo=bar');
|
|
115
|
-
|
|
115
|
+
const reactiveSize = track(() => params.size);
|
|
116
116
|
<>
|
|
117
117
|
<button onClick={() => params.delete('nonexistent')}>{'delete nonexistent'}</button>
|
|
118
118
|
<pre>{params.toString()}</pre>
|
|
119
|
-
<pre>{reactiveSize}</pre>
|
|
119
|
+
<pre>{reactiveSize.value}</pre>
|
|
120
120
|
</>
|
|
121
121
|
}
|
|
122
122
|
|
|
@@ -174,11 +174,11 @@ describe('RippleURLSearchParams > mutation', () => {
|
|
|
174
174
|
it('handles set with multiple existing values', () => {
|
|
175
175
|
function URLTest() @{
|
|
176
176
|
const params = RippleURLSearchParams('foo=bar&foo=baz&foo=qux');
|
|
177
|
-
|
|
177
|
+
const allFoo = track(() => params.getAll('foo'));
|
|
178
178
|
<>
|
|
179
179
|
<button onClick={() => params.set('foo', 'single')}>{'set foo'}</button>
|
|
180
180
|
<pre>{params.toString()}</pre>
|
|
181
|
-
<pre>{JSON.stringify(allFoo)}</pre>
|
|
181
|
+
<pre>{JSON.stringify(allFoo.value)}</pre>
|
|
182
182
|
</>
|
|
183
183
|
}
|
|
184
184
|
|
|
@@ -201,10 +201,10 @@ describe('RippleURLSearchParams > mutation', () => {
|
|
|
201
201
|
it('handles set when value is the same', () => {
|
|
202
202
|
function URLTest() @{
|
|
203
203
|
const params = RippleURLSearchParams('foo=bar');
|
|
204
|
-
|
|
204
|
+
const reactiveString = track(() => params.toString());
|
|
205
205
|
<>
|
|
206
206
|
<button onClick={() => params.set('foo', 'bar')}>{'set same value'}</button>
|
|
207
|
-
<pre>{reactiveString}</pre>
|
|
207
|
+
<pre>{reactiveString.value}</pre>
|
|
208
208
|
<pre>{params.size}</pre>
|
|
209
209
|
</>
|
|
210
210
|
}
|
|
@@ -319,12 +319,12 @@ describe('RippleURLSearchParams > mutation', () => {
|
|
|
319
319
|
it('handles duplicate keys with different values', () => {
|
|
320
320
|
function URLTest() @{
|
|
321
321
|
const params = RippleURLSearchParams();
|
|
322
|
-
|
|
322
|
+
const tags = track(() => params.getAll('tag'));
|
|
323
323
|
<>
|
|
324
324
|
<button onClick={() => params.append('tag', 'javascript')}>{'add js'}</button>
|
|
325
325
|
<button onClick={() => params.append('tag', 'typescript')}>{'add ts'}</button>
|
|
326
326
|
<button onClick={() => params.append('tag', 'ripple')}>{'add ripple'}</button>
|
|
327
|
-
<pre>{JSON.stringify(tags)}</pre>
|
|
327
|
+
<pre>{JSON.stringify(tags.value)}</pre>
|
|
328
328
|
<pre>{params.size}</pre>
|
|
329
329
|
</>
|
|
330
330
|
}
|
|
@@ -4,12 +4,12 @@ describe('RippleURLSearchParams > retrieval', () => {
|
|
|
4
4
|
it('handles get operation with reactivity', () => {
|
|
5
5
|
function URLTest() @{
|
|
6
6
|
const params = RippleURLSearchParams('foo=bar&baz=qux');
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const foo = track(() => params.get('foo'));
|
|
8
|
+
const baz = track(() => params.get('baz'));
|
|
9
9
|
<>
|
|
10
10
|
<button onClick={() => params.set('foo', 'updated')}>{'update foo'}</button>
|
|
11
|
-
<pre>{foo}</pre>
|
|
12
|
-
<pre>{baz}</pre>
|
|
11
|
+
<pre>{foo.value}</pre>
|
|
12
|
+
<pre>{baz.value}</pre>
|
|
13
13
|
</>
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -32,8 +32,8 @@ describe('RippleURLSearchParams > retrieval', () => {
|
|
|
32
32
|
it('handles get for nonexistent key', () => {
|
|
33
33
|
function URLTest() @{
|
|
34
34
|
const params = RippleURLSearchParams('foo=bar');
|
|
35
|
-
|
|
36
|
-
<pre>{String(nonexistent)}</pre>
|
|
35
|
+
const nonexistent = track(() => params.get('nonexistent'));
|
|
36
|
+
<pre>{String(nonexistent.value)}</pre>
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
render(URLTest);
|
|
@@ -44,10 +44,10 @@ describe('RippleURLSearchParams > retrieval', () => {
|
|
|
44
44
|
it('handles getAll operation with reactivity', () => {
|
|
45
45
|
function URLTest() @{
|
|
46
46
|
const params = RippleURLSearchParams('foo=bar&foo=baz');
|
|
47
|
-
|
|
47
|
+
const allFoo = track(() => params.getAll('foo'));
|
|
48
48
|
<>
|
|
49
49
|
<button onClick={() => params.append('foo', 'qux')}>{'append foo'}</button>
|
|
50
|
-
<pre>{JSON.stringify(allFoo)}</pre>
|
|
50
|
+
<pre>{JSON.stringify(allFoo.value)}</pre>
|
|
51
51
|
</>
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -68,13 +68,13 @@ describe('RippleURLSearchParams > retrieval', () => {
|
|
|
68
68
|
it('handles has operation with reactivity', () => {
|
|
69
69
|
function URLTest() @{
|
|
70
70
|
const params = RippleURLSearchParams('foo=bar');
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
const hasFoo = track(() => params.has('foo'));
|
|
72
|
+
const hasBaz = track(() => params.has('baz'));
|
|
73
73
|
<>
|
|
74
74
|
<button onClick={() => params.append('baz', 'qux')}>{'add baz'}</button>
|
|
75
75
|
<button onClick={() => params.delete('foo')}>{'delete foo'}</button>
|
|
76
|
-
<pre>{hasFoo.toString()}</pre>
|
|
77
|
-
<pre>{hasBaz.toString()}</pre>
|
|
76
|
+
<pre>{hasFoo.value.toString()}</pre>
|
|
77
|
+
<pre>{hasBaz.value.toString()}</pre>
|
|
78
78
|
</>
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -105,12 +105,12 @@ describe('RippleURLSearchParams > retrieval', () => {
|
|
|
105
105
|
it('handles has with specific value', () => {
|
|
106
106
|
function URLTest() @{
|
|
107
107
|
const params = RippleURLSearchParams('foo=bar&foo=baz');
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
const hasBarValue = track(() => params.has('foo', 'bar'));
|
|
109
|
+
const hasQuxValue = track(() => params.has('foo', 'qux'));
|
|
110
110
|
<>
|
|
111
111
|
<button onClick={() => params.append('foo', 'qux')}>{'add qux'}</button>
|
|
112
|
-
<pre>{hasBarValue.toString()}</pre>
|
|
113
|
-
<pre>{hasQuxValue.toString()}</pre>
|
|
112
|
+
<pre>{hasBarValue.value.toString()}</pre>
|
|
113
|
+
<pre>{hasQuxValue.value.toString()}</pre>
|
|
114
114
|
</>
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -133,11 +133,11 @@ describe('RippleURLSearchParams > retrieval', () => {
|
|
|
133
133
|
it('handles size property with reactivity', () => {
|
|
134
134
|
function URLTest() @{
|
|
135
135
|
const params = RippleURLSearchParams('foo=bar');
|
|
136
|
-
|
|
136
|
+
const size = track(() => params.size);
|
|
137
137
|
<>
|
|
138
138
|
<button onClick={() => params.append('baz', 'qux')}>{'add'}</button>
|
|
139
139
|
<button onClick={() => params.delete('foo')}>{'delete'}</button>
|
|
140
|
-
<pre>{size}</pre>
|
|
140
|
+
<pre>{size.value}</pre>
|
|
141
141
|
</>
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -4,10 +4,10 @@ describe('RippleURLSearchParams > serialization', () => {
|
|
|
4
4
|
it('handles toString method with reactivity', () => {
|
|
5
5
|
function URLTest() @{
|
|
6
6
|
const params = RippleURLSearchParams('foo=bar');
|
|
7
|
-
|
|
7
|
+
const string = track(() => params.toString());
|
|
8
8
|
<>
|
|
9
9
|
<button onClick={() => params.append('baz', 'qux')}>{'add'}</button>
|
|
10
|
-
<pre>{string}</pre>
|
|
10
|
+
<pre>{string.value}</pre>
|
|
11
11
|
</>
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { flushSync } from 'ripple';
|
|
2
|
+
import { flushSync, hydrate } from 'ripple';
|
|
3
|
+
import { render } from 'ripple/server';
|
|
3
4
|
import { hydrateComponent, container } from '../setup-hydration.js';
|
|
4
5
|
|
|
5
6
|
// Import server-compiled components
|
|
@@ -13,6 +14,26 @@ describe('hydration > basic', () => {
|
|
|
13
14
|
expect(container.innerHTML).toBeHtml('<div>Hello World</div>');
|
|
14
15
|
});
|
|
15
16
|
|
|
17
|
+
it('hydrates without a root boundary, adopting the server DOM', async () => {
|
|
18
|
+
const { body } = await render(ServerComponents.ParentWithChild);
|
|
19
|
+
expect(body.startsWith('<!--[-->')).toBe(true);
|
|
20
|
+
container.innerHTML = body;
|
|
21
|
+
const parent = container.querySelector('.parent');
|
|
22
|
+
|
|
23
|
+
const unmount = hydrate(ClientComponents.ParentWithChild, {
|
|
24
|
+
target: container,
|
|
25
|
+
rootBoundary: false,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(container.querySelector('.parent')).toBe(parent);
|
|
29
|
+
expect(container.innerHTML).toBeHtml(
|
|
30
|
+
'<div class="parent"><span class="child">Child content</span></div>',
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
unmount();
|
|
34
|
+
expect(container.querySelector('.parent')).toBeNull();
|
|
35
|
+
});
|
|
36
|
+
|
|
16
37
|
it('hydrates multiple static elements', async () => {
|
|
17
38
|
await hydrateComponent(ServerComponents.MultipleElements, ClientComponents.MultipleElements);
|
|
18
39
|
expect(container.innerHTML).toBeHtml(
|
|
@@ -209,6 +230,21 @@ describe('hydration > basic', () => {
|
|
|
209
230
|
expect(textIndex).toBeLessThan(endMarker);
|
|
210
231
|
});
|
|
211
232
|
|
|
233
|
+
it('restores typed text children hydrated from empty server text', async () => {
|
|
234
|
+
await hydrateComponent(
|
|
235
|
+
ServerComponents.TypedTextPropWithToggle,
|
|
236
|
+
ClientComponents.TypedTextPropWithToggle,
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
expect(container.querySelector('.text-prop')?.textContent).toBe('');
|
|
240
|
+
|
|
241
|
+
/** @type {any} */ (container.querySelector('.show-text'))?.click();
|
|
242
|
+
flushSync();
|
|
243
|
+
|
|
244
|
+
// A string-typed child is a text node, with no hydration markers.
|
|
245
|
+
expect(container.querySelector('.text-prop')?.innerHTML).toBe('hello');
|
|
246
|
+
});
|
|
247
|
+
|
|
212
248
|
it('hydrates static child component followed by sibling content', async () => {
|
|
213
249
|
await hydrateComponent(
|
|
214
250
|
ServerComponents.StaticChildWithSiblings,
|
|
@@ -294,3 +330,40 @@ describe('hydration > text runs with call-containing primitives', () => {
|
|
|
294
330
|
expect(container.textContent).toBe('fetchedafter-call');
|
|
295
331
|
});
|
|
296
332
|
});
|
|
333
|
+
|
|
334
|
+
describe('primitive text calls', () => {
|
|
335
|
+
it('hydrates merged primitive calls and updates their existing text node', async () => {
|
|
336
|
+
const { unmount } = await hydrateComponent(
|
|
337
|
+
ServerComponents.PrimitiveTextCalls,
|
|
338
|
+
ClientComponents.PrimitiveTextCalls,
|
|
339
|
+
);
|
|
340
|
+
try {
|
|
341
|
+
const node = container.querySelector('.primitive-calls');
|
|
342
|
+
const text = node.firstChild;
|
|
343
|
+
expect(node.textContent).toBe('sum: 4; big: 2');
|
|
344
|
+
container.querySelector('button').click();
|
|
345
|
+
flushSync();
|
|
346
|
+
expect(node.textContent).toBe('sum: 6; big: 3');
|
|
347
|
+
expect(node.firstChild).toBe(text);
|
|
348
|
+
} finally {
|
|
349
|
+
unmount();
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('hydrates elements returned by shadowed built-ins', async () => {
|
|
354
|
+
const { unmount } = await hydrateComponent(
|
|
355
|
+
ServerComponents.ShadowedTextCalls,
|
|
356
|
+
ClientComponents.ShadowedTextCalls,
|
|
357
|
+
);
|
|
358
|
+
try {
|
|
359
|
+
expect([...container.querySelectorAll('b')].map((node) => node.textContent)).toEqual([
|
|
360
|
+
'string',
|
|
361
|
+
'number',
|
|
362
|
+
'bigint',
|
|
363
|
+
'date',
|
|
364
|
+
]);
|
|
365
|
+
} finally {
|
|
366
|
+
unmount();
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
});
|