ripple 0.4.2 → 0.4.3

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 (94) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/package.json +9 -3
  3. package/src/jsx-runtime.d.ts +2 -1
  4. package/src/runtime/array.js +15 -9
  5. package/src/runtime/index-client.js +25 -7
  6. package/src/runtime/index-server.js +1 -0
  7. package/src/runtime/internal/client/attributes.js +313 -0
  8. package/src/runtime/internal/client/blocks.js +4 -14
  9. package/src/runtime/internal/client/component.js +2 -5
  10. package/src/runtime/internal/client/composite.js +14 -6
  11. package/src/runtime/internal/client/constants.js +6 -6
  12. package/src/runtime/internal/client/context.js +3 -2
  13. package/src/runtime/internal/client/css.js +11 -26
  14. package/src/runtime/internal/client/errors.js +188 -0
  15. package/src/runtime/internal/client/events.js +79 -34
  16. package/src/runtime/internal/client/expression.js +21 -116
  17. package/src/runtime/internal/client/for.js +108 -232
  18. package/src/runtime/internal/client/head.js +4 -3
  19. package/src/runtime/internal/client/html.js +5 -3
  20. package/src/runtime/internal/client/hydrate.js +484 -0
  21. package/src/runtime/internal/client/hydration-enabled.js +7 -0
  22. package/src/runtime/internal/client/hydration.js +16 -81
  23. package/src/runtime/internal/client/if.js +13 -9
  24. package/src/runtime/internal/client/index.js +14 -6
  25. package/src/runtime/internal/client/operations.js +11 -7
  26. package/src/runtime/internal/client/portal.js +3 -2
  27. package/src/runtime/internal/client/render.js +42 -325
  28. package/src/runtime/internal/client/root-boundary-enabled.js +7 -0
  29. package/src/runtime/internal/client/runtime.js +87 -363
  30. package/src/runtime/internal/client/template-ns.js +47 -0
  31. package/src/runtime/internal/client/template.js +63 -140
  32. package/src/runtime/internal/client/track-async.js +356 -0
  33. package/src/runtime/internal/client/try.js +133 -319
  34. package/src/runtime/internal/client/types.d.ts +48 -24
  35. package/src/runtime/internal/server/index.js +31 -29
  36. package/src/runtime/internal/server/prerender.js +27 -0
  37. package/src/server/index.js +1 -0
  38. package/src/utils/class-name.js +28 -0
  39. package/tests/client/basic/basic.errors.test.tsrx +3 -3
  40. package/tests/client/compiler/__snapshots__/compiler.typescript.test.tsrx.snap +0 -2
  41. package/tests/client/compiler/compiler.basic.test.tsrx +2 -2
  42. package/tests/client/compiler/compiler.tracked-access.test.tsrx +3 -3
  43. package/tests/client/composite/composite.props.test.tsrx +10 -10
  44. package/tests/client/derived-release.test.tsrx +3 -3
  45. package/tests/client/for-item.test.tsrx +39 -0
  46. package/tests/client/for-selector.test.tsrx +95 -0
  47. package/tests/hydration/compiled/client/basic.js +87 -87
  48. package/tests/hydration/compiled/client/composite.js +5 -5
  49. package/tests/hydration/compiled/client/events.js +40 -48
  50. package/tests/hydration/compiled/client/for.js +162 -181
  51. package/tests/hydration/compiled/client/fragment-cursor.js +521 -289
  52. package/tests/hydration/compiled/client/head.js +22 -22
  53. package/tests/hydration/compiled/client/hmr.js +4 -4
  54. package/tests/hydration/compiled/client/html-in-template.js +5 -5
  55. package/tests/hydration/compiled/client/html.js +152 -162
  56. package/tests/hydration/compiled/client/if-children.js +58 -53
  57. package/tests/hydration/compiled/client/if-fragment-controlflow.js +39 -39
  58. package/tests/hydration/compiled/client/if.js +26 -26
  59. package/tests/hydration/compiled/client/mixed-control-flow.js +56 -68
  60. package/tests/hydration/compiled/client/nested-control-flow.js +139 -271
  61. package/tests/hydration/compiled/client/portal.js +10 -10
  62. package/tests/hydration/compiled/client/reactivity.js +29 -29
  63. package/tests/hydration/compiled/client/return.js +2 -2
  64. package/tests/hydration/compiled/client/streaming.js +33 -35
  65. package/tests/hydration/compiled/client/switch.js +25 -25
  66. package/tests/hydration/compiled/client/track-async-serialization.js +41 -41
  67. package/tests/hydration/compiled/client/try.js +15 -15
  68. package/tests/hydration/compiled/client/typed-text.js +7 -7
  69. package/tests/hydration/compiled/server/basic.js +6 -6
  70. package/tests/hydration/compiled/server/events.js +8 -8
  71. package/tests/hydration/compiled/server/for.js +22 -22
  72. package/tests/hydration/compiled/server/fragment-cursor.js +188 -52
  73. package/tests/hydration/compiled/server/head.js +6 -6
  74. package/tests/hydration/compiled/server/hmr.js +1 -1
  75. package/tests/hydration/compiled/server/html.js +1 -1
  76. package/tests/hydration/compiled/server/if-children.js +9 -9
  77. package/tests/hydration/compiled/server/if.js +5 -5
  78. package/tests/hydration/compiled/server/mixed-control-flow.js +4 -4
  79. package/tests/hydration/compiled/server/portal.js +1 -1
  80. package/tests/hydration/compiled/server/reactivity.js +9 -9
  81. package/tests/hydration/compiled/server/streaming.js +9 -9
  82. package/tests/hydration/compiled/server/switch.js +4 -4
  83. package/tests/hydration/compiled/server/track-async-serialization.js +16 -16
  84. package/tests/hydration/compiled/server/try.js +4 -4
  85. package/tests/hydration/compiled/server/typed-text.js +1 -1
  86. package/tests/hydration/components/fragment-cursor.tsrx +60 -0
  87. package/tests/hydration/components/track-async-serialization.tsrx +2 -2
  88. package/tests/hydration/fragment-cursor.test.js +34 -1
  89. package/tests/hydration/track-async-serialization.test.js +1 -1
  90. package/tests/server/basic.components.test.tsrx +3 -3
  91. package/tests/server/track-async-serialization.test.tsrx +6 -6
  92. package/tests/utils/tracked-types.test.js +3 -3
  93. package/types/index.d.ts +8 -8
  94. package/types/server.d.ts +17 -0
@@ -14,6 +14,7 @@ import { hydrate_next, hydrate_node, hydrating } from './hydration.js';
14
14
  import { create_text, resolve_anchor } from './operations.js';
15
15
  import { active_block, probe_if, run_untracked } from './runtime.js';
16
16
  import { append } from './template.js';
17
+ import { HYDRATION } from 'ripple/internal/client/hydration-enabled';
17
18
 
18
19
  /**
19
20
  * The if block renders its branch directly: the block owns the branch's DOM
@@ -108,7 +109,9 @@ function destroy_branch(state, block) {
108
109
  * is materialized as late as possible: when a branch swap needs the position
109
110
  * of the branch being replaced (still rendered), or when the if renders
110
111
  * 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
+ * `@if` items keeps no anchor nodes in the DOM. A tail sentinel (the if is
113
+ * the last thing rendered into its parent) is never materialized: appending
114
+ * to the parent stays right for the if's whole life.
112
115
  * @param {IfState} state
113
116
  * @param {Block} block
114
117
  */
@@ -147,9 +150,14 @@ function update_branch(state, fn) {
147
150
 
148
151
  var block = /** @type {Block} */ (active_block);
149
152
 
153
+ var anchor = /** @type {AppendIntoAnchor} */ (state.a);
154
+ // A sentinel the if may still materialize (a tail sentinel never is).
155
+ var sentinel = anchor.into === true && anchor.tail !== true;
156
+
150
157
  if (previous !== UNINITIALIZED) {
151
- if (/** @type {AppendIntoAnchor} */ (state.a).into === true) {
158
+ if (sentinel) {
152
159
  materialize_anchor(state, block);
160
+ sentinel = false;
153
161
  }
154
162
  destroy_branch(state, block);
155
163
  }
@@ -161,14 +169,10 @@ function update_branch(state, fn) {
161
169
 
162
170
  if (o !== null) {
163
171
  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
- ) {
172
+ } else if (sentinel && state.start === null && first_child_node(block, null) === null) {
169
173
  materialize_anchor(state, block);
170
174
  }
171
- } else if (/** @type {AppendIntoAnchor} */ (state.a).into === true) {
175
+ } else if (sentinel) {
172
176
  materialize_anchor(state, block);
173
177
  }
174
178
  }
@@ -222,7 +226,7 @@ export function if_block(node, fn, root_controlled, x) {
222
226
  var boundary;
223
227
  var anchor = node;
224
228
 
225
- if (!hydrating) {
229
+ if (!(HYDRATION && hydrating)) {
226
230
  // Evaluate the condition before deciding whether the if needs a block:
227
231
  // a condition that read no tracked state has its branch rendered by the
228
232
  // probe itself, directly under the current block, with no if block, no
@@ -14,8 +14,7 @@ export {
14
14
  set_text,
15
15
  set_text_content,
16
16
  set_class,
17
- set_style,
18
- set_attribute,
17
+ set_class_value,
19
18
  set_value,
20
19
  set_checked,
21
20
  set_selected,
@@ -23,7 +22,6 @@ export {
23
22
 
24
23
  export {
25
24
  render,
26
- render_spread,
27
25
  ref,
28
26
  branch,
29
27
  destroy_block,
@@ -41,7 +39,8 @@ export {
41
39
  SUSPENSE_REJECTED,
42
40
  RENDER_ENTRY as $r,
43
41
  } from './constants.js';
44
- export { event, render_event, delegate } from './events.js';
42
+ export { event, render_event, delegate, listen } from './events.js';
43
+ export { set_style, set_attribute, render_spread } from './attributes.js';
45
44
  export { portal } from './portal.js';
46
45
 
47
46
  export {
@@ -63,7 +62,7 @@ export {
63
62
  update_property,
64
63
  update_pre_property,
65
64
  track,
66
- track_async,
65
+ track_read_only,
67
66
  is_tracked_pending,
68
67
  peek_tracked,
69
68
  push_component,
@@ -80,6 +79,8 @@ export {
80
79
  schedule_update,
81
80
  } from './runtime.js';
82
81
 
82
+ export { track_async } from './track-async.js';
83
+
83
84
  export { composite } from './composite.js';
84
85
 
85
86
  export { render_component } from './component.js';
@@ -96,7 +97,14 @@ export { if_block as switch } from './if.js';
96
97
 
97
98
  export { template, append, text } from './template.js';
98
99
 
99
- export { ripple_array } from '../../array.js';
100
+ export { template_ns } from './template-ns.js';
101
+
102
+ export {
103
+ ripple_array,
104
+ ripple_array_from,
105
+ ripple_array_of,
106
+ ripple_array_from_async,
107
+ } from '../../array.js';
100
108
 
101
109
  export { ripple_object } from '../../object.js';
102
110
 
@@ -8,6 +8,7 @@ import {
8
8
  set_hydrate_node,
9
9
  } from './hydration.js';
10
10
  import { get_descriptor } from '@tsrx/core/runtime/language-helpers';
11
+ import { HYDRATION } from 'ripple/internal/client/hydration-enabled';
11
12
 
12
13
  export { hydrate_first_child, hydrate_next_sibling };
13
14
 
@@ -76,7 +77,7 @@ export function get_last_child(node) {
76
77
  * @returns {Node | null}
77
78
  */
78
79
  export function first_child(node, is_text) {
79
- if (!hydrating) {
80
+ if (!(HYDRATION && hydrating)) {
80
81
  return node.firstChild;
81
82
  }
82
83
  return hydrate_first_child(is_text);
@@ -89,10 +90,13 @@ export function first_child(node, is_text) {
89
90
  * hydration we descend the cursor into `parent` (mirroring first_child) so the
90
91
  * first appended component adopts the server's first child of `parent`.
91
92
  * @param {Node} parent
93
+ * @param {boolean} [tail] the sentinel stands for the tail of `parent`: what
94
+ * it anchors is the last thing the framework appends there, so it stays the
95
+ * right insertion point for its whole life (see the `if` runtime)
92
96
  * @returns {AppendIntoAnchor}
93
97
  */
94
- export function append_into(parent) {
95
- if (hydrating) {
98
+ export function append_into(parent, tail) {
99
+ if (HYDRATION && hydrating) {
96
100
  var child = get_first_child(/** @type {Node} */ (hydrate_node));
97
101
 
98
102
  if (child === null) {
@@ -102,7 +106,7 @@ export function append_into(parent) {
102
106
  set_hydrate_node(child);
103
107
  }
104
108
 
105
- return { parent, into: true };
109
+ return { parent, into: true, tail: tail === true };
106
110
  }
107
111
 
108
112
  /**
@@ -121,7 +125,7 @@ export function resolve_anchor(node) {
121
125
  if (/** @type {AppendIntoAnchor} */ (node).into !== true) {
122
126
  return /** @type {Node} */ (node);
123
127
  }
124
- if (hydrating) {
128
+ if (HYDRATION && hydrating) {
125
129
  return /** @type {Node} */ (hydrate_node);
126
130
  }
127
131
  return /** @type {AppendIntoAnchor} */ (node).parent.appendChild(create_text());
@@ -136,7 +140,7 @@ export function resolve_anchor(node) {
136
140
  export function first_child_frag(node, is_text) {
137
141
  // During hydration, for fragment templates, hydrate_node is already
138
142
  // pointing to the first element of the fragment. Don't descend into it.
139
- if (hydrating) {
143
+ if (HYDRATION && hydrating) {
140
144
  return hydrate_node;
141
145
  }
142
146
  var child = /** @type {Text} */ (first_child(node, is_text));
@@ -163,7 +167,7 @@ export function get_next_sibling(node) {
163
167
  * @returns {Node | null}
164
168
  */
165
169
  export function next_sibling(node, is_text) {
166
- if (!hydrating) {
170
+ if (!(HYDRATION && hydrating)) {
167
171
  return node.nextSibling;
168
172
  }
169
173
  return hydrate_next_sibling(is_text);
@@ -6,6 +6,7 @@ import { handle_root_events, release_root_events } from './events.js';
6
6
  import { active_block } from './runtime.js';
7
7
  import { hydrating, hydrate_node, set_hydration, set_hydrating } from './hydration.js';
8
8
  import { is_tsrx_element, tsrx_element } from '../../element.js';
9
+ import { HYDRATION } from 'ripple/internal/client/hydration-enabled';
9
10
 
10
11
  /**
11
12
  * @typedef {(anchor: AppendIntoAnchor, block: Block) => void} PortalRender
@@ -67,7 +68,7 @@ function run_portal(s) {
67
68
  end: null,
68
69
  r: s.r,
69
70
  c: next_children,
70
- a: { parent: next_target, into: true },
71
+ a: { parent: next_target, into: true, tail: false },
71
72
  b: /** @type {Block} */ (s.p),
72
73
  });
73
74
 
@@ -126,7 +127,7 @@ function create_portal(get_target, render_children, get_children) {
126
127
  p: null,
127
128
  };
128
129
 
129
- if (!hydrating) {
130
+ if (!(HYDRATION && hydrating)) {
130
131
  render(run_portal, state);
131
132
  return;
132
133
  }
@@ -1,21 +1,9 @@
1
1
  /** @import { Block } from '#client' */
2
2
 
3
- import { branch, destroy_block, ref } from './blocks.js';
4
- import { DESTROYED, REF_PROP } from './constants.js';
5
- import { isRefProp as is_ref_prop } from '@tsrx/core/runtime/ref';
6
- import { is_ripple_object } from './utils.js';
7
- import {
8
- get_descriptors,
9
- get_own_property_symbols,
10
- get_prototype_of,
11
- } from '@tsrx/core/runtime/language-helpers';
12
- import { event_listener } from './events.js';
13
- import { get_attribute_event_name, is_event_attribute } from '@tsrx/core/runtime/events';
14
- import { get } from './runtime.js';
15
3
  import { hydrating } from './hydration.js';
4
+ import { HYDRATION } from 'ripple/internal/client/hydration-enabled';
16
5
  import { TEXT_NODE } from '../../../constants.js';
17
- import { clsx } from 'clsx';
18
- import { normalize_css_property_name } from '@tsrx/core/runtime/html';
6
+ import { class_name } from '../../../utils/class-name.js';
19
7
 
20
8
  /**
21
9
  * Sets the text of an element whose only child is that text. The template
@@ -31,7 +19,7 @@ import { normalize_css_property_name } from '@tsrx/core/runtime/html';
31
19
  */
32
20
  export function set_text_content(element, value, prev) {
33
21
  var str = value == null ? '' : value + '';
34
- if (hydrating) {
22
+ if (HYDRATION && hydrating) {
35
23
  var text = element.firstChild;
36
24
  if (text === null) {
37
25
  if (str !== '') {
@@ -63,166 +51,35 @@ export function set_text(text, value) {
63
51
  // The compiled render block compares against the value it last wrote, so
64
52
  // nothing is cached on the node. Only server-rendered text can already
65
53
  // hold the value; a fresh template text node never does.
66
- if (hydrating && text.nodeValue === str) {
54
+ if (HYDRATION && hydrating && text.nodeValue === str) {
67
55
  return;
68
56
  }
69
57
  text.nodeValue = str;
70
58
  }
71
59
 
72
- /** @type {Map<string, string[]>} */
73
- var setters_cache = new Map();
74
-
75
- /**
76
- * @param {Element} element
77
- * @returns {string[]}
78
- */
79
- function get_setters(element) {
80
- var setters = setters_cache.get(element.nodeName);
81
- if (setters) return setters;
82
- setters_cache.set(element.nodeName, (setters = []));
83
-
84
- var descriptors;
85
- var proto = element; // In the case of custom elements there might be setters on the instance
86
- var element_proto = Element.prototype;
87
-
88
- // Stop at Element, from there on there's only unnecessary setters we're not interested in
89
- // Do not use constructor.name here as that's unreliable in some browser environments
90
- while (element_proto !== proto) {
91
- descriptors = get_descriptors(proto);
92
-
93
- for (var key in descriptors) {
94
- if (descriptors[key].set) {
95
- setters.push(key);
96
- }
97
- }
98
-
99
- proto = get_prototype_of(proto);
100
- }
101
-
102
- return setters;
103
- }
104
-
105
- /**
106
- * @param {Element} element
107
- * @param {any} value
108
- * @param {Record<string, string | number | null | undefined> | undefined} prev
109
- * @returns {void}
110
- */
111
- export function set_style(element, value, prev = {}) {
112
- if (value == null) {
113
- element.removeAttribute('style');
114
- } else if (typeof value !== 'string') {
115
- apply_styles(/** @type {HTMLElement} */ (element), value, prev);
116
- } else {
117
- // @ts-ignore
118
- element.style.cssText = value;
119
- }
120
- }
121
-
122
- /**
123
- * @param {Element} element
124
- * @param {string} attribute
125
- * @param {any} value
126
- * @returns {void}
127
- */
128
- export function set_attribute(element, attribute, value) {
129
- if (value == null) {
130
- element.removeAttribute(attribute);
131
- } else if (typeof value !== 'string' && get_setters(element).includes(attribute)) {
132
- /** @type {any} */ (element)[attribute] = value;
133
- } else {
134
- element.setAttribute(attribute, value);
135
- }
136
- }
137
-
138
- /**
139
- * @param {HTMLElement} element
140
- * @param {Record<string, string | number | null | undefined>} new_styles
141
- * @param {Record<string, string | number | null | undefined>} prev
142
- */
143
- function apply_styles(element, new_styles, prev) {
144
- const style = element.style;
145
-
146
- // Apply new styles
147
- for (const key in new_styles) {
148
- const css_prop = normalize_css_property_name(key);
149
- const raw_value = new_styles[key];
150
- const value = raw_value == null ? null : String(raw_value);
151
-
152
- if (!(key in prev) || prev[key] !== value) {
153
- style.setProperty(css_prop, value);
154
- }
155
- }
156
-
157
- // Remove properties that were in prev but not in new_styles
158
- for (const key in prev) {
159
- if (!(key in new_styles)) {
160
- const css_prop = normalize_css_property_name(key);
161
- style.removeProperty(css_prop);
162
- }
163
- }
164
- }
165
-
166
- /**
167
- * Helper function to set a single attribute
168
- * @param {Element} element
169
- * @param {string} key
170
- * @param {any} value
171
- * @param {Record<string, (() => void) | undefined>} remove_listeners
172
- * @param {Record<string | symbol, any>} prev
173
- */
174
- function set_attribute_helper(element, key, value, remove_listeners, prev) {
175
- if (key === 'class') {
176
- const is_html = element.namespaceURI === 'http://www.w3.org/1999/xhtml';
177
- set_class(/** @type {HTMLElement} */ (element), value, undefined, is_html);
178
- } else if (key === 'innerHTML') {
179
- element.removeAttribute('innerhtml');
180
- /** @type {HTMLElement} */ (element).innerHTML = value == null ? '' : String(value);
181
- } else if (key === 'style') {
182
- set_style(element, value, prev.style);
183
- } else if (key === '#class') {
184
- // Special case for the scope classes of an element that spreads props:
185
- // one token per scope hash and applied theme.
186
- element.classList.add(...String(value).split(' ').filter(Boolean));
187
- } else if (typeof key === 'string' && is_event_attribute(key)) {
188
- // Handle event handlers in spread props
189
- if (remove_listeners[key]) {
190
- remove_listeners[key]();
191
- remove_listeners[key] = undefined;
192
- }
193
- if (value != null) {
194
- const event_name = get_attribute_event_name(key, value);
195
- remove_listeners[key] = event_listener(event_name, element, value);
196
- }
197
- } else {
198
- set_attribute(element, key, value);
199
- }
200
- }
201
-
202
60
  /**
61
+ * Writes a class the compiler proved to be a string (or nullish). The
62
+ * compiled render block compares against the class it last applied, so
63
+ * nothing is cached on the element. Removing the attribute when the value is
64
+ * only an empty string costs more than an empty className, so the class is
65
+ * removed only for a nullish value; an element that has no class does not
66
+ * receive an empty one either (the server omits an empty class attribute as
67
+ * well).
203
68
  * @param {HTMLElement} dom
204
- * @param {string} value
69
+ * @param {string | null | undefined} value
205
70
  * @param {string} [hash]
206
71
  * @param {boolean} [is_html]
207
72
  * @returns {void}
208
73
  */
209
74
  export function set_class(dom, value, hash, is_html = true) {
210
- var class_value =
211
- value == null
212
- ? hash === undefined
213
- ? null
214
- : hash
215
- : // Fast-path for string values
216
- typeof value === 'string'
217
- ? value + (hash ? ' ' + hash : '')
218
- : clsx([value, hash]);
75
+ /** @type {string | null} */
76
+ var class_value;
77
+ if (value == null) {
78
+ class_value = hash === undefined ? null : hash;
79
+ } else {
80
+ class_value = hash ? (value === '' ? hash : value + ' ' + hash) : value;
81
+ }
219
82
 
220
- // The compiled render block compares against the class it last applied,
221
- // so nothing is cached on the element. Removing the attribute when the
222
- // value is only an empty string costs more than an empty className, so the
223
- // class is removed only for a nullish value; an element that has no class
224
- // does not receive an empty one either (the server omits an empty class
225
- // attribute as well).
226
83
  if (class_value === null) {
227
84
  dom.removeAttribute('class');
228
85
  } else if (is_html) {
@@ -234,6 +91,24 @@ export function set_class(dom, value, hash, is_html = true) {
234
91
  }
235
92
  }
236
93
 
94
+ /**
95
+ * `set_class` for a value of any shape: arrays, objects and numbers compose
96
+ * as clsx does. Only a bundle with such a class expression carries the joiner.
97
+ * @param {HTMLElement} dom
98
+ * @param {any} value
99
+ * @param {string} [hash]
100
+ * @param {boolean} [is_html]
101
+ * @returns {void}
102
+ */
103
+ export function set_class_value(dom, value, hash, is_html = true) {
104
+ set_class(
105
+ dom,
106
+ value == null || typeof value === 'string' ? value : class_name(value),
107
+ hash,
108
+ is_html,
109
+ );
110
+ }
111
+
237
112
  /**
238
113
  * @param {HTMLInputElement | HTMLProgressElement | HTMLOptionElement} element
239
114
  * @param {any} value
@@ -266,18 +141,12 @@ export function set_value(element, value) {
266
141
  * @returns {void}
267
142
  */
268
143
  export function set_checked(element, checked) {
269
- var attributes = (element.__attributes ??= {});
270
-
271
- if (
272
- attributes.checked ===
273
- (attributes.checked =
274
- // treat null and undefined the same for the initial value
275
- checked ?? undefined)
276
- ) {
277
- return;
144
+ // Compared against the element itself rather than a record of the last
145
+ // write: no expando per checkbox, and a box the user just toggled to the
146
+ // value being set needs no write at all.
147
+ if (element.checked !== (checked = !!checked)) {
148
+ element.checked = checked;
278
149
  }
279
-
280
- element.checked = checked;
281
150
  }
282
151
 
283
152
  /**
@@ -296,155 +165,3 @@ export function set_selected(element, selected) {
296
165
  element.removeAttribute('selected');
297
166
  }
298
167
  }
299
-
300
- /**
301
- * @param {Element} element
302
- * @param {() => Record<string | symbol, any>} fn
303
- * @returns {() => void}
304
- */
305
- export function apply_element_spread(element, fn) {
306
- /** @type {Record<string | symbol, any>} */
307
- var prev = {};
308
- /** @type {Record<string | symbol, Block | undefined>} */
309
- var effects = {};
310
- /** @type {Record<string | symbol, (() => void) | undefined>} */
311
- var remove_listeners = {};
312
-
313
- /** @type {Record<symbol, any>} */
314
- var prev_symbols = {};
315
- /** @type {Record<string, any>} */
316
- var prev_ref_props = {};
317
-
318
- return () => {
319
- var next = fn();
320
- var current_symbols = /** @type {Record<symbol, any>} */ ({});
321
-
322
- for (const symbol of get_own_property_symbols(next)) {
323
- if (symbol.description !== REF_PROP) {
324
- continue;
325
- }
326
- const ref_fn = next[symbol];
327
- current_symbols[symbol] = ref_fn;
328
-
329
- if (
330
- !(symbol in prev_symbols) ||
331
- ref_fn !== prev_symbols[symbol] ||
332
- (effects[symbol] && (effects[symbol].f & DESTROYED) !== 0)
333
- ) {
334
- if (effects[symbol] && (effects[symbol].f & DESTROYED) === 0) {
335
- destroy_block(effects[symbol]);
336
- }
337
- effects[symbol] = create_spread_ref_effect(element, ref_fn);
338
- }
339
- }
340
-
341
- for (const symbol of get_own_property_symbols(prev_symbols)) {
342
- if (!(symbol in current_symbols) && effects[symbol]) {
343
- destroy_block(/** @type {Block} */ (effects[symbol]));
344
- effects[symbol] = undefined;
345
- }
346
- }
347
-
348
- prev_symbols = current_symbols;
349
-
350
- /** @type {Record<string, any>} */
351
- var current_ref_props = {};
352
- var keys = spread_keys(next);
353
-
354
- for (const key of keys) {
355
- const ref_fn = next[key];
356
- if (!is_ref_prop(ref_fn)) {
357
- continue;
358
- }
359
-
360
- current_ref_props[key] = ref_fn;
361
-
362
- if (
363
- !(key in prev_ref_props) ||
364
- ref_fn !== prev_ref_props[key] ||
365
- (effects[key] && (effects[key].f & DESTROYED) !== 0)
366
- ) {
367
- if (effects[key] && (effects[key].f & DESTROYED) === 0) {
368
- destroy_block(effects[key]);
369
- }
370
- effects[key] = create_spread_ref_effect(element, ref_fn);
371
- }
372
- }
373
-
374
- for (const key in prev_ref_props) {
375
- if (!(key in current_ref_props) && effects[key]) {
376
- destroy_block(/** @type {Block} */ (effects[key]));
377
- effects[key] = undefined;
378
- }
379
- }
380
-
381
- prev_ref_props = current_ref_props;
382
-
383
- for (let key in remove_listeners) {
384
- // Remove event listeners that are no longer present
385
- if ((!keys.includes(key) || is_ref_prop(next[key])) && remove_listeners[key]) {
386
- remove_listeners[key]();
387
- remove_listeners[key] = undefined;
388
- }
389
- }
390
-
391
- for (const key in prev) {
392
- if (!keys.includes(key) || is_ref_prop(next[key])) {
393
- if (key === '#class') {
394
- continue;
395
- }
396
- set_attribute_helper(element, key, null, remove_listeners, prev);
397
- }
398
- }
399
-
400
- /** @type {typeof prev} */
401
- const current = {};
402
- for (const key of keys) {
403
- if (key === 'children') continue;
404
-
405
- let value = next[key];
406
- if (is_ref_prop(value)) {
407
- continue;
408
- }
409
- if (is_ripple_object(value)) {
410
- value = get(value);
411
- }
412
- current[key] = value;
413
-
414
- if (key in prev && prev[key] === value && key !== '#class') {
415
- continue;
416
- }
417
-
418
- set_attribute_helper(element, key, value, remove_listeners, prev);
419
- }
420
- prev = current;
421
- };
422
- }
423
-
424
- /**
425
- * The keys an element spread applies: every prop of a props instance, or the
426
- * enumerable keys of a plain object.
427
- * @param {Record<string | symbol, any>} next
428
- * @returns {string[]}
429
- */
430
- function spread_keys(next) {
431
- /** @type {string[]} */
432
- var keys = [];
433
- for (var key in next) keys.push(key);
434
- return keys;
435
- }
436
-
437
- /**
438
- * Keep spread refs in a branch block so ordinary spread updates do not destroy
439
- * and recreate the ref block before `apply_element_spread` can compare the
440
- * previous and current ref values.
441
- *
442
- * @param {Element} element
443
- * @param {any} ref_fn
444
- * @returns {Block}
445
- */
446
- function create_spread_ref_effect(element, ref_fn) {
447
- return branch(() => {
448
- ref(element, () => ref_fn);
449
- });
450
- }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Whether `mount()` and `hydrate()` render the app under a default
3
+ * try/pending/catch boundary. The Vite plugin's `rootBoundary: false` build
4
+ * aliases this module to one exporting `false`; the bundler then drops the
5
+ * boundary runtime from an app that renders without one.
6
+ */
7
+ export const ROOT_BOUNDARY = true;