ripple 0.3.128 → 0.4.1

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 (197) hide show
  1. package/CHANGELOG.md +294 -0
  2. package/package.json +11 -4
  3. package/src/jsx-runtime.d.ts +12 -2
  4. package/src/runtime/element.js +23 -6
  5. package/src/runtime/index-client.js +81 -41
  6. package/src/runtime/index-server.js +1 -0
  7. package/src/runtime/internal/client/blocks.js +173 -91
  8. package/src/runtime/internal/client/component.js +4 -30
  9. package/src/runtime/internal/client/composite.js +12 -13
  10. package/src/runtime/internal/client/constants.js +9 -0
  11. package/src/runtime/internal/client/context.js +12 -19
  12. package/src/runtime/internal/client/css.js +7 -9
  13. package/src/runtime/internal/client/events.js +178 -90
  14. package/src/runtime/internal/client/expression.js +45 -25
  15. package/src/runtime/internal/client/for.js +409 -129
  16. package/src/runtime/internal/client/head.js +10 -5
  17. package/src/runtime/internal/client/html.js +1 -0
  18. package/src/runtime/internal/client/hydration.js +152 -10
  19. package/src/runtime/internal/client/if.js +239 -50
  20. package/src/runtime/internal/client/index.js +4 -12
  21. package/src/runtime/internal/client/operations.js +40 -61
  22. package/src/runtime/internal/client/portal.js +163 -85
  23. package/src/runtime/internal/client/render.js +44 -43
  24. package/src/runtime/internal/client/rpc.js +3 -3
  25. package/src/runtime/internal/client/runtime.js +592 -433
  26. package/src/runtime/internal/client/selector.js +5 -2
  27. package/src/runtime/internal/client/template.js +133 -158
  28. package/src/runtime/internal/client/transport.js +38 -0
  29. package/src/runtime/internal/client/try.js +465 -426
  30. package/src/runtime/internal/client/types.d.ts +56 -23
  31. package/src/runtime/internal/client/utils.js +1 -3
  32. package/src/runtime/internal/server/dynamic.js +20 -21
  33. package/src/runtime/internal/server/index.js +329 -277
  34. package/src/runtime/internal/server/rpc.js +3 -2
  35. package/src/runtime/internal/server/transport.js +16 -0
  36. package/src/runtime/transport.js +21 -0
  37. package/tests/client/__snapshots__/for.test.tsrx.snap +0 -5
  38. package/tests/client/array/array.copy-within.test.tsrx +12 -12
  39. package/tests/client/array/array.derived.test.tsrx +46 -46
  40. package/tests/client/array/array.iteration.test.tsrx +10 -10
  41. package/tests/client/array/array.mutations.test.tsrx +20 -20
  42. package/tests/client/array/array.static.test.tsrx +11 -11
  43. package/tests/client/array/array.to-methods.test.tsrx +6 -6
  44. package/tests/client/async-suspend.test.tsrx +74 -75
  45. package/tests/client/basic/basic.attributes.test.tsrx +123 -83
  46. package/tests/client/basic/basic.collections.test.tsrx +12 -12
  47. package/tests/client/basic/basic.components.test.tsrx +99 -25
  48. package/tests/client/basic/basic.errors.test.tsrx +18 -18
  49. package/tests/client/basic/basic.events.test.tsrx +42 -42
  50. package/tests/client/basic/basic.get-set.test.tsrx +8 -8
  51. package/tests/client/basic/basic.reactivity.test.tsrx +73 -38
  52. package/tests/client/basic/basic.rendering.test.tsrx +25 -25
  53. package/tests/client/basic/basic.utilities.test.tsrx +3 -3
  54. package/tests/client/boundaries.test.tsrx +8 -8
  55. package/tests/client/boxed-locals.test.tsrx +452 -0
  56. package/tests/client/compiler/__snapshots__/compiler.assignments.test.tsrx.snap +6 -6
  57. package/tests/client/compiler/compiler.assignments.test.tsrx +9 -73
  58. package/tests/client/compiler/compiler.attributes.test.tsrx +23 -23
  59. package/tests/client/compiler/compiler.basic.test.tsrx +15 -60
  60. package/tests/client/compiler/compiler.tracked-access.test.tsrx +18 -43
  61. package/tests/client/compiler/compiler.try-in-function.test.tsrx +2 -2
  62. package/tests/client/composite/__snapshots__/composite.render.test.tsrx.snap +0 -1
  63. package/tests/client/composite/composite.dynamic-components.test.tsrx +77 -6
  64. package/tests/client/composite/composite.props.test.tsrx +149 -12
  65. package/tests/client/composite/composite.reactivity.test.tsrx +51 -52
  66. package/tests/client/composite/composite.render.test.tsrx +26 -9
  67. package/tests/client/css/scoped-styles.test.tsrx +8 -8
  68. package/tests/client/date.test.tsrx +42 -42
  69. package/tests/client/derived-release.test.tsrx +370 -0
  70. package/tests/client/dynamic-elements.test.tsrx +45 -45
  71. package/tests/client/events.test.tsrx +97 -71
  72. package/tests/client/for-selector.test.tsrx +14 -14
  73. package/tests/client/for-single-root-items.test.tsrx +118 -3
  74. package/tests/client/for.test.tsrx +332 -31
  75. package/tests/client/head.test.tsrx +19 -19
  76. package/tests/client/html.test.tsrx +11 -9
  77. package/tests/client/if-block.test.tsrx +80 -0
  78. package/tests/client/map.test.tsrx +16 -16
  79. package/tests/client/media-query.test.tsrx +7 -7
  80. package/tests/client/portal.test.tsrx +224 -26
  81. package/tests/client/primitive-text.test.tsrx +50 -0
  82. package/tests/client/prop-kind-switch.test.tsrx +141 -0
  83. package/tests/client/ref.test.tsrx +17 -17
  84. package/tests/client/return.test.tsrx +26 -3
  85. package/tests/client/root-boundary.test.tsrx +42 -0
  86. package/tests/client/scoped-flush.test.tsrx +8 -8
  87. package/tests/client/set.test.tsrx +6 -6
  88. package/tests/client/svg.test.tsrx +11 -11
  89. package/tests/client/switch.test.tsrx +44 -44
  90. package/tests/client/track-async-hydration.test.tsrx +4 -4
  91. package/tests/client/try.test.tsrx +151 -83
  92. package/tests/client/tsx.test.tsrx +43 -40
  93. package/tests/client/url/url.derived.test.tsrx +6 -6
  94. package/tests/client/url-search-params/url-search-params.derived.test.tsrx +8 -8
  95. package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +10 -10
  96. package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +10 -10
  97. package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +18 -18
  98. package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +2 -2
  99. package/tests/hydration/basic.test.js +74 -1
  100. package/tests/hydration/build-components.js +22 -1
  101. package/tests/hydration/compiled/client/basic.js +1142 -713
  102. package/tests/hydration/compiled/client/composite.js +151 -100
  103. package/tests/hydration/compiled/client/events.js +184 -171
  104. package/tests/hydration/compiled/client/for.js +1472 -1132
  105. package/tests/hydration/compiled/client/fragment-cursor.js +2341 -0
  106. package/tests/hydration/compiled/client/head.js +254 -201
  107. package/tests/hydration/compiled/client/hmr.js +54 -41
  108. package/tests/hydration/compiled/client/html-in-template.js +54 -39
  109. package/tests/hydration/compiled/client/html.js +1633 -1263
  110. package/tests/hydration/compiled/client/if-children.js +420 -270
  111. package/tests/hydration/compiled/client/if-fragment-controlflow.js +363 -317
  112. package/tests/hydration/compiled/client/if.js +229 -219
  113. package/tests/hydration/compiled/client/mixed-control-flow.js +420 -404
  114. package/tests/hydration/compiled/client/nested-control-flow.js +1326 -1277
  115. package/tests/hydration/compiled/client/portal.js +87 -95
  116. package/tests/hydration/compiled/client/reactivity.js +135 -101
  117. package/tests/hydration/compiled/client/return.js +28 -19
  118. package/tests/hydration/compiled/client/streaming.js +390 -306
  119. package/tests/hydration/compiled/client/switch.js +258 -266
  120. package/tests/hydration/compiled/client/track-async-serialization.js +448 -280
  121. package/tests/hydration/compiled/client/try.js +173 -129
  122. package/tests/hydration/compiled/client/typed-text.js +71 -0
  123. package/tests/hydration/compiled/fixtures/money.js +10 -0
  124. package/tests/hydration/compiled/server/basic.js +123 -6
  125. package/tests/hydration/compiled/server/composite.js +16 -30
  126. package/tests/hydration/compiled/server/events.js +66 -18
  127. package/tests/hydration/compiled/server/for.js +117 -41
  128. package/tests/hydration/compiled/server/fragment-cursor.js +1687 -0
  129. package/tests/hydration/compiled/server/head.js +20 -20
  130. package/tests/hydration/compiled/server/hmr.js +2 -2
  131. package/tests/hydration/compiled/server/html.js +70 -238
  132. package/tests/hydration/compiled/server/if-children.js +107 -15
  133. package/tests/hydration/compiled/server/if.js +11 -15
  134. package/tests/hydration/compiled/server/mixed-control-flow.js +8 -8
  135. package/tests/hydration/compiled/server/portal.js +14 -22
  136. package/tests/hydration/compiled/server/reactivity.js +14 -14
  137. package/tests/hydration/compiled/server/streaming.js +19 -19
  138. package/tests/hydration/compiled/server/switch.js +8 -8
  139. package/tests/hydration/compiled/server/track-async-serialization.js +150 -23
  140. package/tests/hydration/compiled/server/try.js +8 -8
  141. package/tests/hydration/compiled/server/typed-text.js +17 -0
  142. package/tests/hydration/components/basic.tsrx +44 -8
  143. package/tests/hydration/components/composite.tsrx +2 -2
  144. package/tests/hydration/components/events.tsrx +25 -25
  145. package/tests/hydration/components/for.tsrx +115 -67
  146. package/tests/hydration/components/fragment-cursor.tsrx +650 -0
  147. package/tests/hydration/components/head.tsrx +16 -16
  148. package/tests/hydration/components/hmr.tsrx +2 -2
  149. package/tests/hydration/components/html.tsrx +7 -7
  150. package/tests/hydration/components/if-children.tsrx +72 -25
  151. package/tests/hydration/components/if.tsrx +18 -18
  152. package/tests/hydration/components/mixed-control-flow.tsrx +11 -11
  153. package/tests/hydration/components/portal.tsrx +3 -3
  154. package/tests/hydration/components/reactivity.tsrx +16 -16
  155. package/tests/hydration/components/streaming.tsrx +20 -20
  156. package/tests/hydration/components/switch.tsrx +20 -20
  157. package/tests/hydration/components/text-types.ts +4 -0
  158. package/tests/hydration/components/track-async-serialization.tsrx +80 -26
  159. package/tests/hydration/components/try.tsrx +8 -8
  160. package/tests/hydration/components/typed-text.tsrx +22 -0
  161. package/tests/hydration/fixtures/money.js +10 -0
  162. package/tests/hydration/for.test.js +36 -0
  163. package/tests/hydration/fragment-cursor.test.js +111 -0
  164. package/tests/hydration/if-children.test.js +24 -0
  165. package/tests/hydration/streaming.test.js +19 -0
  166. package/tests/hydration/track-async-serialization.test.js +87 -2
  167. package/tests/hydration/tsconfig.text-types.json +10 -0
  168. package/tests/hydration/typed-text.test.js +34 -0
  169. package/tests/server/await.test.tsrx +3 -3
  170. package/tests/server/basic.attributes.test.tsrx +70 -42
  171. package/tests/server/basic.components.test.tsrx +64 -8
  172. package/tests/server/basic.test.tsrx +34 -32
  173. package/tests/server/compiler.test.tsrx +1 -70
  174. package/tests/server/composite.props.test.tsrx +6 -6
  175. package/tests/server/dynamic-elements.test.tsrx +31 -8
  176. package/tests/server/for.test.tsrx +94 -0
  177. package/tests/server/head.test.tsrx +11 -11
  178. package/tests/server/scoped-styles.test.tsrx +2 -2
  179. package/tests/server/streaming-ssr.test.tsrx +48 -43
  180. package/tests/server/track-async-serialization.test.tsrx +204 -21
  181. package/tests/server/try.test.tsrx +48 -48
  182. package/tests/types/transport.ts +40 -0
  183. package/tests/utils/jsx-runtime-types.test.js +67 -2
  184. package/tests/utils/tracked-types.test.js +117 -0
  185. package/tests/utils/vite-plugin-config.test.js +23 -0
  186. package/types/index.d.ts +49 -9
  187. package/types/server.d.ts +3 -0
  188. package/types/transport.d.ts +15 -0
  189. package/src/runtime/internal/client/switch.js +0 -113
  190. package/src/utils/errors.js +0 -13
  191. package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +0 -12
  192. package/tests/client/compiler/__snapshots__/compiler.typescript.test.rsrx.snap +0 -46
  193. package/tests/client/lazy-array.test.tsrx +0 -31
  194. package/tests/client/lazy-destructuring.test.tsrx +0 -438
  195. package/tests/client/tracked-index-access.test.tsrx +0 -113
  196. package/tests/server/lazy-destructuring.test.tsrx +0 -595
  197. package/tests/server/tracked-index-access.test.tsrx +0 -76
package/CHANGELOG.md CHANGED
@@ -1,5 +1,299 @@
1
1
  # ripple
2
2
 
3
+ ## 0.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1477](https://github.com/Ripple-TS/ripple/pull/1477)
8
+ [`670d669`](https://github.com/Ripple-TS/ripple/commit/670d66916a0b2d58254adb3248abc9d3e816fd8d)
9
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Use property-specific CSS
10
+ types for HTML and SVG style objects and export `CSSProperties` from `ripple`
11
+ and `ripple/jsx-runtime`. Numeric lengths such as `width: 400` now produce a
12
+ type error; specify units with `width: '400px'` or `width: '24rem'`. Unitless
13
+ CSS properties, zero lengths, camelCase and kebab-case names, and CSS custom
14
+ properties remain supported.
15
+
16
+ Style properties set to `null` or `undefined` are omitted during server
17
+ rendering and removed on the client, matching the nullable and optional property
18
+ types and supporting conditional style values. Ripple does not infer or add CSS
19
+ units.
20
+
21
+ Re-export the `JSX` and `Ripple` namespaces as types from `ripple` so element
22
+ prop and event types can be imported from the main package. The
23
+ `ripple/jsx-runtime` exports remain available.
24
+
25
+ - [#1475](https://github.com/Ripple-TS/ripple/pull/1475)
26
+ [`e008b3f`](https://github.com/Ripple-TS/ripple/commit/e008b3f933a5a5b54ca8b74ae80aa98b6ddfb82f)
27
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Show public Tracked, Derived,
28
+ and WritableDerived type names in value property hovers.
29
+
30
+ ## 0.4.0
31
+
32
+ ### Minor Changes
33
+
34
+ - [#1472](https://github.com/Ripple-TS/ripple/pull/1472)
35
+ [`5d3e60d`](https://github.com/Ripple-TS/ripple/commit/5d3e60d86129ac20d23c86a923914ee44efea9d1)
36
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Lazy destructuring is removed
37
+ from the language (tsrx RFC
38
+ [#106](https://github.com/Ripple-TS/ripple/issues/106)). `&[ ... ]` and
39
+ `&{ ... }` are syntax errors now; a tracked value is read and written through
40
+ `.value`, and a `Tracked`, `Derived`, or `WritableDerived` object is passed to a
41
+ child as it is (`<Child {count} />`, or `count={track(() => count.value)}` for a
42
+ read-only view).
43
+
44
+ | Before | After |
45
+ | ---------------------------------------------------------------- | ------------------------------------------------------------------------ |
46
+ | `let &[count] = track(0); count++; {count}` | `const count = track(0); count.value++; {count.value}` |
47
+ | `let &[count, countT] = track(0); <Child count={countT} />` | `const count = track(0); <Child {count} />` |
48
+ | `let &[double] = track(() => count * 2)` | `const double = track(() => count.value * 2)` |
49
+ | `function Card({ count: &[count] }: { count: Tracked<number> })` | `function Card({ count }: { count: Tracked<number> })` and `count.value` |
50
+
51
+ `Tracked<V>`, `Derived<V>`, and `WritableDerived<V>` are plain `{ value: V }`
52
+ shapes; the `[V, Tracked<V>]` tuple member, the runtime `[0]`/`[1]` accessors,
53
+ and the `lazy_array_*` helpers are gone, and `count[0]` reports the ordinary
54
+ "use `.value`" error. Rest and default patterns lower to native destructuring: a
55
+ keyed `@for` pattern with a rest element or a default is destructured once per
56
+ item change, and a destructuring assignment onto boxed `let`s writes the boxes
57
+ in place; the `fallback`, `exclude_from_object`, and `array_slice` runtime
58
+ helpers are gone. On the server a dynamic tag `<{tag} />` is lowered to
59
+ `_$_.dynamic_element(tag, props)`, with the tag and the element's own props
60
+ passed separately, so the props never carry an `is` entry.
61
+
62
+ ### Patch Changes
63
+
64
+ - [#1458](https://github.com/Ripple-TS/ripple/pull/1458)
65
+ [`bbc1445`](https://github.com/Ripple-TS/ripple/commit/bbc14455485e054c145ebc78f08a771d41ea16f1)
66
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Lighter list, `@if`, and
67
+ mount output on the client. A controlled `@for` (the sole content of its
68
+ element) appends its items into the parent instead of inserting before an empty
69
+ text anchor, and an `@if` at the root of such an item creates an anchor only
70
+ once it needs a position of its own (a branch swap, or a branch that renders
71
+ nothing), so a keyed list of `@if` items keeps no anchor nodes in the DOM;
72
+ `mount()` likewise renders into the target without an anchor node. An `@if`
73
+ block now renders its branch directly and owns the branch's DOM range, and a
74
+ list keeps its inputs in block state, so every `@if` and `@for` allocates one
75
+ block less and no closures. Text and class updates compare against the last
76
+ value in the render block's state instead of a cache on the DOM node; the
77
+ compiler emits that compared form for single updates as well.
78
+
79
+ A selector key whose subscribers all left stays in the selector's map for reuse
80
+ when the key comes back, and is swept once more than 1024 released keys pile up.
81
+
82
+ - [#1469](https://github.com/Ripple-TS/ripple/pull/1469)
83
+ [`b3a38ce`](https://github.com/Ripple-TS/ripple/commit/b3a38ce0aae66a9dcac0b663c30161eed0d7547a)
84
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Add custom serialization for
85
+ trackAsync hydration and RPC arguments/results using `transport` handlers in
86
+ ripple.config.ts. Register matching encoders and decoders automatically in
87
+ development and production, and expose `setTransport` for custom integrations.
88
+ Apps without a transport retain the plain JSON hydration fast path. Provide a
89
+ browser entry for config helpers so importing defineConfig does not load the
90
+ Vite plugin's Node.js dependencies during hydration. A configured hydration
91
+ payload embeds devalue's flattened form directly, so the client revives it
92
+ without a second string encoding.
93
+
94
+ - [#1460](https://github.com/Ripple-TS/ripple/pull/1460)
95
+ [`2ef5da5`](https://github.com/Ripple-TS/ripple/commit/2ef5da52b26fc13e11537dd28df0e2b376ca0f52)
96
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Cheaper element refs,
97
+ effects, and component templates on the client. A `ref` is one effect block
98
+ keyed on its state instead of a render block that re-evaluates the thunk and
99
+ creates a branch and an effect for it, since the ref value is read untracked and
100
+ fixed for the life of the enclosing block. Effects deferred until a component
101
+ has rendered are recorded as flat entries, and an effect's first run skips the
102
+ child and teardown sweep. A tracked value written during a flush keeps its
103
+ previous value for teardowns on itself instead of in a map, and `flushSync`
104
+ releases those values when it finishes (they were only released by the microtask
105
+ flush before).
106
+
107
+ The compiler types the bindings of a destructured pattern
108
+ (`{ item }: { item: Item }` as component props, or `const { item } = props` from
109
+ a typed initializer), so their property reads lower to direct text and attribute
110
+ writes. A component whose body has setup statements beside a single root element
111
+ clones that element instead of a fragment.
112
+
113
+ - [#1470](https://github.com/Ripple-TS/ripple/pull/1470)
114
+ [`0321791`](https://github.com/Ripple-TS/ripple/commit/03217912651e0a49edb8060a92602fd0e0abe193)
115
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Component props are plain
116
+ objects, evaluated once. A call site such as `<Child a={x} b="y" />` compiles to
117
+ the object literal `{ a: x, b: 'y' }` and passes it to the component as is: no
118
+ getters, no props class, no Proxy for spreads, no `Props` helpers, and no
119
+ `Object.keys`/spread lowering, so props behave like any other object in `.tsrx`
120
+ and `.ts` code alike. A prop expression is evaluated when the component is
121
+ called, so a child that must follow a change now receives a live value
122
+ explicitly: a tracked object (`count={count}`) or a derived
123
+ (`title={track(() => label + count.value)}`), read through `.value`.
124
+ `count={count.value}` passes the current number. `track(fn)` is typed
125
+ `Derived<V>` with a read-only `value`; `track(fn, get, set)` or
126
+ `track(fn, undefined, true)` is a `WritableDerived<V>`, and a write to a
127
+ read-only derived warns in development. A dynamic element
128
+ (`<{tag} class={x} />`) still updates its attributes reactively. `Props.keys`,
129
+ `Props.values`, `Props.entries`, `Props.has`, `Props.spread`, `Props.rest`,
130
+ `Props.ownSymbolKeys` and `Props.ownAllKeys` are removed, as they are plain
131
+ `Object` operations now.
132
+
133
+ - [#1457](https://github.com/Ripple-TS/ripple/pull/1457)
134
+ [`020d269`](https://github.com/Ripple-TS/ripple/commit/020d2699f8e1333367ac9f7abe9247bab55e272d)
135
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Fix hydration when a fragment
136
+ root or a slot is followed by siblings. The compiler's closing `next(n)` for a
137
+ fragment root now counts from wherever the cursor was last positioned, so a
138
+ trailing element navigated to for an event or attribute, tracked text, a
139
+ control-flow block, a component, or a `{expression}` no longer makes the cursor
140
+ overshoot or fall short of the fragment's last node; a trailing element that was
141
+ descended into is popped back to. Control-flow bodies nested inside an element
142
+ now emit that `next(n)` too. At runtime, a `{expression}` leaves the cursor on
143
+ its own end marker like every other block, and a multi-node branch or `@for`
144
+ item steps to its block's end marker after appending, so the next sibling, item,
145
+ or component adopts the right node.
146
+
147
+ - [#1462](https://github.com/Ripple-TS/ripple/pull/1462)
148
+ [`453bf4b`](https://github.com/Ripple-TS/ripple/commit/453bf4ba8b1b31963df26696545e446ab4b1658e)
149
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Faster keyed lists and
150
+ lighter component output on the client. A `@for` keyed by the item itself
151
+ (`key item`) keys by identity with no key callback, so a re-run compares the
152
+ item arrays directly and skips the per-item value update; a same-length keyed
153
+ re-run rewrites the list's block array in place instead of allocating a new one,
154
+ with the unchanged prefix and suffix skipped by plain compares. Static child
155
+ components that follow their template siblings append into the parent element
156
+ instead of inserting before a `<!>` placeholder, so a component with trailing
157
+ child components clones fewer nodes and leaves no comment anchors in the DOM.
158
+ The compiler also infers a primitive text type through a call whose callee is
159
+ declared to return one (a binding typed as a function type, an `as` assertion, a
160
+ function with a declared return type, or a method or function-typed property of
161
+ an annotated object), so such text lowers to `set_text` instead of a generic
162
+ expression block.
163
+
164
+ - [#1463](https://github.com/Ripple-TS/ripple/pull/1463)
165
+ [`411809f`](https://github.com/Ripple-TS/ripple/commit/411809fb38f25b259a1078a901839318e4207871)
166
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Faster cold hydration and
167
+ server rendering. `hydrate()` accepts `rootBoundary: false` like `mount()`,
168
+ stepping over the server's root marker itself instead of rendering under a `try`
169
+ block (a streamed shell whose root suspended still hydrates under the default
170
+ boundary, which alone can adopt and activate its slot). The client runtime
171
+ compiles less on a cold page: the hydration cursor steps live in one module and
172
+ assign the cursor directly, a list's first render never compiles the diff, a
173
+ template's clone path stays out of its hydrating closure, `run_block` keeps
174
+ re-run and teardown handling in separate functions, and the compiler adopts an
175
+ element's lone text child in place (`hydrate_text`) with no `pop()`. A `@for`
176
+ whose `index` nothing reads allocates no tracked index per item. Removing the
177
+ server's inline styles now starts from an animation frame rather than inside
178
+ `mount()`/`hydrate()`.
179
+
180
+ On the server, `render()` joins its buffer tree with a plain walk instead of
181
+ `flat(Infinity).join('')`, `escape()` returns strings without `&`, `<` or `"`
182
+ after a single regex test, and an `Output` allocates its stream, css and
183
+ async-operation collections only for the root or on first use.
184
+
185
+ - [#1453](https://github.com/Ripple-TS/ripple/pull/1453)
186
+ [`82bc9ee`](https://github.com/Ripple-TS/ripple/commit/82bc9ee585c6b273bf69890bc2f230aba922bf19)
187
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Speed up portals and root
188
+ event delegation. `<Portal>` now lowers to a dedicated `portal()` runtime call,
189
+ portal content appends straight into its target without a placeholder node,
190
+ delegated root listeners are shared per target instead of per portal, event
191
+ registration no longer scans every root target, and `@if` blocks and compiled
192
+ `event()` calls allocate fewer closures. `mount()` accepts `rootBoundary: false`
193
+ to render without the default root try/pending boundary. Cold code paths
194
+ (keyed-list diff, block error handling, effect-phase flushing, hydration
195
+ branches) live in their own functions so a first mount compiles less. A
196
+ component whose root is `@if`, `@for`, `@switch`, or `@try` now re-renders at
197
+ its own position when it is portal content or one of an element's all-component
198
+ children, instead of at the end of the parent; `mount()`/`hydrate()` disposers
199
+ are safe to call more than once.
200
+
201
+ - [#1470](https://github.com/Ripple-TS/ripple/pull/1470)
202
+ [`0321791`](https://github.com/Ripple-TS/ripple/commit/03217912651e0a49edb8060a92602fd0e0abe193)
203
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Cheaper component
204
+ instantiation and `@if` chains on the client. An `@else if`, and a branch whose
205
+ only statement is another `@if`, fold into the enclosing `@if` block on both the
206
+ client and the server, so a chain costs one block and one hydration boundary. An
207
+ `@if` whose condition reads no tracked state renders its branch directly under
208
+ the enclosing block with no block of its own (a block without dependencies is
209
+ never scheduled, so nothing is lost); a dynamic condition keeps its block and
210
+ adopts the dependencies recorded while it was evaluated. A module-level
211
+ component also carries a direct render entry so `render_component` skips the
212
+ element it would otherwise return. An `@if` condition and its branches compile
213
+ to module-level functions when they capture at most one local (passed through
214
+ the if runtime), a condition returns the branch function to render instead of
215
+ calling a selection callback, and a render block's function is module-level with
216
+ its captured locals carried on the block state, so a component instantiation
217
+ creates no closures for its ifs or its reactive text and attribute updates.
218
+ `Context.get()` walks a chain of the context entries that were set rather than
219
+ every ancestor component. A module-level component's render body is a
220
+ module-level function that receives the props from the element instead of a
221
+ closure created per instantiation, `Context.get()`/`set()` calls on a
222
+ `new Context()` binding skip the `with_scope` wrapper, and `mount()` only
223
+ schedules the SSR style removal when the page has server-rendered inline styles.
224
+ A `@switch` now lowers onto the same runtime as `@if`: its selector returns the
225
+ case to render, so a `@switch` over a value that reads no tracked state renders
226
+ its case with no block, its cases are module-level functions when they capture
227
+ at most one local, and the separate switch runtime is gone. The probe that
228
+ decides whether an `@if` needs a block now renders a static branch itself, one
229
+ runtime call instead of four. A module-level component with a destructuring
230
+ parameter gets the direct render entry as well, and an `@if` or `@switch` is
231
+ hoisted whatever number of locals it captures: several travel as one object
232
+ literal destructured in the hoisted signatures. A binding that hoisted template
233
+ code writes (a `ref={name}` setter, a handler that assigns it) or that is
234
+ reassigned after template code reads it is compiled to a `{ v }` box, so the
235
+ hoisted functions share the variable with the component exactly as a closure
236
+ would: a plain `let`, a name inside a destructuring pattern of any shape, a
237
+ function or catch parameter (reboxed first thing in the body), and a `let`
238
+ written through a destructuring or `for...of` assignment target.
239
+
240
+ - [#1473](https://github.com/Ripple-TS/ripple/pull/1473)
241
+ [`eb638ea`](https://github.com/Ripple-TS/ripple/commit/eb638ea4a3c73b7ecb98ccedfffc576661dc67b3)
242
+ Thanks [@leonidaz](https://github.com/leonidaz)! - A derived created during a
243
+ block's run (a `track(() => ...)` or `tracked.readOnly()` in an `@if` branch, a
244
+ keyed `@for` item, a render expression, or component setup) is now unsubscribed
245
+ from its sources when that block reruns or is destroyed, unless a reader that is
246
+ still alive holds it. Before, such deriveds stayed subscribed until the block
247
+ that owned them was destroyed, so a branch that toggled repeatedly accumulated
248
+ one stale derived per toggle, each walked on every write to the source. The
249
+ bookkeeping lives in a side table keyed by the creating block, so blocks that
250
+ create no deriveds pay nothing.
251
+
252
+ - [#1466](https://github.com/Ripple-TS/ripple/pull/1466)
253
+ [`2631804`](https://github.com/Ripple-TS/ripple/commit/2631804cd28980b213643ac4ef5e5bcb814eae57)
254
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Faster streaming SSR. A
255
+ resolved `trackAsync` value made of plain data (strings, finite numbers,
256
+ booleans, `null`, arrays and plain objects, each reachable once) now travels in
257
+ its hydration script as raw JSON that `JSON.parse` of the envelope yields
258
+ directly, instead of a devalue-encoded string; values JSON cannot represent
259
+ (`undefined`, `NaN`, `-0`, bigints, Dates, Maps, Sets, shared references,
260
+ cycles) keep the devalue encoding, and the client reads either form. Boundaries
261
+ that settle in the same task (data arriving together, a batch of promises
262
+ resolved by one timer or I/O callback) stream as one chunk, flushed once the
263
+ task's microtasks have drained; closing the stream flushes whatever is still
264
+ queued first. `devalue` is updated to 5.9.2, with the stringify performance
265
+ patch proposed upstream in sveltejs/devalue#190 applied through pnpm's
266
+ `patchedDependencies` until it is released.
267
+
268
+ - [#1472](https://github.com/Ripple-TS/ripple/pull/1472)
269
+ [`5d3e60d`](https://github.com/Ripple-TS/ripple/commit/5d3e60d86129ac20d23c86a923914ee44efea9d1)
270
+ Thanks [@leonidaz](https://github.com/leonidaz)! - `tracked.readOnly()` returns
271
+ a read-only view of a `Tracked` or `WritableDerived`: a `Derived<V>` that
272
+ follows the value and rejects writes, for a child, function, or context that
273
+ should only read it. It is equivalent to `track(() => tracked.value)`. On a
274
+ derived that is already read-only, `readOnly()` returns the derived itself.
275
+
276
+ - [#1465](https://github.com/Ripple-TS/ripple/pull/1465)
277
+ [`8ace206`](https://github.com/Ripple-TS/ripple/commit/8ace206bfed2cd80b758e6e72f94084d2ffe1455)
278
+ Thanks [@leonidaz](https://github.com/leonidaz)! - `@try` blocks and the root
279
+ boundary keep their state in one explicit `TryState` object, and the pending,
280
+ catch, request and streaming helpers are module functions that take it. A
281
+ boundary allocates its state and one branch closure instead of a closure per
282
+ helper.
283
+ - Updated dependencies
284
+ [[`bbc1445`](https://github.com/Ripple-TS/ripple/commit/bbc14455485e054c145ebc78f08a771d41ea16f1),
285
+ [`0321791`](https://github.com/Ripple-TS/ripple/commit/03217912651e0a49edb8060a92602fd0e0abe193),
286
+ [`2ef5da5`](https://github.com/Ripple-TS/ripple/commit/2ef5da52b26fc13e11537dd28df0e2b376ca0f52),
287
+ [`0321791`](https://github.com/Ripple-TS/ripple/commit/03217912651e0a49edb8060a92602fd0e0abe193),
288
+ [`020d269`](https://github.com/Ripple-TS/ripple/commit/020d2699f8e1333367ac9f7abe9247bab55e272d),
289
+ [`453bf4b`](https://github.com/Ripple-TS/ripple/commit/453bf4ba8b1b31963df26696545e446ab4b1658e),
290
+ [`411809f`](https://github.com/Ripple-TS/ripple/commit/411809fb38f25b259a1078a901839318e4207871),
291
+ [`82bc9ee`](https://github.com/Ripple-TS/ripple/commit/82bc9ee585c6b273bf69890bc2f230aba922bf19),
292
+ [`69d50d9`](https://github.com/Ripple-TS/ripple/commit/69d50d9849d8484a88d39b2908bb682d27ec7bc9),
293
+ [`0321791`](https://github.com/Ripple-TS/ripple/commit/03217912651e0a49edb8060a92602fd0e0abe193),
294
+ [`5d3e60d`](https://github.com/Ripple-TS/ripple/commit/5d3e60d86129ac20d23c86a923914ee44efea9d1)]:
295
+ - @tsrx/ripple@0.2.0
296
+
3
297
  ## 0.3.128
4
298
 
5
299
  ### Patch Changes
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Ripple is an elegant TypeScript UI framework",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.3.128",
6
+ "version": "0.4.1",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index-client.js",
9
9
  "main": "src/runtime/index-client.js",
@@ -29,6 +29,12 @@
29
29
  "default": "./src/runtime/index-server.js"
30
30
  },
31
31
  "./package.json": "./package.json",
32
+ "./transport": {
33
+ "types": "./types/transport.d.ts",
34
+ "worker": "./src/runtime/internal/server/transport.js",
35
+ "browser": "./src/runtime/internal/client/transport.js",
36
+ "default": "./src/runtime/internal/server/transport.js"
37
+ },
32
38
  "./server": {
33
39
  "types": "./types/server.d.ts",
34
40
  "default": "./src/server/index.js"
@@ -71,10 +77,11 @@
71
77
  },
72
78
  "dependencies": {
73
79
  "clsx": "^2.1.1",
74
- "devalue": "^5.8.1",
80
+ "csstype": "^3.2.3",
81
+ "devalue": "^5.9.2",
75
82
  "esm-env": "^1.2.2",
76
- "@tsrx/core": "^0.1.67",
77
- "@tsrx/ripple": "0.1.65"
83
+ "@tsrx/core": "^0.2.0",
84
+ "@tsrx/ripple": "0.2.0"
78
85
  },
79
86
  "devDependencies": {
80
87
  "@types/estree": "^1.0.8",
@@ -7,6 +7,7 @@ import type {
7
7
  TSRXElement,
8
8
  } from '#public';
9
9
  import type { Nullable } from '@tsrx/core/types/helpers';
10
+ import type * as CSS from 'csstype';
10
11
  export type { RefValue } from '#public';
11
12
 
12
13
  /**
@@ -50,10 +51,18 @@ export function jsxs(
50
51
  */
51
52
  export function Fragment(props: FragmentProps): TSRXElement;
52
53
 
54
+ type NullableStyleProperties<T> = { [Property in keyof T]: T[Property] | null };
55
+
53
56
  /** JSX attributes live under Ripple so editor hovers identify their framework. */
54
57
  declare namespace Ripple {
55
58
  type ClassValue = string | import('clsx').ClassArray | import('clsx').ClassDictionary;
56
59
 
60
+ /** Inline CSS styles. Values keep their units; null and undefined remove a property. */
61
+ interface CSSProperties
62
+ extends NullableStyleProperties<CSS.Properties>, NullableStyleProperties<CSS.PropertiesHyphen> {
63
+ [property: `--${string}`]: string | number | null | undefined;
64
+ }
65
+
57
66
  type Event<
58
67
  Target extends globalThis.EventTarget,
59
68
  EventType extends globalThis.Event,
@@ -493,7 +502,7 @@ declare namespace Ripple {
493
502
  class?: ClassValue | undefined | null;
494
503
  className?: Nullable<string>;
495
504
  id?: Nullable<string>;
496
- style?: Nullable<string> | Record<string, string | number>;
505
+ style?: Nullable<string> | CSSProperties;
497
506
  title?: Nullable<string>;
498
507
  lang?: Nullable<string>;
499
508
  dir?: 'ltr' | 'rtl' | 'auto';
@@ -627,7 +636,7 @@ declare namespace Ripple {
627
636
  // Styling
628
637
  class?: ClassValue | undefined | null;
629
638
  className?: Nullable<string>;
630
- style?: Nullable<string> | Record<string, string | number>;
639
+ style?: Nullable<string> | CSSProperties;
631
640
 
632
641
  // Presentation attributes
633
642
  alignmentBaseline?:
@@ -1405,6 +1414,7 @@ declare namespace Ripple {
1405
1414
  }
1406
1415
 
1407
1416
  export import ClassValue = Ripple.ClassValue;
1417
+ export import CSSProperties = Ripple.CSSProperties;
1408
1418
  export import JSX = Ripple.JSX;
1409
1419
  export { Ripple };
1410
1420
 
@@ -1,21 +1,38 @@
1
- const TSRX_ELEMENT = Symbol.for('ripple.element');
1
+ export const TSRX_ELEMENT = Symbol.for('ripple.element');
2
2
 
3
3
  /**
4
4
  * @typedef {{
5
5
  * render: Function;
6
+ * p: any;
6
7
  * [TSRX_ELEMENT]: true;
7
8
  * }} TSRXElement
8
9
  */
9
10
 
10
11
  /**
12
+ * Elements share one constructor so every instance has the same shape and the
13
+ * marker lives on the prototype instead of being defined per allocation.
11
14
  * @param {Function} render
15
+ * @param {any} p
16
+ * @this {TSRXElement}
17
+ */
18
+ function TSRXElementImpl(render, p) {
19
+ this.render = render;
20
+ this.p = p;
21
+ }
22
+ /** @type {any} */ (TSRXElementImpl.prototype)[TSRX_ELEMENT] = true;
23
+
24
+ /**
25
+ * @param {Function} render `(anchor, block, p) => …`
26
+ * @param {any} [p] a value handed back to `render` as its third argument: a
27
+ * module-level component render function receives the component's props
28
+ * this way instead of closing over them, so instantiating the component
29
+ * allocates no closure
12
30
  * @returns {TSRXElement}
13
31
  */
14
- export function tsrx_element(render) {
15
- return {
16
- render,
17
- [TSRX_ELEMENT]: true,
18
- };
32
+ export function tsrx_element(render, p = null) {
33
+ return /** @type {TSRXElement} */ (
34
+ /** @type {unknown} */ (new /** @type {any} */ (TSRXElementImpl)(render, p))
35
+ );
19
36
  }
20
37
 
21
38
  /**
@@ -1,28 +1,24 @@
1
- /** @import { RootBoundaryOptions } from '#client' */
1
+ /** @import { AppendIntoAnchor, RootBoundaryOptions } from '#client' */
2
2
 
3
3
  import { destroy_block, root } from './internal/client/blocks.js';
4
- import { handle_root_events } from './internal/client/events.js';
5
- import {
6
- get_first_child,
7
- get_next_sibling,
8
- init_operations,
9
- } from './internal/client/operations.js';
4
+ import { handle_root_events, release_root_events } from './internal/client/events.js';
5
+ import { init_operations } from './internal/client/operations.js';
10
6
  import { render_component } from './internal/client/component.js';
11
- import { create_anchor } from './internal/client/utils.js';
12
7
  import { try_block } from './internal/client/try.js';
13
- import { remove_ssr_css } from './internal/client/css.js';
8
+ import { remove_styles } from './internal/client/css.js';
14
9
  import { normalize_children } from './element.js';
15
10
  import {
16
- clear_track_hash_reference,
11
+ hydrate_next,
17
12
  hydrate_node,
18
13
  hydrating,
19
- set_hydrate_node,
20
- set_hydrating,
14
+ set_hydration,
15
+ track_hash_reference,
21
16
  } from './internal/client/hydration.js';
22
17
  import { COMMENT_NODE, HYDRATION_START } from '../constants.js';
23
18
 
24
19
  // Re-export JSX runtime functions for jsxImportSource: "ripple"
25
20
  export { jsx, jsxs, Fragment } from '../jsx-runtime.js';
21
+ export { set_transport as setTransport } from './internal/client/transport.js';
26
22
  export {
27
23
  UNINITIALIZED,
28
24
  TRACKED_UPDATED,
@@ -31,7 +27,7 @@ export {
31
27
  } from './internal/client/constants.js';
32
28
 
33
29
  /**
34
- * @param {Node} anchor
30
+ * @param {Node | AppendIntoAnchor} anchor
35
31
  * @param {(anchor: Node) => void} render_content
36
32
  * @param {RootBoundaryOptions | undefined} boundary
37
33
  * @returns {void}
@@ -55,6 +51,7 @@ function render_root_boundary(anchor, render_content, boundary) {
55
51
  render_component(Pending, pending_anchor, {});
56
52
  }
57
53
  },
54
+ true,
58
55
  );
59
56
  }
60
57
 
@@ -63,71 +60,104 @@ function render_root_boundary(anchor, render_content, boundary) {
63
60
  * get, so `mount`/`hydrate` accept a component function as the `children`
64
61
  * prop.
65
62
  *
66
- * @param {Record<string, any> | undefined} props
63
+ * @param {Record<string, any>} props
67
64
  * @returns {Record<string, any>}
68
65
  */
69
66
  function normalize_props(props) {
70
- if (props?.children != null) {
71
- return { ...props, children: normalize_children(props.children) };
72
- }
73
-
74
- return props || {};
67
+ return { ...props, children: normalize_children(props.children) };
75
68
  }
76
69
 
77
70
  /**
71
+ * `rootBoundary` configures the default `try`/`pending`/`catch` boundary the
72
+ * app is rendered under; `false` renders without one. Without a root
73
+ * boundary, `trackAsync()` must sit inside a user `@try` block, and errors
74
+ * that escape one propagate out of the flush.
78
75
  * @param {Function} component
79
- * @param {{ props?: Record<string, any>, target: HTMLElement, rootBoundary?: RootBoundaryOptions }} options
76
+ * @param {{ props?: Record<string, any>, target: HTMLElement, rootBoundary?: RootBoundaryOptions | false }} options
80
77
  * @returns {() => void}
81
78
  */
82
79
  export function mount(component, options) {
83
80
  init_operations();
84
- remove_ssr_css();
81
+ // Only a page with server-rendered inline styles has anything to remove.
82
+ // Requesting a frame otherwise makes the browser attach layout to the
83
+ // mounted tree before the app's next update, which slows a teardown that
84
+ // would have preceded the first paint.
85
+ if (document.querySelector('style[data-ripple-ssr]') !== null) {
86
+ requestAnimationFrame(remove_styles);
87
+ }
85
88
 
86
- const props = normalize_props(options.props);
89
+ let props = options.props ?? {};
90
+ if (props.children != null) {
91
+ props = normalize_props(props);
92
+ }
87
93
  const target = options.target;
88
- const anchor = create_anchor();
89
94
 
90
95
  // Clear target content in case of SSR
91
96
  if (target.firstChild) {
92
97
  target.textContent = '';
93
98
  }
94
99
 
95
- target.append(anchor);
100
+ // The app appends into the emptied target; a root that keeps inserting
101
+ // relative to its anchor materializes one (see `resolve_anchor`).
102
+ /** @type {AppendIntoAnchor} */
103
+ const anchor = { parent: target, into: true };
96
104
 
97
- const cleanup_events = handle_root_events(target);
105
+ /** @type {import('./internal/client/events.js').RootTargetRef | null} */
106
+ let events_ref = handle_root_events(target);
107
+
108
+ const root_boundary = options.rootBoundary;
98
109
 
99
110
  const _root = root(() => {
111
+ if (root_boundary === false) {
112
+ render_component(component, anchor, props);
113
+ return;
114
+ }
100
115
  render_root_boundary(
101
116
  anchor,
102
117
  (component_anchor) => {
103
118
  render_component(component, component_anchor, props);
104
119
  },
105
- options.rootBoundary,
120
+ root_boundary,
106
121
  );
107
122
  });
108
123
 
109
124
  return () => {
110
- cleanup_events();
125
+ // The disposer may be called again (a remount, HMR, a stale reference);
126
+ // only the first call owns the target's delegated-listener ref.
127
+ if (events_ref === null) return;
128
+ release_root_events(events_ref);
129
+ events_ref = null;
111
130
  destroy_block(_root);
112
131
  };
113
132
  }
114
133
 
115
134
  /**
135
+ * `rootBoundary` works as in {@link mount}: the app hydrates under a default
136
+ * `try` boundary, or with `false` directly under the root block. Server output
137
+ * always carries the root boundary markers; without a boundary, `hydrate()`
138
+ * steps over a plain `<!--[-->` itself. A streamed shell whose root suspended
139
+ * starts with a `<!--[?N-->` / `<!--[!N-->` slot instead, and only a boundary
140
+ * can adopt its fallback and activate the chunk, so that shell hydrates under
141
+ * the default boundary regardless.
116
142
  * @param {Function} component
117
- * @param {{ props?: Record<string, any>, target: HTMLElement, rootBoundary?: RootBoundaryOptions }} options
143
+ * @param {{ props?: Record<string, any>, target: HTMLElement, rootBoundary?: RootBoundaryOptions | false }} options
118
144
  * @returns {() => void}
119
145
  */
120
146
  export function hydrate(component, options) {
121
147
  init_operations();
122
- remove_ssr_css();
148
+ requestAnimationFrame(remove_styles);
123
149
 
124
- const props = normalize_props(options.props);
150
+ let props = options.props ?? {};
151
+ if (props.children != null) {
152
+ props = normalize_props(props);
153
+ }
125
154
  const target = options.target;
126
155
  const was_hydrating = hydrating;
127
156
  const previous_hydrate_node = hydrate_node;
128
- let anchor = get_first_child(target);
157
+ let anchor = target.firstChild;
129
158
 
130
- const cleanup_events = handle_root_events(target);
159
+ /** @type {import('./internal/client/events.js').RootTargetRef | null} */
160
+ let events_ref = handle_root_events(target);
131
161
  let _root;
132
162
 
133
163
  try {
@@ -139,33 +169,43 @@ export function hydrate(component, options) {
139
169
  // marker instead of a plain `<!--[-->`
140
170
  !(/** @type {Comment} */ (anchor).data.startsWith(HYDRATION_START)))
141
171
  ) {
142
- anchor = get_next_sibling(anchor);
172
+ anchor = anchor.nextSibling;
143
173
  }
144
174
 
145
- set_hydrating(true);
146
- set_hydrate_node(/** @type {Comment} */ (anchor));
175
+ set_hydration(true, /** @type {Comment} */ (anchor));
176
+
177
+ const root_boundary = options.rootBoundary;
178
+ const marker = /** @type {Comment} */ (anchor);
147
179
 
148
180
  _root = root(() => {
181
+ if (root_boundary === false && marker.data === HYDRATION_START) {
182
+ // The root boundary's own hydration walk: consume the `<!--[-->`
183
+ // marker and render against it, as `try_block` does for the root.
184
+ hydrate_next();
185
+ render_component(component, marker, props);
186
+ return;
187
+ }
149
188
  render_root_boundary(
150
- /** @type {Comment} */ (anchor),
189
+ marker,
151
190
  (component_anchor) => {
152
191
  render_component(component, component_anchor, props);
153
192
  },
154
- options.rootBoundary,
193
+ root_boundary === false ? undefined : root_boundary,
155
194
  );
156
195
  });
157
196
  } catch (e) {
158
197
  throw e;
159
198
  } finally {
160
- set_hydrating(was_hydrating);
161
- set_hydrate_node(previous_hydrate_node, true);
199
+ set_hydration(was_hydrating, previous_hydrate_node);
162
200
  if (!was_hydrating) {
163
- clear_track_hash_reference();
201
+ track_hash_reference.clear();
164
202
  }
165
203
  }
166
204
 
167
205
  return () => {
168
- cleanup_events();
206
+ if (events_ref === null) return;
207
+ release_root_events(events_ref);
208
+ events_ref = null;
169
209
  destroy_block(_root);
170
210
  };
171
211
  }