ripple 0.3.83 → 0.3.85

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/package.json +3 -3
  3. package/src/constants.js +4 -0
  4. package/src/runtime/index-client.js +2 -0
  5. package/src/runtime/index-server.js +13 -0
  6. package/src/runtime/internal/client/component.js +3 -3
  7. package/src/runtime/internal/client/constants.js +4 -0
  8. package/src/runtime/internal/client/for.js +22 -1
  9. package/src/runtime/internal/client/if.js +18 -2
  10. package/src/runtime/internal/client/index.js +1 -0
  11. package/src/runtime/internal/client/operations.js +25 -0
  12. package/src/runtime/internal/client/runtime.js +62 -10
  13. package/src/runtime/internal/client/switch.js +16 -2
  14. package/src/runtime/internal/client/template.js +10 -2
  15. package/src/runtime/internal/client/try.js +12 -2
  16. package/src/runtime/internal/client/types.d.ts +4 -0
  17. package/src/runtime/proxy.js +17 -1
  18. package/tests/client/__snapshots__/for.test.tsrx.snap +0 -2
  19. package/tests/client/compiler/compiler.basic.test.tsrx +5 -2
  20. package/tests/client/composite/__snapshots__/composite.render.test.tsrx.snap +0 -4
  21. package/tests/client/object.test.tsrx +47 -1
  22. package/tests/client/scoped-flush.test.tsrx +105 -0
  23. package/tests/hydration/compiled/client/basic.js +198 -214
  24. package/tests/hydration/compiled/client/composite.js +23 -72
  25. package/tests/hydration/compiled/client/for.js +66 -72
  26. package/tests/hydration/compiled/client/hmr.js +2 -13
  27. package/tests/hydration/compiled/client/html.js +619 -479
  28. package/tests/hydration/compiled/client/if-children.js +72 -84
  29. package/tests/hydration/compiled/client/if.js +87 -92
  30. package/tests/hydration/compiled/client/mixed-control-flow.js +120 -160
  31. package/tests/hydration/compiled/client/nested-control-flow.js +288 -360
  32. package/tests/hydration/compiled/client/portal.js +15 -21
  33. package/tests/hydration/compiled/client/reactivity.js +7 -12
  34. package/tests/hydration/compiled/client/switch.js +113 -115
  35. package/tests/hydration/compiled/client/track-async-serialization.js +71 -122
  36. package/tests/hydration/compiled/client/try.js +26 -41
  37. package/tests/hydration/compiled/server/basic.js +268 -556
  38. package/tests/hydration/compiled/server/composite.js +31 -50
  39. package/tests/hydration/compiled/server/events.js +37 -173
  40. package/tests/hydration/compiled/server/for.js +236 -847
  41. package/tests/hydration/compiled/server/head.js +110 -241
  42. package/tests/hydration/compiled/server/hmr.js +14 -41
  43. package/tests/hydration/compiled/server/html-in-template.js +14 -38
  44. package/tests/hydration/compiled/server/html.js +951 -1176
  45. package/tests/hydration/compiled/server/if-children.js +59 -493
  46. package/tests/hydration/compiled/server/if.js +57 -209
  47. package/tests/hydration/compiled/server/mixed-control-flow.js +144 -250
  48. package/tests/hydration/compiled/server/nested-control-flow.js +309 -559
  49. package/tests/hydration/compiled/server/portal.js +94 -156
  50. package/tests/hydration/compiled/server/reactivity.js +23 -65
  51. package/tests/hydration/compiled/server/return.js +6 -16
  52. package/tests/hydration/compiled/server/switch.js +91 -219
  53. package/tests/hydration/compiled/server/track-async-serialization.js +211 -256
  54. package/tests/hydration/compiled/server/try.js +67 -126
  55. package/tests/hydration/components/html.tsrx +75 -0
  56. package/tests/hydration/html.test.js +50 -0
  57. package/tests/server/compiler.test.tsrx +6 -2
  58. package/types/index.d.ts +2 -0
@@ -2,7 +2,6 @@
2
2
 
3
3
  exports[`composite > render > correct handles passing through component props and children 1`] = `
4
4
  <div>
5
- <!---->
6
5
  <div>
7
6
  <div>
8
7
  I am A
@@ -17,21 +16,18 @@ exports[`composite > render > correct handles passing through component props an
17
16
  </div>
18
17
  <!---->
19
18
  </div>
20
- <!---->
21
19
 
22
20
  </div>
23
21
  `;
24
22
 
25
23
  exports[`composite > render > render simple text as children 1`] = `
26
24
  <div>
27
- <!---->
28
25
  <button
29
26
  class="my-button"
30
27
  >
31
28
  Click Me
32
29
  <!---->
33
30
  </button>
34
- <!---->
35
31
 
36
32
  </div>
37
33
  `;
@@ -1,4 +1,4 @@
1
- import { RippleObject, flushSync } from 'ripple';
1
+ import { RippleObject, effect, flushSync, snapshot } from 'ripple';
2
2
  import { TRACKED_OBJECT } from '../../src/runtime/internal/client/constants.js';
3
3
 
4
4
  describe('RippleObject', () => {
@@ -194,4 +194,50 @@ describe('RippleObject', () => {
194
194
  expect(pre1.textContent).toBe('1');
195
195
  expect(pre3.textContent).toBe('{"a":1,"b":1,"c":{"d":{"e":8}}}');
196
196
  });
197
+
198
+ it('snapshot() returns a detached plain copy of current values', () => {
199
+ let snap: any;
200
+
201
+ function ObjectTest() @{
202
+ const obj = new RippleObject({ a: 1, b: 2 });
203
+ obj.a = 10;
204
+ snap = snapshot(obj);
205
+ // a later mutation must not affect the snapshot
206
+ obj.a = 99;
207
+ <div />
208
+ }
209
+
210
+ render(ObjectTest);
211
+
212
+ expect(snap).toEqual({ a: 10, b: 2 });
213
+ // detached: a plain object, not a proxy
214
+ expect(TRACKED_OBJECT in snap).toBe(false);
215
+ expect(snap.a).toBe(10);
216
+ });
217
+
218
+ it('snapshot() reads without registering reactivity', () => {
219
+ let runs = 0;
220
+
221
+ function ObjectTest() @{
222
+ const obj = new RippleObject({ a: 0 });
223
+ effect(() => {
224
+ snapshot(obj);
225
+ runs++;
226
+ });
227
+ <button
228
+ onClick={() => {
229
+ obj.a++;
230
+ }}
231
+ >{'Increment A'}</button>
232
+ }
233
+
234
+ render(ObjectTest);
235
+ flushSync();
236
+ expect(runs).toBe(1);
237
+
238
+ container.querySelectorAll('button')[0].click();
239
+ flushSync();
240
+ // snapshot did not subscribe, so the effect must not re-run
241
+ expect(runs).toBe(1);
242
+ });
197
243
  });
@@ -0,0 +1,105 @@
1
+ import { flushSync, track, Context } from 'ripple';
2
+
3
+ describe('scoped flush', () => {
4
+ // Scoped descent must not change observable behaviour: a tracked shared across
5
+ // sibling subtrees via a module-level variable (escaping its owner subtree)
6
+ // must still update its out-of-subtree subscriber.
7
+ it('updates a sibling subtree that reads a module-smuggled tracked', () => {
8
+ let shared = null;
9
+ let bump = null;
10
+
11
+ function Producer() @{
12
+ let &[v, vT] = track(0);
13
+ shared = vT;
14
+ bump = () => {
15
+ v += 1;
16
+ };
17
+ <div class="producer">{'producer'}</div>
18
+ }
19
+
20
+ function Consumer() @{
21
+ <div class="consumer">
22
+ {shared ? shared.value : -1}
23
+ </div>
24
+ }
25
+
26
+ function App() @{
27
+ <>
28
+ <Producer />
29
+ <Consumer />
30
+ </>
31
+ }
32
+
33
+ render(App);
34
+
35
+ expect(container.querySelector('.consumer').textContent).toBe('0');
36
+
37
+ flushSync(() => bump());
38
+
39
+ expect(container.querySelector('.consumer').textContent).toBe('1');
40
+
41
+ flushSync(() => bump());
42
+
43
+ expect(container.querySelector('.consumer').textContent).toBe('2');
44
+ });
45
+
46
+ // The common, scope-safe case: a Context-provided tracked read at many leaves.
47
+ // A root update must fan out to all leaves; a scoped update must reach only the
48
+ // targeted subtree's leaf and leave the others untouched.
49
+ it('fans out a root update and scopes a nested update', () => {
50
+ const Ctx = new Context(null);
51
+ let bumpRoot = null;
52
+ let bumpLeftLocal = null;
53
+
54
+ function Leaf(props) @{
55
+ const rootT = Ctx.get();
56
+ <span class={'leaf-' + props.id}>
57
+ {rootT ? rootT.value : -1}
58
+ </span>
59
+ }
60
+
61
+ function Branch(props) @{
62
+ let &[local, localT] = track(0);
63
+ if (props.side === 'L') {
64
+ bumpLeftLocal = () => {
65
+ local += 1;
66
+ };
67
+ }
68
+ <div class={'branch-' + props.side}>
69
+ <Leaf id={props.side + '0'} />
70
+ <Leaf id={props.side + '1'} />
71
+ </div>
72
+ }
73
+
74
+ function App() @{
75
+ let &[root, rootT] = track(0);
76
+ Ctx.set(rootT);
77
+ bumpRoot = () => {
78
+ root += 1;
79
+ };
80
+ <div>
81
+ <Branch side="L" />
82
+ <Branch side="R" />
83
+ </div>
84
+ }
85
+
86
+ render(App);
87
+
88
+ const leaves = () => ['L0', 'L1', 'R0', 'R1'].map(
89
+ (id) => container.querySelector('.leaf-' + id).textContent,
90
+ );
91
+
92
+ expect(leaves()).toEqual(['0', '0', '0', '0']);
93
+
94
+ flushSync(() => bumpRoot());
95
+ expect(leaves()).toEqual(['1', '1', '1', '1']);
96
+
97
+ flushSync(() => bumpLeftLocal());
98
+ // Local bump touches no Context value, so leaf text is unchanged, but the
99
+ // update must flush without error and not disturb sibling subtrees.
100
+ expect(leaves()).toEqual(['1', '1', '1', '1']);
101
+
102
+ flushSync(() => bumpRoot());
103
+ expect(leaves()).toEqual(['2', '2', '2', '2']);
104
+ });
105
+ });