ripple 0.2.208 → 0.2.210

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 (108) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +2 -1
  3. package/package.json +2 -6
  4. package/shims/rollup-estree-types.d.ts +1 -1
  5. package/src/compiler/index.d.ts +1 -0
  6. package/src/compiler/index.js +7 -1
  7. package/src/compiler/phases/1-parse/index.js +15 -6
  8. package/src/compiler/phases/2-analyze/css-analyze.js +100 -104
  9. package/src/compiler/phases/2-analyze/index.js +215 -2
  10. package/src/compiler/phases/3-transform/client/index.js +388 -50
  11. package/src/compiler/phases/3-transform/segments.js +123 -39
  12. package/src/compiler/phases/3-transform/server/index.js +266 -13
  13. package/src/compiler/types/index.d.ts +16 -3
  14. package/src/compiler/utils.js +1 -15
  15. package/src/constants.js +0 -2
  16. package/src/helpers.d.ts +4 -0
  17. package/src/html-tree-validation.js +211 -0
  18. package/src/jsx-runtime.d.ts +260 -259
  19. package/src/jsx-runtime.js +12 -12
  20. package/src/runtime/array.js +17 -17
  21. package/src/runtime/create-subscriber.js +1 -1
  22. package/src/runtime/index-client.js +1 -5
  23. package/src/runtime/index-server.js +15 -0
  24. package/src/runtime/internal/client/compat.js +3 -3
  25. package/src/runtime/internal/client/composite.js +6 -1
  26. package/src/runtime/internal/client/head.js +50 -4
  27. package/src/runtime/internal/client/html.js +73 -12
  28. package/src/runtime/internal/client/hydration.js +12 -0
  29. package/src/runtime/internal/client/index.js +1 -1
  30. package/src/runtime/internal/client/portal.js +54 -29
  31. package/src/runtime/internal/client/rpc.js +3 -1
  32. package/src/runtime/internal/client/switch.js +5 -0
  33. package/src/runtime/internal/client/template.js +117 -11
  34. package/src/runtime/internal/client/try.js +1 -0
  35. package/src/runtime/internal/server/index.js +113 -1
  36. package/src/runtime/internal/server/rpc.js +4 -4
  37. package/src/runtime/map.js +2 -2
  38. package/src/runtime/object.js +6 -6
  39. package/src/runtime/proxy.js +12 -11
  40. package/src/runtime/reactive-value.js +9 -1
  41. package/src/runtime/set.js +12 -7
  42. package/src/runtime/url-search-params.js +0 -1
  43. package/src/server/index.js +4 -0
  44. package/src/utils/hashing.js +15 -0
  45. package/src/utils/normalize_css_property_name.js +1 -1
  46. package/tests/client/array/array.mutations.test.ripple +8 -8
  47. package/tests/client/basic/basic.errors.test.ripple +28 -0
  48. package/tests/client/basic/basic.events.test.ripple +6 -3
  49. package/tests/client/basic/basic.utilities.test.ripple +1 -1
  50. package/tests/client/compiler/compiler.regex.test.ripple +10 -8
  51. package/tests/client/composite/composite.generics.test.ripple +5 -2
  52. package/tests/client/dynamic-elements.test.ripple +30 -1
  53. package/tests/client/function-overload-import.ripple +6 -7
  54. package/tests/client/html.test.ripple +0 -1
  55. package/tests/client/object.test.ripple +2 -2
  56. package/tests/client/portal.test.ripple +3 -3
  57. package/tests/client/return.test.ripple +2500 -0
  58. package/tests/client/try.test.ripple +69 -0
  59. package/tests/client/typescript-generics.test.ripple +1 -1
  60. package/tests/client/url/url.derived.test.ripple +1 -1
  61. package/tests/client/url/url.parsing.test.ripple +3 -3
  62. package/tests/client/url/url.partial-removal.test.ripple +7 -7
  63. package/tests/client/url/url.reactivity.test.ripple +15 -15
  64. package/tests/client/url/url.serialization.test.ripple +2 -2
  65. package/tests/hydration/basic.test.js +23 -0
  66. package/tests/hydration/build-components.js +10 -4
  67. package/tests/hydration/compiled/client/basic.js +165 -3
  68. package/tests/hydration/compiled/client/for.js +1140 -23
  69. package/tests/hydration/compiled/client/head.js +234 -0
  70. package/tests/hydration/compiled/client/html.js +135 -0
  71. package/tests/hydration/compiled/client/portal.js +172 -0
  72. package/tests/hydration/compiled/client/reactivity.js +3 -1
  73. package/tests/hydration/compiled/client/return.js +1976 -0
  74. package/tests/hydration/compiled/client/switch.js +162 -0
  75. package/tests/hydration/compiled/server/basic.js +249 -0
  76. package/tests/hydration/compiled/server/events.js +1 -1
  77. package/tests/hydration/compiled/server/for.js +891 -1
  78. package/tests/hydration/compiled/server/head.js +291 -0
  79. package/tests/hydration/compiled/server/html.js +133 -0
  80. package/tests/hydration/compiled/server/if.js +1 -1
  81. package/tests/hydration/compiled/server/portal.js +250 -0
  82. package/tests/hydration/compiled/server/reactivity.js +1 -1
  83. package/tests/hydration/compiled/server/return.js +1969 -0
  84. package/tests/hydration/compiled/server/switch.js +130 -0
  85. package/tests/hydration/components/basic.ripple +55 -0
  86. package/tests/hydration/components/for.ripple +403 -0
  87. package/tests/hydration/components/head.ripple +111 -0
  88. package/tests/hydration/components/html.ripple +38 -0
  89. package/tests/hydration/components/portal.ripple +49 -0
  90. package/tests/hydration/components/return.ripple +564 -0
  91. package/tests/hydration/components/switch.ripple +51 -0
  92. package/tests/hydration/for.test.js +363 -0
  93. package/tests/hydration/head.test.js +105 -0
  94. package/tests/hydration/html.test.js +46 -0
  95. package/tests/hydration/portal.test.js +71 -0
  96. package/tests/hydration/return.test.js +544 -0
  97. package/tests/hydration/switch.test.js +42 -0
  98. package/tests/server/basic.attributes.test.ripple +1 -1
  99. package/tests/server/compiler.test.ripple +22 -0
  100. package/tests/server/composite.test.ripple +5 -2
  101. package/tests/server/html-nesting-validation.test.ripple +237 -0
  102. package/tests/server/return.test.ripple +1379 -0
  103. package/tests/setup-hydration.js +6 -1
  104. package/tests/utils/escaping.test.js +3 -1
  105. package/tests/utils/normalize_css_property_name.test.js +0 -1
  106. package/tests/utils/patterns.test.js +6 -2
  107. package/tests/utils/sanitize_template_string.test.js +3 -2
  108. package/types/server.d.ts +16 -0
@@ -5,9 +5,11 @@ import {
5
5
  TEMPLATE_USE_IMPORT_NODE,
6
6
  TEMPLATE_SVG_NAMESPACE,
7
7
  TEMPLATE_MATHML_NAMESPACE,
8
+ HYDRATION_START,
9
+ HYDRATION_END,
8
10
  } from '../../../constants.js';
9
11
  import { hydrate_next, hydrate_node, hydrating, pop } from './hydration.js';
10
- import { create_text, get_first_child, is_firefox } from './operations.js';
12
+ import { create_text, get_first_child, get_next_sibling, is_firefox } from './operations.js';
11
13
  import { active_block, active_namespace } from './runtime.js';
12
14
 
13
15
  /**
@@ -68,30 +70,103 @@ export function template(content, flags) {
68
70
  var is_comment = content === '<!>';
69
71
  var has_start = !is_comment && !content.startsWith('<!>');
70
72
 
73
+ // For fragments, eagerly create the node so we can walk its children
74
+ // during hydration to find the correct end node. The eagerly-created
75
+ // node is reused as the clone template in the non-hydrating path.
76
+ if (is_fragment) {
77
+ node = create_fragment_from_html(
78
+ has_start ? content : '<!>' + content,
79
+ use_svg_namespace,
80
+ use_mathml_namespace,
81
+ );
82
+ }
83
+
71
84
  return () => {
72
85
  if (hydrating) {
73
- assign_nodes(/** @type {Node} */ (hydrate_node), /** @type {Node} */ (hydrate_node));
86
+ if (is_fragment) {
87
+ // Walk the template fragment's children in lockstep with hydrated
88
+ // DOM siblings. Comment nodes (<!>) are control flow anchors whose
89
+ // hydration markers (<!--[-->...<!--]-->) are consumed by block
90
+ // processing, so we skip them and only advance for element/text nodes.
91
+ var start = /** @type {Node} */ (hydrate_node);
92
+ var end = start;
93
+ var children = /** @type {DocumentFragment} */ (node).childNodes;
94
+ var is_first = true;
95
+
96
+ for (var i = 0; i < children.length; i++) {
97
+ if (children[i].nodeType === 8) continue;
98
+
99
+ if (is_first) {
100
+ is_first = false;
101
+ continue;
102
+ }
103
+
104
+ // Advance past comment nodes in the hydrated DOM. Each <!>
105
+ // anchor in the template expands to a <!--[-->...<!--]-->
106
+ // region, and there may be consecutive ones. Track depth so
107
+ // nested blocks are skipped, and stop at the first non-comment
108
+ // node at depth 0.
109
+ var next = get_next_sibling(end);
110
+ var depth = 0;
111
+
112
+ while (next !== null) {
113
+ if (next.nodeType === 8) {
114
+ var data = /** @type {Comment} */ (next).data;
115
+ if (data === HYDRATION_START) {
116
+ depth++;
117
+ } else if (data === HYDRATION_END) {
118
+ if (depth > 0) {
119
+ depth--;
120
+ } else {
121
+ // Reached a close marker that belongs to a parent block
122
+ next = null;
123
+ break;
124
+ }
125
+ }
126
+ next = get_next_sibling(next);
127
+ continue;
128
+ }
129
+
130
+ if (depth === 0) {
131
+ break;
132
+ }
133
+ next = get_next_sibling(next);
134
+ }
135
+
136
+ if (next === null) {
137
+ break;
138
+ }
139
+ end = next;
140
+ }
141
+
142
+ assign_nodes(start, end);
143
+ return start;
144
+ } else {
145
+ assign_nodes(/** @type {Node} */ (hydrate_node), /** @type {Node} */ (hydrate_node));
146
+ }
74
147
  return /** @type {Node} */ (hydrate_node);
75
148
  }
76
149
  // If using runtime namespace, check active_namespace
77
150
  var svg = !is_comment && (use_svg_namespace || active_namespace === 'svg');
78
151
  var mathml = !is_comment && (use_mathml_namespace || active_namespace === 'mathml');
79
152
 
80
- if (node === undefined) {
153
+ if (node === undefined || use_svg_namespace !== svg || use_mathml_namespace !== mathml) {
81
154
  node = create_fragment_from_html(has_start ? content : '<!>' + content, svg, mathml);
82
155
  if (!is_fragment) node = /** @type {Node} */ (get_first_child(node));
83
156
  }
84
157
 
158
+ /** @type {DocumentFragment | Node} */
85
159
  var clone =
86
160
  use_import_node || is_firefox
87
161
  ? document.importNode(/** @type {Node} */ (node), true)
88
162
  : /** @type {Node} */ (node).cloneNode(true);
89
163
 
90
164
  if (is_fragment) {
91
- var start = get_first_child(clone);
92
- var end = clone.lastChild;
165
+ // we know for sure that children exist
166
+ var start = /** @type {Node} */ (get_first_child(/** @type {DocumentFragment} */ (clone)));
167
+ var end = /** @type {Node} */ (/** @type {DocumentFragment} */ (clone).lastChild);
93
168
 
94
- assign_nodes(/** @type {Node} */ (start), /** @type {Node} */ (end));
169
+ assign_nodes(start, end);
95
170
  } else {
96
171
  assign_nodes(clone, clone);
97
172
  }
@@ -104,15 +179,44 @@ export function template(content, flags) {
104
179
  * Appends a DOM node before the anchor node.
105
180
  * @param {ChildNode} anchor - The anchor node.
106
181
  * @param {Node} dom - The DOM node to append.
182
+ * @param {boolean} [skip_advance] - If true, don't advance hydrate_node (used when next() already positioned it).
107
183
  */
108
- export function append(anchor, dom) {
184
+ export function append(anchor, dom, skip_advance) {
109
185
  if (hydrating) {
110
- pop(dom);
186
+ // When skip_advance is true, the caller (e.g., a fragment component) has already
187
+ // used next() to position hydrate_node correctly. We must NOT reset it.
188
+ if (skip_advance) {
189
+ return;
190
+ }
191
+
111
192
  // During hydration, if anchor === dom, we're hydrating a child component
112
193
  // where the "anchor" IS the content. Don't advance past it.
113
- if (anchor !== dom) {
114
- hydrate_next();
194
+ if (anchor === dom) {
195
+ pop(dom);
196
+ return;
115
197
  }
198
+
199
+ // If the hydration cursor has descended into dom's children (e.g. after
200
+ // child()/sibling() traversal inside a single-node template), we need
201
+ // pop() to reset back to dom's sibling level before advancing.
202
+ // But if the cursor is already at dom's sibling level (e.g. because
203
+ // nested control flow blocks advanced it past dom via sibling traversal),
204
+ // pop() would incorrectly reset backwards — so we skip it.
205
+ if (hydrate_node?.parentNode === dom) {
206
+ pop(dom);
207
+ } else if (hydrate_node !== dom) {
208
+ // Cursor has advanced past dom via sibling traversal (due to nested
209
+ // block processing). Update the branch block's end to reflect the
210
+ // actual extent, which may be past the statically-assigned end from
211
+ // the template's assign_nodes call.
212
+ var block = /** @type {Block} */ (active_block);
213
+ var s = block.s;
214
+ if (s !== null) {
215
+ s.end = /** @type {Node} */ (hydrate_node);
216
+ }
217
+ }
218
+
219
+ hydrate_next();
116
220
  return;
117
221
  }
118
222
  anchor.before(/** @type {Node} */ (dom));
@@ -123,7 +227,9 @@ export function text(data = '') {
123
227
  assign_nodes(/** @type {Node} */ (hydrate_node), /** @type {Node} */ (hydrate_node));
124
228
  return /** @type {Node} */ (hydrate_node);
125
229
  }
126
- return create_text(data);
230
+ var node = create_text(data);
231
+ assign_nodes(node, node);
232
+ return node;
127
233
  }
128
234
 
129
235
  /**
@@ -39,6 +39,7 @@ export function try_block(node, fn, catch_fn, pending_fn = null) {
39
39
  */
40
40
  function move_block(block, fragment) {
41
41
  var state = block.s;
42
+ if (state === null) return;
42
43
  var node = state.start;
43
44
  var end = state.end;
44
45
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  @import { Component, Dependency, Derived, Tracked } from '#server';
3
- @import { render, renderToStream, SSRComponent } from 'ripple/server';
3
+ @import { SSRComponent } from 'ripple/server';
4
4
  */
5
5
 
6
6
  import { Readable } from 'stream';
@@ -11,9 +11,14 @@ import { is_boolean_attribute } from '../../../compiler/utils.js';
11
11
  import { clsx } from 'clsx';
12
12
  import { normalize_css_property_name } from '../../../utils/normalize_css_property_name.js';
13
13
  import { BLOCK_CLOSE, BLOCK_OPEN } from '../../../constants.js';
14
+ import {
15
+ is_tag_valid_with_parent,
16
+ is_tag_valid_with_ancestor,
17
+ } from '../../../html-tree-validation.js';
14
18
 
15
19
  export { escape };
16
20
  export { register_component_css as register_css } from './css-registry.js';
21
+ export { hash } from '../../../utils/hashing.js';
17
22
 
18
23
  /** @type {null | Component} */
19
24
  export let active_component = null;
@@ -225,6 +230,9 @@ export async function render(component) {
225
230
  let body = '';
226
231
  let css = new Set();
227
232
 
233
+ // Reset dev-mode element tracking state at the start of each render
234
+ reset_element_state();
235
+
228
236
  try {
229
237
  if (component.async) {
230
238
  await component(output, {});
@@ -240,6 +248,8 @@ export async function render(component) {
240
248
  css = output.css;
241
249
  } catch (error) {
242
250
  console.log(error);
251
+ } finally {
252
+ reset_element_state();
243
253
  }
244
254
  return { head, body, css };
245
255
  }
@@ -260,6 +270,9 @@ export function renderToStream(component) {
260
270
  * @param {Output} output
261
271
  */
262
272
  async function render_in_chunks(component, stream, output) {
273
+ // Reset dev-mode element tracking state at the start of each render
274
+ reset_element_state();
275
+
263
276
  try {
264
277
  if (component.async) {
265
278
  await component(output, {});
@@ -273,6 +286,8 @@ async function render_in_chunks(component, stream, output) {
273
286
  } catch (error) {
274
287
  console.error(error);
275
288
  stream.emit('error', error);
289
+ } finally {
290
+ reset_element_state();
276
291
  }
277
292
  }
278
293
  /**
@@ -294,6 +309,103 @@ export function pop_component() {
294
309
  active_component = component.p;
295
310
  }
296
311
 
312
+ /**
313
+ * @typedef {{
314
+ * tag: string;
315
+ * parent: undefined | ElementContext;
316
+ * filename: undefined | string;
317
+ * line: number;
318
+ * column: number;
319
+ * }} ElementContext
320
+ */
321
+
322
+ /** @type {ElementContext | undefined} */
323
+ let current_element;
324
+
325
+ /**
326
+ * @type {Set<string>}
327
+ */
328
+ let seen_warnings = new Set();
329
+
330
+ /**
331
+ * @param {string} message
332
+ */
333
+ function print_nesting_error(message) {
334
+ message =
335
+ `node_invalid_placement_ssr: ${message}\n\n` +
336
+ 'This can cause content to shift around as the browser repairs the HTML, and will likely result in a hydration mismatch.';
337
+
338
+ if (seen_warnings.has(message)) return;
339
+ seen_warnings.add(message);
340
+
341
+ // eslint-disable-next-line no-console
342
+ console.error(message);
343
+ }
344
+
345
+ /**
346
+ * Pushes an element onto the element stack and validates its nesting.
347
+ * Used during DEV mode SSR to detect invalid HTML nesting that would cause
348
+ * the browser to repair the HTML, breaking hydration.
349
+ * @param {string} tag
350
+ * @param {string} filename
351
+ * @param {number} line
352
+ * @param {number} column
353
+ * @returns {void}
354
+ */
355
+ export function push_element(tag, filename, line, column) {
356
+ var parent = current_element;
357
+ var element = { tag, parent, filename, line, column };
358
+
359
+ if (parent !== undefined) {
360
+ var ancestor = parent.parent;
361
+ var ancestors = [parent.tag];
362
+
363
+ const child_loc = filename ? `${filename}:${line}:${column}` : undefined;
364
+ const parent_loc = parent.filename
365
+ ? `${parent.filename}:${parent.line}:${parent.column}`
366
+ : undefined;
367
+
368
+ const message = is_tag_valid_with_parent(tag, parent.tag, child_loc, parent_loc);
369
+ if (message) print_nesting_error(message);
370
+
371
+ while (ancestor != null) {
372
+ ancestors.push(ancestor.tag);
373
+ const ancestor_loc = ancestor.filename
374
+ ? `${ancestor.filename}:${ancestor.line}:${ancestor.column}`
375
+ : undefined;
376
+
377
+ const ancestor_message = is_tag_valid_with_ancestor(tag, ancestors, child_loc, ancestor_loc);
378
+ if (ancestor_message) print_nesting_error(ancestor_message);
379
+
380
+ ancestor = ancestor.parent;
381
+ }
382
+ }
383
+
384
+ current_element = element;
385
+ }
386
+
387
+ /**
388
+ * Pops the current element from the element stack.
389
+ * @returns {void}
390
+ */
391
+ export function pop_element() {
392
+ if (current_element !== undefined) {
393
+ current_element = current_element.parent;
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Resets the dev-mode element tracking state.
399
+ * Called automatically at the start/end of each render to prevent
400
+ * state from leaking between renders (e.g., if a render throws).
401
+ * Also exported for testing purposes.
402
+ * @returns {void}
403
+ */
404
+ export function reset_element_state() {
405
+ seen_warnings = new Set();
406
+ current_element = undefined;
407
+ }
408
+
297
409
  /**
298
410
  * @param {() => any} fn
299
411
  * @returns {Promise<void>}
@@ -3,11 +3,11 @@ import * as devalue from 'devalue';
3
3
  /**
4
4
  * @template {any[]} T
5
5
  * @template V
6
- * @param {(...args: T) => Promise<V>} fn
6
+ * @param {(...args: T) => Promise<V>} fn
7
7
  * @param {string} rpc_arguments_string
8
8
  */
9
9
  export async function executeServerFunction(fn, rpc_arguments_string) {
10
- const rpc_arguments = devalue.parse(rpc_arguments_string);
11
- const result = await fn.apply(null, rpc_arguments);
10
+ const rpc_arguments = devalue.parse(rpc_arguments_string);
11
+ const result = await fn.apply(null, rpc_arguments);
12
12
  return devalue.stringify({ value: result });
13
- }
13
+ }
@@ -24,7 +24,7 @@ export class TrackedMap extends Map {
24
24
  constructor(iterable) {
25
25
  super();
26
26
 
27
- var block = this.#block = safe_scope();
27
+ var block = (this.#block = safe_scope());
28
28
 
29
29
  if (iterable) {
30
30
  for (var [key, value] of iterable) {
@@ -49,7 +49,7 @@ export class TrackedMap extends Map {
49
49
  var map_proto = Map.prototype;
50
50
 
51
51
  for (const method of introspect_methods) {
52
- /** @type {any} */ (proto)[method] = function (/** @type {...any} */ ...v) {
52
+ /** @type {any} */ (proto)[method] = function (/** @type {...any} */ ...v) {
53
53
  this.size;
54
54
  this.#read_all();
55
55
 
@@ -9,13 +9,13 @@ import { object_proxy } from './proxy.js';
9
9
  * @returns {TrackedObject<T>}
10
10
  */
11
11
  export function TrackedObject(obj) {
12
- if (!new.target) {
13
- throw new Error("TrackedObject must be called with 'new'");
14
- }
12
+ if (!new.target) {
13
+ throw new Error("TrackedObject must be called with 'new'");
14
+ }
15
15
 
16
- var block = safe_scope();
16
+ var block = safe_scope();
17
17
 
18
- return object_proxy(obj, block);
18
+ return object_proxy(obj, block);
19
19
  }
20
20
 
21
21
  /**
@@ -25,5 +25,5 @@ export function TrackedObject(obj) {
25
25
  * @returns {TrackedObject<T>}
26
26
  */
27
27
  export function tracked_object(obj, block) {
28
- return object_proxy(obj, block);
28
+ return object_proxy(obj, block);
29
29
  }
@@ -26,10 +26,10 @@ import {
26
26
  export function proxy(value, block) {
27
27
  // if non-proxyable, or is already a proxy, return `value`
28
28
  if (
29
- typeof value !== 'object'
30
- || value === null
31
- || TRACKED_ARRAY in value
32
- || TRACKED_OBJECT in value
29
+ typeof value !== 'object' ||
30
+ value === null ||
31
+ TRACKED_ARRAY in value ||
32
+ TRACKED_OBJECT in value
33
33
  ) {
34
34
  return value;
35
35
  }
@@ -153,7 +153,9 @@ export function proxy(value, block) {
153
153
  },
154
154
 
155
155
  setPrototypeOf() {
156
- throw new Error(`Cannot set prototype of ${is_proxied_array ? '\`TrackedArray\`' : '\`TrackedObject\`'}`);
156
+ throw new Error(
157
+ `Cannot set prototype of ${is_proxied_array ? '\`TrackedArray\`' : '\`TrackedObject\`'}`,
158
+ );
157
159
  },
158
160
 
159
161
  deleteProperty(target, prop) {
@@ -185,7 +187,7 @@ export function proxy(value, block) {
185
187
 
186
188
  if (t !== undefined || !exists || get_descriptor(target, prop)?.writable) {
187
189
  if (t === undefined) {
188
- t = tracked(exists ? /** @type {any} */ (target)[prop] : UNINITIALIZED, block);
190
+ t = tracked(exists ? /** @type {any} */ (target)[prop] : UNINITIALIZED, block);
189
191
 
190
192
  tracked_elements.set(prop, t);
191
193
  }
@@ -257,14 +259,13 @@ export function proxy(value, block) {
257
259
  enumerable: true,
258
260
  configurable: true,
259
261
  value,
260
- writable: true
262
+ writable: true,
261
263
  };
262
264
  }
263
265
  }
264
266
 
265
267
  return descriptor;
266
268
  },
267
-
268
269
  });
269
270
  }
270
271
 
@@ -284,7 +285,7 @@ export function array_proxy({ elements, block, from_static = false, use_array =
284
285
 
285
286
  if (
286
287
  from_static &&
287
- (first = get_first_if_length(/** @type {Array<T>} */(elements))) !== undefined
288
+ (first = get_first_if_length(/** @type {Array<T>} */ (elements))) !== undefined
288
289
  ) {
289
290
  arr = new Array();
290
291
  arr[0] = first;
@@ -334,8 +335,8 @@ function get_first_if_length(array) {
334
335
  array.length === 1 &&
335
336
  0 in array &&
336
337
  Number.isInteger(first) &&
337
- /** @type {number} */ (first) >= 0 &&
338
- /** @type {number} */ (first) <= MAX_ARRAY_LENGTH
338
+ /** @type {number} */ (first) >= 0 &&
339
+ /** @type {number} */ (first) <= MAX_ARRAY_LENGTH
339
340
  ) {
340
341
  return /** @type {number} */ (first);
341
342
  }
@@ -17,5 +17,13 @@ export function ReactiveValue(fn, start) {
17
17
  const s = createSubscriber(start);
18
18
  const block = safe_scope();
19
19
 
20
- return (derived(fn, block, () => { s(); return fn(); }, (_, prev) => prev));
20
+ return derived(
21
+ fn,
22
+ block,
23
+ () => {
24
+ s();
25
+ return fn();
26
+ },
27
+ (_, prev) => prev,
28
+ );
21
29
  }
@@ -28,7 +28,7 @@ export class TrackedSet extends Set {
28
28
  constructor(iterable) {
29
29
  super();
30
30
 
31
- var block = this.#block = safe_scope();
31
+ var block = (this.#block = safe_scope());
32
32
 
33
33
  if (iterable) {
34
34
  for (var item of iterable) {
@@ -57,7 +57,7 @@ export class TrackedSet extends Set {
57
57
  continue;
58
58
  }
59
59
 
60
- /** @type {any} */ (proto)[method] = function (/** @type {...any} */ ...v) {
60
+ /** @type {any} */ (proto)[method] = function (/** @type {...any} */ ...v) {
61
61
  this.size;
62
62
 
63
63
  return /** @type {any} */ (set_proto)[method].apply(this, v);
@@ -69,7 +69,10 @@ export class TrackedSet extends Set {
69
69
  continue;
70
70
  }
71
71
 
72
- /** @type {any} */ (proto)[method] = function (/** @type {any} */ other, /** @type {...any} */ ...v) {
72
+ /** @type {any} */ (proto)[method] = function (
73
+ /** @type {any} */ other,
74
+ /** @type {...any} */ ...v
75
+ ) {
73
76
  this.size;
74
77
 
75
78
  if (other instanceof TrackedSet) {
@@ -85,14 +88,17 @@ export class TrackedSet extends Set {
85
88
  continue;
86
89
  }
87
90
 
88
- /** @type {any} */ (proto)[method] = function (/** @type {any} */ other, /** @type {...any} */ ...v) {
91
+ /** @type {any} */ (proto)[method] = function (
92
+ /** @type {any} */ other,
93
+ /** @type {...any} */ ...v
94
+ ) {
89
95
  this.size;
90
96
 
91
97
  if (other instanceof TrackedSet) {
92
98
  other.size;
93
99
  }
94
100
 
95
- return new TrackedSet(/** @type {any} */(set_proto)[method].apply(this, [other, ...v]));
101
+ return new TrackedSet(/** @type {any} */ (set_proto)[method].apply(this, [other, ...v]));
96
102
  };
97
103
  }
98
104
  }
@@ -138,9 +144,8 @@ export class TrackedSet extends Set {
138
144
  /**
139
145
  * @param {T} value
140
146
  * @return {boolean}
141
- */
147
+ */
142
148
  has(value) {
143
-
144
149
  var has = super.has(value);
145
150
  var tracked_items = this.#tracked_items;
146
151
  var t = tracked_items.get(value);
@@ -3,7 +3,6 @@ import { get_current_url } from './url.js';
3
3
 
4
4
  export const REPLACE = Symbol();
5
5
 
6
-
7
6
  export class TrackedURLSearchParams extends URLSearchParams {
8
7
  #block = safe_scope();
9
8
  #version = tracked(0, this.#block);
@@ -1,3 +1,7 @@
1
+ // SSR helpers
1
2
  export { render, renderToStream } from '../runtime/internal/server/index.js';
2
3
  export { get_css_for_hashes } from '../runtime/internal/server/css-registry.js';
3
4
  export { executeServerFunction } from '../runtime/internal/server/rpc.js';
5
+
6
+ // Re-export server runtime for components compiled for SSR
7
+ export * from '../runtime/index-server.js';
@@ -0,0 +1,15 @@
1
+ const regex_return_characters = /\r/g;
2
+
3
+ /**
4
+ * Hashes a string to a base36 value
5
+ * @param {string} str
6
+ * @returns {string}
7
+ */
8
+ export function hash(str) {
9
+ str = str.replace(regex_return_characters, '');
10
+ let hash = 5381;
11
+ let i = str.length;
12
+
13
+ while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
14
+ return (hash >>> 0).toString(36);
15
+ }
@@ -16,7 +16,7 @@ export function normalize_css_property_name(str) {
16
16
  return normalized_result;
17
17
  }
18
18
 
19
- normalized_result = str.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
19
+ normalized_result = str.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase());
20
20
  normalized_properties_cache.set(str, normalized_result);
21
21
 
22
22
  return normalized_result;
@@ -5,7 +5,7 @@ describe('TrackedArray > mutations', () => {
5
5
  component ArrayTest() {
6
6
  let items = new TrackedArray(1, 2, 3);
7
7
 
8
- <button onClick={() => items[items.length] = items.length + 1}>{'increment'}</button>
8
+ <button onClick={() => (items[items.length] = items.length + 1)}>{'increment'}</button>
9
9
 
10
10
  <Child {items} />
11
11
  }
@@ -234,7 +234,7 @@ describe('TrackedArray > mutations', () => {
234
234
  component ArrayTest() {
235
235
  let items = new TrackedArray(1, 2, 3);
236
236
 
237
- <button onClick={() => items.forEach((item, i) => items[i] = item * 2)}>
237
+ <button onClick={() => items.forEach((item, i) => (items[i] = item * 2))}>
238
238
  {'double all'}
239
239
  </button>
240
240
  <pre>{JSON.stringify(items)}</pre>
@@ -258,7 +258,7 @@ describe('TrackedArray > mutations', () => {
258
258
  component ArrayTest() {
259
259
  let items = new TrackedArray(1, 2, 3);
260
260
 
261
- <button onClick={() => items.forEach((item, i) => items[i] = item * 2)}>
261
+ <button onClick={() => items.forEach((item, i) => (items[i] = item * 2))}>
262
262
  {'double all'}
263
263
  </button>
264
264
 
@@ -291,7 +291,7 @@ describe('TrackedArray > mutations', () => {
291
291
  let firstItem = track(() => items[0]);
292
292
  let secondItem = track(() => items[1]);
293
293
 
294
- <button onClick={() => items[0] = 100}>{'change first'}</button>
294
+ <button onClick={() => (items[0] = 100)}>{'change first'}</button>
295
295
  <pre>{@firstItem}</pre>
296
296
  <pre>{@secondItem}</pre>
297
297
  <pre>{items[0]}</pre>
@@ -323,8 +323,8 @@ describe('TrackedArray > mutations', () => {
323
323
  let items = new TrackedArray(1, 2, 3);
324
324
  let length = track(() => items.length);
325
325
 
326
- <button onClick={() => items.length = 5}>{'expand'}</button>
327
- <button onClick={() => items.length = 2}>{'shrink'}</button>
326
+ <button onClick={() => (items.length = 5)}>{'expand'}</button>
327
+ <button onClick={() => (items.length = 2)}>{'shrink'}</button>
328
328
  <pre>{JSON.stringify(items)}</pre>
329
329
  <pre>{@length}</pre>
330
330
  }
@@ -357,8 +357,8 @@ describe('TrackedArray > mutations', () => {
357
357
  component ArrayTest() {
358
358
  let items = new TrackedArray(1, 2, 3, 4, 5);
359
359
 
360
- <button onClick={() => items.length = 3}>{'truncate'}</button>
361
- <button onClick={() => items.length = 7}>{'expand'}</button>
360
+ <button onClick={() => (items.length = 3)}>{'truncate'}</button>
361
+ <button onClick={() => (items.length = 7)}>{'expand'}</button>
362
362
  <pre>{JSON.stringify(items)}</pre>
363
363
  <pre>{items.length}</pre>
364
364
  }