ripple 0.3.128 → 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.
Files changed (192) hide show
  1. package/CHANGELOG.md +267 -0
  2. package/package.json +10 -4
  3. package/src/runtime/element.js +23 -6
  4. package/src/runtime/index-client.js +81 -41
  5. package/src/runtime/index-server.js +1 -0
  6. package/src/runtime/internal/client/blocks.js +173 -91
  7. package/src/runtime/internal/client/component.js +4 -30
  8. package/src/runtime/internal/client/composite.js +12 -13
  9. package/src/runtime/internal/client/constants.js +9 -0
  10. package/src/runtime/internal/client/context.js +12 -19
  11. package/src/runtime/internal/client/css.js +7 -9
  12. package/src/runtime/internal/client/events.js +178 -90
  13. package/src/runtime/internal/client/expression.js +45 -25
  14. package/src/runtime/internal/client/for.js +409 -129
  15. package/src/runtime/internal/client/head.js +10 -5
  16. package/src/runtime/internal/client/html.js +1 -0
  17. package/src/runtime/internal/client/hydration.js +152 -10
  18. package/src/runtime/internal/client/if.js +239 -50
  19. package/src/runtime/internal/client/index.js +4 -12
  20. package/src/runtime/internal/client/operations.js +40 -61
  21. package/src/runtime/internal/client/portal.js +163 -85
  22. package/src/runtime/internal/client/render.js +39 -39
  23. package/src/runtime/internal/client/rpc.js +3 -3
  24. package/src/runtime/internal/client/runtime.js +592 -433
  25. package/src/runtime/internal/client/selector.js +5 -2
  26. package/src/runtime/internal/client/template.js +133 -158
  27. package/src/runtime/internal/client/transport.js +38 -0
  28. package/src/runtime/internal/client/try.js +465 -426
  29. package/src/runtime/internal/client/types.d.ts +56 -23
  30. package/src/runtime/internal/client/utils.js +1 -3
  31. package/src/runtime/internal/server/dynamic.js +20 -21
  32. package/src/runtime/internal/server/index.js +325 -275
  33. package/src/runtime/internal/server/rpc.js +3 -2
  34. package/src/runtime/internal/server/transport.js +16 -0
  35. package/src/runtime/transport.js +21 -0
  36. package/tests/client/__snapshots__/for.test.tsrx.snap +0 -5
  37. package/tests/client/array/array.copy-within.test.tsrx +12 -12
  38. package/tests/client/array/array.derived.test.tsrx +46 -46
  39. package/tests/client/array/array.iteration.test.tsrx +10 -10
  40. package/tests/client/array/array.mutations.test.tsrx +20 -20
  41. package/tests/client/array/array.static.test.tsrx +11 -11
  42. package/tests/client/array/array.to-methods.test.tsrx +6 -6
  43. package/tests/client/async-suspend.test.tsrx +74 -75
  44. package/tests/client/basic/basic.attributes.test.tsrx +86 -83
  45. package/tests/client/basic/basic.collections.test.tsrx +12 -12
  46. package/tests/client/basic/basic.components.test.tsrx +99 -25
  47. package/tests/client/basic/basic.errors.test.tsrx +18 -18
  48. package/tests/client/basic/basic.events.test.tsrx +42 -42
  49. package/tests/client/basic/basic.get-set.test.tsrx +8 -8
  50. package/tests/client/basic/basic.reactivity.test.tsrx +73 -38
  51. package/tests/client/basic/basic.rendering.test.tsrx +25 -25
  52. package/tests/client/basic/basic.utilities.test.tsrx +3 -3
  53. package/tests/client/boundaries.test.tsrx +8 -8
  54. package/tests/client/boxed-locals.test.tsrx +452 -0
  55. package/tests/client/compiler/__snapshots__/compiler.assignments.test.tsrx.snap +6 -6
  56. package/tests/client/compiler/compiler.assignments.test.tsrx +9 -73
  57. package/tests/client/compiler/compiler.attributes.test.tsrx +23 -23
  58. package/tests/client/compiler/compiler.basic.test.tsrx +15 -60
  59. package/tests/client/compiler/compiler.tracked-access.test.tsrx +18 -43
  60. package/tests/client/compiler/compiler.try-in-function.test.tsrx +2 -2
  61. package/tests/client/composite/__snapshots__/composite.render.test.tsrx.snap +0 -1
  62. package/tests/client/composite/composite.dynamic-components.test.tsrx +77 -6
  63. package/tests/client/composite/composite.props.test.tsrx +149 -12
  64. package/tests/client/composite/composite.reactivity.test.tsrx +51 -52
  65. package/tests/client/composite/composite.render.test.tsrx +26 -9
  66. package/tests/client/css/scoped-styles.test.tsrx +8 -8
  67. package/tests/client/date.test.tsrx +42 -42
  68. package/tests/client/derived-release.test.tsrx +370 -0
  69. package/tests/client/dynamic-elements.test.tsrx +45 -45
  70. package/tests/client/events.test.tsrx +97 -71
  71. package/tests/client/for-selector.test.tsrx +14 -14
  72. package/tests/client/for-single-root-items.test.tsrx +118 -3
  73. package/tests/client/for.test.tsrx +332 -31
  74. package/tests/client/head.test.tsrx +19 -19
  75. package/tests/client/html.test.tsrx +11 -9
  76. package/tests/client/if-block.test.tsrx +80 -0
  77. package/tests/client/map.test.tsrx +16 -16
  78. package/tests/client/media-query.test.tsrx +7 -7
  79. package/tests/client/portal.test.tsrx +224 -26
  80. package/tests/client/primitive-text.test.tsrx +50 -0
  81. package/tests/client/prop-kind-switch.test.tsrx +141 -0
  82. package/tests/client/ref.test.tsrx +17 -17
  83. package/tests/client/return.test.tsrx +26 -3
  84. package/tests/client/root-boundary.test.tsrx +42 -0
  85. package/tests/client/scoped-flush.test.tsrx +8 -8
  86. package/tests/client/set.test.tsrx +6 -6
  87. package/tests/client/svg.test.tsrx +11 -11
  88. package/tests/client/switch.test.tsrx +44 -44
  89. package/tests/client/track-async-hydration.test.tsrx +4 -4
  90. package/tests/client/try.test.tsrx +151 -83
  91. package/tests/client/tsx.test.tsrx +43 -40
  92. package/tests/client/url/url.derived.test.tsrx +6 -6
  93. package/tests/client/url-search-params/url-search-params.derived.test.tsrx +8 -8
  94. package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +10 -10
  95. package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +10 -10
  96. package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +18 -18
  97. package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +2 -2
  98. package/tests/hydration/basic.test.js +74 -1
  99. package/tests/hydration/build-components.js +22 -1
  100. package/tests/hydration/compiled/client/basic.js +1142 -713
  101. package/tests/hydration/compiled/client/composite.js +151 -100
  102. package/tests/hydration/compiled/client/events.js +184 -171
  103. package/tests/hydration/compiled/client/for.js +1472 -1132
  104. package/tests/hydration/compiled/client/fragment-cursor.js +2341 -0
  105. package/tests/hydration/compiled/client/head.js +254 -201
  106. package/tests/hydration/compiled/client/hmr.js +54 -41
  107. package/tests/hydration/compiled/client/html-in-template.js +54 -39
  108. package/tests/hydration/compiled/client/html.js +1633 -1263
  109. package/tests/hydration/compiled/client/if-children.js +420 -270
  110. package/tests/hydration/compiled/client/if-fragment-controlflow.js +363 -317
  111. package/tests/hydration/compiled/client/if.js +229 -219
  112. package/tests/hydration/compiled/client/mixed-control-flow.js +420 -404
  113. package/tests/hydration/compiled/client/nested-control-flow.js +1326 -1277
  114. package/tests/hydration/compiled/client/portal.js +87 -95
  115. package/tests/hydration/compiled/client/reactivity.js +135 -101
  116. package/tests/hydration/compiled/client/return.js +28 -19
  117. package/tests/hydration/compiled/client/streaming.js +390 -306
  118. package/tests/hydration/compiled/client/switch.js +258 -266
  119. package/tests/hydration/compiled/client/track-async-serialization.js +448 -280
  120. package/tests/hydration/compiled/client/try.js +173 -129
  121. package/tests/hydration/compiled/client/typed-text.js +71 -0
  122. package/tests/hydration/compiled/fixtures/money.js +10 -0
  123. package/tests/hydration/compiled/server/basic.js +123 -6
  124. package/tests/hydration/compiled/server/composite.js +16 -30
  125. package/tests/hydration/compiled/server/events.js +66 -18
  126. package/tests/hydration/compiled/server/for.js +117 -41
  127. package/tests/hydration/compiled/server/fragment-cursor.js +1687 -0
  128. package/tests/hydration/compiled/server/head.js +20 -20
  129. package/tests/hydration/compiled/server/hmr.js +2 -2
  130. package/tests/hydration/compiled/server/html.js +70 -238
  131. package/tests/hydration/compiled/server/if-children.js +107 -15
  132. package/tests/hydration/compiled/server/if.js +11 -15
  133. package/tests/hydration/compiled/server/mixed-control-flow.js +8 -8
  134. package/tests/hydration/compiled/server/portal.js +14 -22
  135. package/tests/hydration/compiled/server/reactivity.js +14 -14
  136. package/tests/hydration/compiled/server/streaming.js +19 -19
  137. package/tests/hydration/compiled/server/switch.js +8 -8
  138. package/tests/hydration/compiled/server/track-async-serialization.js +150 -23
  139. package/tests/hydration/compiled/server/try.js +8 -8
  140. package/tests/hydration/compiled/server/typed-text.js +17 -0
  141. package/tests/hydration/components/basic.tsrx +44 -8
  142. package/tests/hydration/components/composite.tsrx +2 -2
  143. package/tests/hydration/components/events.tsrx +25 -25
  144. package/tests/hydration/components/for.tsrx +115 -67
  145. package/tests/hydration/components/fragment-cursor.tsrx +650 -0
  146. package/tests/hydration/components/head.tsrx +16 -16
  147. package/tests/hydration/components/hmr.tsrx +2 -2
  148. package/tests/hydration/components/html.tsrx +7 -7
  149. package/tests/hydration/components/if-children.tsrx +72 -25
  150. package/tests/hydration/components/if.tsrx +18 -18
  151. package/tests/hydration/components/mixed-control-flow.tsrx +11 -11
  152. package/tests/hydration/components/portal.tsrx +3 -3
  153. package/tests/hydration/components/reactivity.tsrx +16 -16
  154. package/tests/hydration/components/streaming.tsrx +20 -20
  155. package/tests/hydration/components/switch.tsrx +20 -20
  156. package/tests/hydration/components/text-types.ts +4 -0
  157. package/tests/hydration/components/track-async-serialization.tsrx +80 -26
  158. package/tests/hydration/components/try.tsrx +8 -8
  159. package/tests/hydration/components/typed-text.tsrx +22 -0
  160. package/tests/hydration/fixtures/money.js +10 -0
  161. package/tests/hydration/for.test.js +36 -0
  162. package/tests/hydration/fragment-cursor.test.js +111 -0
  163. package/tests/hydration/if-children.test.js +24 -0
  164. package/tests/hydration/streaming.test.js +19 -0
  165. package/tests/hydration/track-async-serialization.test.js +87 -2
  166. package/tests/hydration/tsconfig.text-types.json +10 -0
  167. package/tests/hydration/typed-text.test.js +34 -0
  168. package/tests/server/await.test.tsrx +3 -3
  169. package/tests/server/basic.attributes.test.tsrx +42 -42
  170. package/tests/server/basic.components.test.tsrx +64 -8
  171. package/tests/server/basic.test.tsrx +34 -32
  172. package/tests/server/compiler.test.tsrx +1 -70
  173. package/tests/server/composite.props.test.tsrx +6 -6
  174. package/tests/server/dynamic-elements.test.tsrx +31 -8
  175. package/tests/server/for.test.tsrx +94 -0
  176. package/tests/server/head.test.tsrx +11 -11
  177. package/tests/server/scoped-styles.test.tsrx +2 -2
  178. package/tests/server/streaming-ssr.test.tsrx +48 -43
  179. package/tests/server/track-async-serialization.test.tsrx +204 -21
  180. package/tests/server/try.test.tsrx +48 -48
  181. package/tests/types/transport.ts +40 -0
  182. package/tests/utils/vite-plugin-config.test.js +23 -0
  183. package/types/index.d.ts +48 -7
  184. package/types/server.d.ts +3 -0
  185. package/types/transport.d.ts +15 -0
  186. package/src/runtime/internal/client/switch.js +0 -113
  187. package/src/utils/errors.js +0 -13
  188. package/tests/client/lazy-array.test.tsrx +0 -31
  189. package/tests/client/lazy-destructuring.test.tsrx +0 -438
  190. package/tests/client/tracked-index-access.test.tsrx +0 -113
  191. package/tests/server/lazy-destructuring.test.tsrx +0 -595
  192. package/tests/server/tracked-index-access.test.tsrx +0 -76
@@ -1,8 +1,14 @@
1
1
  import { render } from './blocks.js';
2
- import { HEAD_BLOCK } from './constants.js';
2
+ import { DETACHED_BLOCK, HEAD_BLOCK } from './constants.js';
3
3
  import { COMMENT_NODE } from '../../../constants.js';
4
4
  import { create_text, get_first_child, get_next_sibling } from './operations.js';
5
- import { hydrate_node, hydrating, set_hydrate_node, set_hydrating } from './hydration.js';
5
+ import {
6
+ hydrate_node,
7
+ hydrating,
8
+ set_hydrate_node,
9
+ set_hydrating,
10
+ set_hydration,
11
+ } from './hydration.js';
6
12
 
7
13
  /**
8
14
  * @param {string} hash
@@ -49,11 +55,10 @@ export function head(hash, render_fn) {
49
55
  }
50
56
 
51
57
  try {
52
- render(() => render_fn(anchor), null, HEAD_BLOCK);
58
+ render(() => render_fn(anchor), null, HEAD_BLOCK | DETACHED_BLOCK);
53
59
  } finally {
54
60
  if (was_hydrating) {
55
- set_hydrating(true);
56
- set_hydrate_node(previous_hydrate_node);
61
+ set_hydration(true, previous_hydrate_node);
57
62
  }
58
63
  }
59
64
  }
@@ -28,6 +28,7 @@ export function html(node, get_html, svg = false, mathml = false) {
28
28
  if (hydrating) {
29
29
  // The anchor is the hash comment - we need to skip past it and its content
30
30
  set_hydrate_node(anchor);
31
+ /** @type {Node | null} */
31
32
  var next = hydrate_next();
32
33
 
33
34
  // Walk until we find the empty comment end marker
@@ -1,12 +1,14 @@
1
- /** @import { Derived, Tracked } from '#client' */
1
+ /** @import { AppendIntoAnchor, Block, Derived, Tracked } from '#client' */
2
2
 
3
3
  import {
4
4
  COMMENT_NODE,
5
5
  HYDRATION_END,
6
6
  HYDRATION_ERROR,
7
7
  HYDRATION_START,
8
+ TEXT_NODE,
8
9
  } from '../../../constants.js';
9
- import { get_next_sibling } from './operations.js';
10
+ import { create_text, first_child_getter, next_sibling_getter } from './operations.js';
11
+ import { active_block } from './runtime.js';
10
12
 
11
13
  export let hydrating = false;
12
14
 
@@ -21,15 +23,22 @@ export let hydrate_node = null;
21
23
  */
22
24
  export const track_hash_reference = new Map();
23
25
 
24
- export function clear_track_hash_reference() {
25
- track_hash_reference.clear();
26
+ /**
27
+ * @param {boolean} value
28
+ */
29
+ export function set_hydrating(value) {
30
+ hydrating = value;
26
31
  }
27
32
 
28
33
  /**
34
+ * Enters or leaves a hydration walk: both the flag and the cursor, which is
35
+ * allowed to be null when restoring the state of a client-only mount.
29
36
  * @param {boolean} value
37
+ * @param {Node | null} node
30
38
  */
31
- export function set_hydrating(value) {
39
+ export function set_hydration(value, node) {
32
40
  hydrating = value;
41
+ hydrate_node = node;
33
42
  }
34
43
 
35
44
  /**
@@ -44,11 +53,144 @@ export function set_hydrate_node(node, mounting = false) {
44
53
  }
45
54
 
46
55
  export function hydrate_next() {
47
- return set_hydrate_node(get_next_sibling(/** @type {Node} */ (hydrate_node)));
56
+ var node = next_sibling_getter.call(/** @type {Node} */ (hydrate_node));
57
+ if (node === null) {
58
+ throw HYDRATION_ERROR;
59
+ }
60
+ return (hydrate_node = node);
61
+ }
62
+
63
+ /**
64
+ * Descends the cursor into the current node's first child, the way a
65
+ * compiled `element.firstChild` read does while mounting.
66
+ * @param {boolean} [is_text]
67
+ * @returns {Node}
68
+ */
69
+ export function hydrate_first_child(is_text) {
70
+ var child = first_child_getter.call(/** @type {Node} */ (hydrate_node));
71
+
72
+ if (child === null || (is_text === true && child.nodeType !== TEXT_NODE)) {
73
+ child = repair_first_child(child);
74
+ }
75
+
76
+ return (hydrate_node = child);
77
+ }
78
+
79
+ /**
80
+ * Adopts the text node that is an element's only child, leaving the cursor on
81
+ * the element: nothing else inside it navigates, so the parent's sibling
82
+ * traversal continues from the element with no pop().
83
+ * @returns {Node}
84
+ */
85
+ export function hydrate_text_child() {
86
+ var child = first_child_getter.call(/** @type {Node} */ (hydrate_node));
87
+
88
+ if (child === null || child.nodeType !== TEXT_NODE) {
89
+ child = repair_first_child(child);
90
+ }
91
+
92
+ return child;
48
93
  }
49
94
 
50
- export function hydrate_advance() {
51
- hydrate_node = get_next_sibling(/** @type {Node} */ (hydrate_node));
95
+ /**
96
+ * Handles `<p>{text}</p>` where `text` rendered empty on the server: there is
97
+ * no text node to adopt (or, with siblings, the next node is not one), so one
98
+ * is created in its place.
99
+ * @param {Node | null} child
100
+ * @returns {Text}
101
+ */
102
+ function repair_first_child(child) {
103
+ var text = create_text();
104
+ if (child === null) {
105
+ /** @type {Node} */ (hydrate_node).appendChild(text);
106
+ } else {
107
+ /** @type {ChildNode} */ (child).before(text);
108
+ }
109
+ return text;
110
+ }
111
+
112
+ /**
113
+ * Moves the cursor to the current node's next sibling, the way a compiled
114
+ * `node.nextSibling` read does while mounting.
115
+ * @param {boolean} [is_text]
116
+ * @returns {Node}
117
+ */
118
+ export function hydrate_next_sibling(is_text) {
119
+ var next_sibling = /** @type {ChildNode | null} */ (
120
+ next_sibling_getter.call(/** @type {Node} */ (hydrate_node))
121
+ );
122
+
123
+ if (next_sibling === null || (is_text === true && next_sibling.nodeType !== TEXT_NODE)) {
124
+ if (is_text !== true) {
125
+ throw HYDRATION_ERROR;
126
+ }
127
+ next_sibling = repair_next_sibling(next_sibling);
128
+ }
129
+
130
+ return (hydrate_node = next_sibling);
131
+ }
132
+
133
+ /**
134
+ * A sibling `{expression}` that rendered empty on the server has no text node
135
+ * to hydrate: create one after the current node (or before whatever follows).
136
+ * @param {ChildNode | null} next_sibling
137
+ * @returns {Text}
138
+ */
139
+ function repair_next_sibling(next_sibling) {
140
+ var text = create_text();
141
+ if (next_sibling === null) {
142
+ /** @type {ChildNode} */ (hydrate_node).after(text);
143
+ } else {
144
+ next_sibling.before(text);
145
+ }
146
+ return text;
147
+ }
148
+
149
+ /**
150
+ * The hydration path of `append`: repositions the hydration cursor instead
151
+ * of inserting. Kept out of the insert path so a client-only mount never
152
+ * compiles it.
153
+ *
154
+ * Every hydrated node, block, and component leaves the cursor on its last
155
+ * DOM node, and whoever owns the next node steps past it: a parent element
156
+ * with its sibling traversal, a control-flow block by reaching its end
157
+ * marker, an append-into sentinel by adopting the cursor as the next
158
+ * component's first node.
159
+ * @param {ChildNode | AppendIntoAnchor} anchor
160
+ * @param {Node} dom
161
+ */
162
+ export function hydrate_append(anchor, dom) {
163
+ var node = /** @type {Node} */ (hydrate_node);
164
+
165
+ // The cursor descended into dom's children (child()/sibling() traversal
166
+ // inside a single-node template) without a compiler-emitted pop(): bring it
167
+ // back up to dom before deciding where to leave it.
168
+ if (node !== dom && dom.contains(node)) {
169
+ node = dom;
170
+ }
171
+
172
+ // A child component renders into the anchor it was handed, which is its
173
+ // own first node. Its content is hydrated, so leave the cursor on the
174
+ // content's last node for the parent's sibling traversal.
175
+ if (anchor === dom) {
176
+ hydrate_node = node;
177
+ return;
178
+ }
179
+
180
+ if (node !== dom) {
181
+ // A fragment's cursor sits on its last top-level node, past the
182
+ // template's first node: widen the block's end to cover the whole
183
+ // fragment rather than only the node assign_nodes saw.
184
+ var s = /** @type {Block} */ (active_block).s;
185
+ if (s !== null) {
186
+ s.end = node;
187
+ }
188
+ }
189
+
190
+ // Step past the content: a branch lands on its block's end marker, the
191
+ // root on the boundary's end marker, an append-into sentinel on the next
192
+ // component's first node.
193
+ hydrate_node = next_sibling_getter.call(node);
52
194
  }
53
195
 
54
196
  export function next(n = 1) {
@@ -56,7 +198,7 @@ export function next(n = 1) {
56
198
  var node = hydrate_node;
57
199
 
58
200
  for (var i = 0; i < n; i++) {
59
- node = get_next_sibling(/** @type {Node} */ (node));
201
+ node = next_sibling_getter.call(/** @type {Node} */ (node));
60
202
  }
61
203
 
62
204
  hydrate_node = node;
@@ -91,6 +233,6 @@ export function skip_to_hydration_end() {
91
233
  depth += 1;
92
234
  }
93
235
  }
94
- node = /** @type {Node} */ (get_next_sibling(node));
236
+ node = /** @type {Node} */ (next_sibling_getter.call(node));
95
237
  }
96
238
  }
@@ -1,71 +1,260 @@
1
- /** @import { Block } from '#client' */
1
+ /** @import { AppendIntoAnchor, Block } from '#client' */
2
2
 
3
- import { branch, destroy_block, render } from './blocks.js';
4
- import { IF_BLOCK, UNINITIALIZED } from './constants.js';
3
+ import {
4
+ block,
5
+ branch,
6
+ destroy_block,
7
+ get_first_node,
8
+ get_last_node,
9
+ move_block_last,
10
+ remove_block_dom,
11
+ } from './blocks.js';
12
+ import { DETACHED_BLOCK, IF_BLOCK, RENDER_BLOCK, UNINITIALIZED } from './constants.js';
5
13
  import { hydrate_next, hydrate_node, hydrating } from './hydration.js';
14
+ import { create_text, resolve_anchor } from './operations.js';
15
+ import { active_block, probe_if, run_untracked } from './runtime.js';
6
16
  import { append } from './template.js';
7
17
 
8
18
  /**
9
- * @param {Node} node
10
- * @param {(set_branch: (fn: (anchor: Node) => void, flag?: boolean) => void) => void} fn
19
+ * The if block renders its branch directly: the block owns the branch's DOM
20
+ * range and its children are the branch's blocks, so no branch block sits in
21
+ * between. Its condition runs tracked and returns the branch function to
22
+ * render (or nothing); the branch renders untracked, like a branch block
23
+ * would.
24
+ * @typedef {{
25
+ * start: Node | null;
26
+ * end: Node | null;
27
+ * a: Node | AppendIntoAnchor;
28
+ * fn: (x: any) => Branch | undefined;
29
+ * x: any;
30
+ * b: Branch | undefined | typeof UNINITIALIZED;
31
+ * o: Block | null;
32
+ * }} IfState
33
+ * @typedef {(anchor: Node, x: any) => void} Branch
34
+ */
35
+
36
+ function noop() {}
37
+
38
+ /**
39
+ * The last DOM node rendered by the current branch through a child block,
40
+ * for a branch that has no range of its own (its root is control flow).
41
+ * @param {Block} block
42
+ * @param {Block | null} skip
43
+ * @returns {Node | null}
44
+ */
45
+ function last_child_node(block, skip) {
46
+ var child = block.last;
47
+ while (child !== null) {
48
+ if (child !== skip) {
49
+ var node = get_last_node(child);
50
+ if (node !== null) {
51
+ return node;
52
+ }
53
+ }
54
+ child = child.prev;
55
+ }
56
+ return null;
57
+ }
58
+
59
+ /**
60
+ * @param {Block} block
61
+ * @param {Block | null} skip
62
+ * @returns {Node | null}
63
+ */
64
+ function first_child_node(block, skip) {
65
+ var child = block.first;
66
+ while (child !== null) {
67
+ if (child !== skip) {
68
+ var node = get_first_node(child);
69
+ if (node !== null) {
70
+ return node;
71
+ }
72
+ }
73
+ child = child.next;
74
+ }
75
+ return null;
76
+ }
77
+
78
+ /**
79
+ * Destroys the current branch: its DOM range, then its blocks (all children
80
+ * of the if block except the anchor's owner). `remove_dom` stays on for the
81
+ * children when the branch renders through them and has no range itself.
82
+ * @param {IfState} state
83
+ * @param {Block} block
84
+ */
85
+ function destroy_branch(state, block) {
86
+ var start = state.start;
87
+ var remove_dom = start === null;
88
+
89
+ if (!remove_dom) {
90
+ remove_block_dom(start, /** @type {Node} */ (state.end));
91
+ state.start = state.end = null;
92
+ }
93
+
94
+ var keep = state.o;
95
+ var child = block.first;
96
+ while (child !== null) {
97
+ var next = child.next;
98
+ if (child !== keep) {
99
+ destroy_block(child, remove_dom);
100
+ }
101
+ child = next;
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Turns the append-into sentinel of a root-controlled if into a text anchor
107
+ * the if owns. The sentinel is only right for the first append, so the anchor
108
+ * is materialized as late as possible: when a branch swap needs the position
109
+ * of the branch being replaced (still rendered), or when the if renders
110
+ * nothing and would otherwise have no position at all. Until then a list of
111
+ * `@if` items keeps no anchor nodes in the DOM.
112
+ * @param {IfState} state
113
+ * @param {Block} block
114
+ */
115
+ function materialize_anchor(state, block) {
116
+ var sentinel = /** @type {AppendIntoAnchor} */ (state.a);
117
+ var parent = sentinel.parent;
118
+ var text = create_text();
119
+ var last = state.start !== null ? state.end : last_child_node(block, null);
120
+
121
+ if (last === null || last.parentNode !== parent) {
122
+ parent.appendChild(text);
123
+ } else {
124
+ /** @type {ChildNode} */ (last).after(text);
125
+ }
126
+
127
+ state.a = text;
128
+ // Created after the current branch, so block order follows DOM order;
129
+ // `update_branch` relinks it behind every later branch. The anchor sits
130
+ // outside the if's own range (it is the if's last node, see
131
+ // `get_last_node`), so the owner is detached: it removes the anchor even
132
+ // when the if has already removed its range.
133
+ state.o = branch(noop, DETACHED_BLOCK, { start: text, end: text });
134
+ }
135
+
136
+ /**
137
+ * Renders `fn` as the if's branch when it is not the current one. The
138
+ * condition returns the branch function itself, so a branch is identified by
139
+ * that function and no flag is needed.
140
+ * @param {IfState} state
141
+ * @param {Branch | undefined} fn
142
+ */
143
+ function update_branch(state, fn) {
144
+ var previous = state.b;
145
+ if (previous === fn) return;
146
+ state.b = fn;
147
+
148
+ var block = /** @type {Block} */ (active_block);
149
+
150
+ if (previous !== UNINITIALIZED) {
151
+ if (/** @type {AppendIntoAnchor} */ (state.a).into === true) {
152
+ materialize_anchor(state, block);
153
+ }
154
+ destroy_branch(state, block);
155
+ }
156
+
157
+ var o = state.o;
158
+
159
+ if (fn !== undefined) {
160
+ run_untracked(fn, /** @type {Node} */ (state.a), state.x);
161
+
162
+ if (o !== null) {
163
+ move_block_last(o);
164
+ } else if (
165
+ /** @type {AppendIntoAnchor} */ (state.a).into === true &&
166
+ state.start === null &&
167
+ first_child_node(block, null) === null
168
+ ) {
169
+ materialize_anchor(state, block);
170
+ }
171
+ } else if (/** @type {AppendIntoAnchor} */ (state.a).into === true) {
172
+ materialize_anchor(state, block);
173
+ }
174
+ }
175
+
176
+ /**
177
+ * @param {IfState} state
178
+ */
179
+ function run_if(state) {
180
+ update_branch(state, state.fn(state.x));
181
+ }
182
+
183
+ /**
184
+ * State lives on the block instead of per-if closures.
185
+ * @param {Node | AppendIntoAnchor} anchor
186
+ * @param {IfState['fn']} fn
187
+ * @param {any} x
188
+ * @returns {IfState}
189
+ */
190
+ function if_block_state(anchor, fn, x) {
191
+ return {
192
+ // DOM range of the current branch
193
+ start: null,
194
+ end: null,
195
+ a: anchor,
196
+ fn,
197
+ // the captured local a hoisted condition and its branches receive
198
+ x,
199
+ // the current branch
200
+ b: UNINITIALIZED,
201
+ // block owning the anchor materialized from a sentinel
202
+ o: null,
203
+ };
204
+ }
205
+
206
+ /**
207
+ * @param {Node | AppendIntoAnchor} node
208
+ * @param {IfState['fn']} fn
11
209
  * @param {boolean} [root_controlled] When true the block renders directly before
12
- * the component's `__anchor` (no synthesized `<!>` wrapper). During hydration
210
+ * the component's `__anchor` (no synthesized `<!>` wrapper), which may be an
211
+ * append-into sentinel: branches then append into the parent until the if
212
+ * needs a position of its own (see `materialize_anchor`). During hydration
13
213
  * the SSR boundary start marker sits at the cursor; we hand it to `append()`
14
214
  * afterwards so it performs the same context-aware boundary advance the
15
215
  * eliminated wrapper's `append()` used to do.
216
+ * @param {any} [x] The one local a hoisted condition and its branches capture,
217
+ * passed to them as their second argument (see the compiler's if lowering).
16
218
  * @returns {void}
17
219
  */
18
- export function if_block(node, fn, root_controlled) {
220
+ export function if_block(node, fn, root_controlled, x) {
19
221
  /** @type {Node | undefined} */
20
222
  var boundary;
21
-
22
- if (hydrating) {
23
- if (root_controlled) {
24
- boundary = /** @type {Node} */ (hydrate_node);
25
- }
26
- hydrate_next();
27
- }
28
-
29
223
  var anchor = node;
30
- var has_branch = false;
31
- /** @type {any} */
32
- var condition = UNINITIALIZED;
33
- /** @type {Block | null} */
34
- var b = null;
35
-
36
- /** @type {(fn: (anchor: Node) => void, flag?: boolean) => void} */
37
- var set_branch = (fn, flag = true) => {
38
- has_branch = true;
39
- update_branch(flag, fn);
40
- };
41
-
42
- /** @type {(new_condition: any, fn: ((anchor: Node) => void) | null) => void} */
43
- var update_branch = (new_condition, fn) => {
44
- if (condition === (condition = new_condition)) return;
45
224
 
46
- if (b !== null) {
47
- destroy_block(b);
48
- b = null;
225
+ if (!hydrating) {
226
+ // Evaluate the condition before deciding whether the if needs a block:
227
+ // a condition that read no tracked state has its branch rendered by the
228
+ // probe itself, directly under the current block, with no if block, no
229
+ // state, and the branch's DOM and blocks belonging to the enclosing
230
+ // block like any other content. A dynamic condition gets its block,
231
+ // whose first run evaluates it again and subscribes it.
232
+ var rendered;
233
+ try {
234
+ rendered = probe_if(fn, x, node);
235
+ } catch {
236
+ // A condition that throws (a pending async read) is the block's to
237
+ // handle: create it and let its first run evaluate the condition.
238
+ rendered = false;
49
239
  }
50
-
51
- if (fn !== null) {
52
- b = branch(() => fn(anchor));
240
+ if (!rendered) {
241
+ block(RENDER_BLOCK | IF_BLOCK, run_if, if_block_state(anchor, fn, x));
53
242
  }
54
- };
243
+ return;
244
+ }
55
245
 
56
- render(
57
- () => {
58
- has_branch = false;
59
- fn(set_branch);
60
- if (!has_branch) {
61
- update_branch(null, null);
62
- }
63
- },
64
- null,
65
- IF_BLOCK,
66
- );
246
+ if (root_controlled) {
247
+ // A sentinel resolves to the cursor, the block's SSR boundary marker.
248
+ anchor = resolve_anchor(node);
249
+ boundary = /** @type {Node} */ (hydrate_node);
250
+ }
251
+ hydrate_next();
252
+
253
+ block(RENDER_BLOCK | IF_BLOCK, run_if, if_block_state(anchor, fn, x));
67
254
 
68
- if (hydrating && root_controlled) {
69
- append(/** @type {ChildNode} */ (anchor), /** @type {Node} */ (boundary));
255
+ if (root_controlled) {
256
+ // The original `node`: for a sentinel, `hydrate_append` performs the
257
+ // cursor advance that stands in for the eliminated sibling navigation.
258
+ append(/** @type {ChildNode} */ (node), /** @type {Node} */ (boundary));
70
259
  }
71
260
  }
@@ -4,6 +4,7 @@ export {
4
4
  next_sibling as sibling,
5
5
  hydrate_first_child as hydrate_child,
6
6
  hydrate_next_sibling as hydrate_sibling,
7
+ hydrate_text_child as hydrate_text,
7
8
  append_into,
8
9
  document,
9
10
  create_text,
@@ -38,8 +39,10 @@ export {
38
39
  TRACKED_UPDATED,
39
40
  SUSPENSE_PENDING,
40
41
  SUSPENSE_REJECTED,
42
+ RENDER_ENTRY as $r,
41
43
  } from './constants.js';
42
44
  export { event, render_event, delegate } from './events.js';
45
+ export { portal } from './portal.js';
43
46
 
44
47
  export {
45
48
  active_block,
@@ -49,14 +52,8 @@ export {
49
52
  get,
50
53
  get_tracked,
51
54
  get_derived,
52
- lazy_array_get,
53
- lazy_array_rest,
54
- lazy_array_set,
55
- lazy_array_update,
56
- lazy_array_update_pre,
57
55
  set,
58
56
  tracked,
59
- spread_props,
60
57
  computed_property,
61
58
  call_property,
62
59
  get_property,
@@ -74,11 +71,8 @@ export {
74
71
  untrack,
75
72
  ref_prop,
76
73
  create_ref_prop,
77
- fallback,
78
- exclude_from_object,
79
74
  derived,
80
75
  tick,
81
- proxy_props,
82
76
  with_block,
83
77
  with_ns,
84
78
  handle_error,
@@ -98,12 +92,10 @@ export { if_block as if } from './if.js';
98
92
 
99
93
  export { try_block as try, get_pending_boundary } from './try.js';
100
94
 
101
- export { switch_block as switch } from './switch.js';
95
+ export { if_block as switch } from './if.js';
102
96
 
103
97
  export { template, append, text } from './template.js';
104
98
 
105
- export { array_slice } from '@tsrx/core/runtime/language-helpers';
106
-
107
99
  export { ripple_array } from '../../array.js';
108
100
 
109
101
  export { ripple_object } from '../../object.js';