ripple 0.4.4 → 0.4.5

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 (27) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/package.json +2 -2
  3. package/src/runtime/internal/client/attributes.js +8 -1
  4. package/src/runtime/internal/client/events.js +15 -1
  5. package/src/runtime/internal/client/index.js +2 -0
  6. package/src/runtime/internal/client/operations.js +9 -4
  7. package/src/runtime/internal/client/render.js +37 -0
  8. package/src/runtime/internal/client/runtime.js +21 -9
  9. package/src/runtime/internal/client/template.js +69 -22
  10. package/tests/hydration/compiled/client/basic.js +43 -16
  11. package/tests/hydration/compiled/client/composite.js +2 -2
  12. package/tests/hydration/compiled/client/events.js +21 -6
  13. package/tests/hydration/compiled/client/for.js +37 -9
  14. package/tests/hydration/compiled/client/fragment-cursor.js +160 -41
  15. package/tests/hydration/compiled/client/head.js +3 -3
  16. package/tests/hydration/compiled/client/hmr.js +5 -2
  17. package/tests/hydration/compiled/client/html.js +66 -37
  18. package/tests/hydration/compiled/client/if-children.js +24 -7
  19. package/tests/hydration/compiled/client/if-fragment-controlflow.js +7 -7
  20. package/tests/hydration/compiled/client/if.js +5 -1
  21. package/tests/hydration/compiled/client/nested-control-flow.js +3 -3
  22. package/tests/hydration/compiled/client/portal.js +15 -4
  23. package/tests/hydration/compiled/client/reactivity.js +6 -2
  24. package/tests/hydration/compiled/client/streaming.js +4 -1
  25. package/tests/hydration/compiled/client/track-async-serialization.js +10 -2
  26. package/tests/hydration/compiled/client/try.js +4 -1
  27. package/tests/utils/inline-drift.test.js +83 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # ripple
2
2
 
3
+ ## 0.4.5
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1497](https://github.com/Ripple-TS/ripple/pull/1497)
8
+ [`2469ccf`](https://github.com/Ripple-TS/ripple/commit/2469ccfba1a6afad4af6658a777dc735767c5625)
9
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Trim the cold path of a first
10
+ mount: `template_el` builds nested templates and builds a master in the document
11
+ itself unless the compiler flagged a resource-loading element; Firefox is
12
+ detected without reading `navigator` (about 0.1 ms on a fresh page);
13
+ `Event.prototype.__root` is declared with the first delegated listener instead
14
+ of at mount; `set_attribute` skips the property-setter walk for `data-` and
15
+ `aria-` names; the first flush of a mounted app's effects runs in one phase;
16
+ `track()` and deferred component effects call one function less each; and a
17
+ mount before any delegated event is registered attaches no listeners.
18
+
19
+ New `set_attribute_value` and `set_property_value` helpers write an attribute
20
+ the compiler classified with the Web IDL setter table, so a standard element
21
+ never triggers the descriptor walk of `set_attribute`.
22
+
23
+ - Updated dependencies
24
+ [[`2469ccf`](https://github.com/Ripple-TS/ripple/commit/2469ccfba1a6afad4af6658a777dc735767c5625)]:
25
+ - @tsrx/ripple@0.2.4
26
+
3
27
  ## 0.4.4
4
28
 
5
29
  ### Patch Changes
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Ripple is an elegant TypeScript UI framework",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.4.4",
6
+ "version": "0.4.5",
7
7
  "type": "module",
8
8
  "sideEffects": false,
9
9
  "module": "src/runtime/index-client.js",
@@ -87,7 +87,7 @@
87
87
  "devalue": "^5.9.2",
88
88
  "esm-env": "^1.2.2",
89
89
  "@tsrx/core": "^0.2.3",
90
- "@tsrx/ripple": "0.2.3"
90
+ "@tsrx/ripple": "0.2.4"
91
91
  },
92
92
  "devDependencies": {
93
93
  "@types/estree": "^1.0.8",
@@ -80,7 +80,14 @@ export function set_style(element, value, prev = {}) {
80
80
  export function set_attribute(element, attribute, value) {
81
81
  if (value == null) {
82
82
  element.removeAttribute(attribute);
83
- } else if (typeof value !== 'string' && get_setters(element).includes(attribute)) {
83
+ } else if (
84
+ typeof value !== 'string' &&
85
+ // A `data-` or `aria-` name is never a property: the setter walk it
86
+ // would trigger reads every descriptor of the element's prototypes.
87
+ !attribute.startsWith('data-') &&
88
+ !attribute.startsWith('aria-') &&
89
+ get_setters(element).includes(attribute)
90
+ ) {
84
91
  /** @type {any} */ (element)[attribute] = value;
85
92
  } else {
86
93
  element.setAttribute(attribute, value);
@@ -536,6 +536,8 @@ export function delegate(events) {
536
536
  }
537
537
  }
538
538
 
539
+ var event_prototype_prepared = false;
540
+
539
541
  /**
540
542
  * @param {RootTargetRef} ref
541
543
  * @param {Iterable<string>} events
@@ -546,6 +548,14 @@ function register_root_events(ref, events) {
546
548
 
547
549
  for (var event_name of events) {
548
550
  if (registered_events.has(event_name)) continue;
551
+ if (!event_prototype_prepared) {
552
+ event_prototype_prepared = true;
553
+ // `__root` is read on every delegated event: declared on the
554
+ // prototype, the read stops at Event instead of walking to Object.
555
+ // Done with the first listener, so a mount before any delegated
556
+ // event is registered never touches `Event.prototype`.
557
+ Event.prototype.__root = undefined;
558
+ }
549
559
  registered_events.add(event_name);
550
560
 
551
561
  target.addEventListener(event_name, handle_event_propagation, {
@@ -571,7 +581,11 @@ export function handle_root_events(target) {
571
581
  if (ref === undefined) {
572
582
  ref = { n: 0, e: new Set(), t: target };
573
583
  root_target_refs.set(target, ref);
574
- register_root_events(ref, all_registered_events);
584
+ // Before any delegated event is registered (a first mount) there is
585
+ // nothing to attach.
586
+ if (all_registered_events.size > 0) {
587
+ register_root_events(ref, all_registered_events);
588
+ }
575
589
  }
576
590
 
577
591
  ref.n += 1;
@@ -18,6 +18,8 @@ export {
18
18
  set_value,
19
19
  set_checked,
20
20
  set_selected,
21
+ set_attribute_value,
22
+ set_property_value,
21
23
  } from './render.js';
22
24
 
23
25
  export {
@@ -32,9 +32,12 @@ export var is_firefox;
32
32
  export function init_operations() {
33
33
  var node_prototype = Node.prototype;
34
34
  var element_prototype = Element.prototype;
35
- var event_target_prototype = Event.prototype;
36
35
 
37
- is_firefox = /Firefox/.test(navigator.userAgent);
36
+ // Firefox prefers `importNode` for template content (see `template.js`).
37
+ // Its Firefox-only `window` property is the test: the first read of
38
+ // `navigator` creates its wrapper, about 0.1 ms on a fresh page, paid by
39
+ // the first mount.
40
+ is_firefox = 'mozInnerScreenX' in window;
38
41
  document = window.document;
39
42
 
40
43
  first_child_getter = /** @type {(() => Node | null)} */ (
@@ -47,9 +50,11 @@ export function init_operations() {
47
50
  get_descriptor(node_prototype, 'lastChild')?.get
48
51
  );
49
52
 
50
- // the following assignments improve perf of lookups on DOM nodes
53
+ // Declared on the prototype so a delegated handler lookup on an element
54
+ // stops at Element instead of walking to Object. `Event.prototype.__root`
55
+ // is declared with the first delegated listener (`events.js`): touching
56
+ // `Event.prototype` costs about 0.05 ms on a fresh page.
51
57
  element_prototype.__click = undefined;
52
- event_target_prototype.__root = undefined;
53
58
  }
54
59
 
55
60
  /**
@@ -165,3 +165,40 @@ export function set_selected(element, selected) {
165
165
  element.removeAttribute('selected');
166
166
  }
167
167
  }
168
+
169
+ /**
170
+ * Writes an attribute the compiler proved is not a property of its element
171
+ * (`dom-setters.js` in the compiler), so no setter is looked for: `null` and
172
+ * `undefined` remove it.
173
+ * @param {Element} element
174
+ * @param {string} attribute
175
+ * @param {any} value
176
+ * @returns {void}
177
+ */
178
+ export function set_attribute_value(element, attribute, value) {
179
+ if (value == null) {
180
+ element.removeAttribute(attribute);
181
+ } else {
182
+ element.setAttribute(attribute, value);
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Writes a name the compiler proved is a settable property of its element:
188
+ * a string sets the attribute, any other value the property, and `null` or
189
+ * `undefined` remove the attribute, as `set_attribute` decides at runtime
190
+ * after discovering the element's setters.
191
+ * @param {Element} element
192
+ * @param {string} attribute
193
+ * @param {any} value
194
+ * @returns {void}
195
+ */
196
+ export function set_property_value(element, attribute, value) {
197
+ if (value == null) {
198
+ element.removeAttribute(attribute);
199
+ } else if (typeof value !== 'string') {
200
+ /** @type {any} */ (element)[attribute] = value;
201
+ } else {
202
+ element.setAttribute(attribute, value);
203
+ }
204
+ }
@@ -4,7 +4,7 @@
4
4
  /** @typedef {DerivedValue} Derived */
5
5
 
6
6
  import { DEV } from 'esm-env';
7
- import { destroy_block, destroy_non_branch_children, effect } from './blocks.js';
7
+ import { block, destroy_block, destroy_non_branch_children } from './blocks.js';
8
8
  import {
9
9
  ASYNC_DERIVED_READ_THROWN,
10
10
  BLOCK_HAS_RUN,
@@ -796,7 +796,9 @@ export function track_read_only(value, block) {
796
796
  * @returns {Tracked | Derived}
797
797
  */
798
798
  export function track(v, b, hash, get, set) {
799
- if (is_ripple_object(v)) {
799
+ // is_ripple_object(), inline: track() is on every component's cold path
800
+ // (kept identical by tests/utils/inline-drift.test.js).
801
+ if (typeof v === 'object' && v !== null && typeof v.f === 'number') {
800
802
  return v;
801
803
  }
802
804
  if (b === null) {
@@ -1126,21 +1128,29 @@ function flush_queue(pending) {
1126
1128
  blocks.sort(by_block_id);
1127
1129
  }
1128
1130
 
1129
- var has_effects = false;
1131
+ var effect_kinds = 0;
1132
+ var has_render_blocks = false;
1130
1133
 
1131
1134
  for (var i = 0; i < length; i++) {
1132
1135
  var block = blocks[i];
1133
1136
  var flags = block.f;
1134
1137
  block.f = flags & ~SCHEDULED;
1135
- if ((flags & (PRE_EFFECT_BLOCK | EFFECT_BLOCK)) !== 0) {
1136
- has_effects = true;
1138
+ var kind = flags & (PRE_EFFECT_BLOCK | EFFECT_BLOCK);
1139
+ if (kind !== 0) {
1140
+ effect_kinds |= kind;
1141
+ } else {
1142
+ has_render_blocks = true;
1137
1143
  }
1138
1144
  }
1139
1145
 
1140
1146
  // New schedules land in the queue that replaced `pending`, so the array
1141
- // can be run in place. Effects need the three-phase ordering; a queue of
1142
- // only render blocks (the common flush) runs straight through.
1143
- if (has_effects) {
1147
+ // can be run in place. A queue mixing kinds needs the three-phase
1148
+ // ordering; one of a single kind (the common flush of render blocks,
1149
+ // the first flush of a mounted app's effects) runs straight through.
1150
+ if (
1151
+ effect_kinds === (PRE_EFFECT_BLOCK | EFFECT_BLOCK) ||
1152
+ (effect_kinds !== 0 && has_render_blocks)
1153
+ ) {
1144
1154
  run_phases(blocks, length);
1145
1155
  } else {
1146
1156
  run_phase(blocks);
@@ -1853,7 +1863,9 @@ function create_deferred_effects(effects) {
1853
1863
  for (var i = 0; i < length; i += 3) {
1854
1864
  active_block = /** @type {Block} */ (effects[i + 1]);
1855
1865
  active_reaction = /** @type {Block | Derived | null} */ (effects[i + 2]);
1856
- effect(/** @type {Function} */ (effects[i]));
1866
+ // effect(), inline: one function less on every component's cold path
1867
+ // (kept identical by tests/utils/inline-drift.test.js).
1868
+ block(EFFECT_BLOCK, /** @type {Function} */ (effects[i]));
1857
1869
  }
1858
1870
  active_block = previous_block;
1859
1871
  active_reaction = previous_reaction;
@@ -153,19 +153,74 @@ export function template(content, flags = 0, count = 1) {
153
153
  var inert_document;
154
154
 
155
155
  /**
156
- * A template that is one HTML element with static attributes and at most one
157
- * text child, built with DOM calls instead of parsed: a `<template>` parse
158
- * has a fixed cost that dwarfs the element itself, and an app's first render
159
- * pays it once per distinct template. The compiler decides which templates
160
- * qualify (`template_el` in the client transform); every other shape still
161
- * parses. The element is created in the inert template document and in the
162
- * active namespace, as a parsed template would be.
156
+ * A nested element of a DOM-built template: its tag, attributes and children,
157
+ * trailing absences left out.
158
+ * @typedef {[string, (string[] | null)?, (string | TemplateChildren)?]} TemplateElementSpec
159
+ */
160
+
161
+ /**
162
+ * The children of a DOM-built template element in order: a nested element,
163
+ * a text string, or `null` for a placeholder comment.
164
+ * @typedef {Array<string | null | TemplateElementSpec>} TemplateChildren
165
+ */
166
+
167
+ /**
168
+ * Builds one element of a DOM-built template, in the inert document and
169
+ * the active namespace, as a parsed template would create it.
170
+ * @param {Document} doc
171
+ * @param {keyof typeof NAMESPACE_URI} ns
172
+ * @param {string} tag
173
+ * @param {string[] | null | undefined} attributes
174
+ * @param {string | TemplateChildren | undefined} children
175
+ * @returns {Element}
176
+ */
177
+ function build_template_element(doc, ns, tag, attributes, children) {
178
+ var node =
179
+ ns === DEFAULT_NAMESPACE ? doc.createElement(tag) : doc.createElementNS(NAMESPACE_URI[ns], tag);
180
+ if (attributes != null) {
181
+ for (var i = 0; i < attributes.length; i += 2) {
182
+ node.setAttribute(attributes[i], attributes[i + 1]);
183
+ }
184
+ }
185
+ if (children === undefined || typeof children === 'string') {
186
+ if (children !== undefined && children !== '') {
187
+ node.textContent = children;
188
+ }
189
+ } else {
190
+ for (var j = 0; j < children.length; j++) {
191
+ var child = children[j];
192
+ node.appendChild(
193
+ child === null
194
+ ? doc.createComment('')
195
+ : typeof child === 'string'
196
+ ? doc.createTextNode(child)
197
+ : build_template_element(doc, ns, child[0], child[1], child[2]),
198
+ );
199
+ }
200
+ }
201
+ return node;
202
+ }
203
+
204
+ /**
205
+ * A template of a few HTML elements with static attributes, built with DOM
206
+ * calls instead of parsed: a `<template>` parse has a fixed cost that dwarfs
207
+ * the elements themselves, and an app's first render pays it once per
208
+ * distinct template, plus the parser's own start-up on a fresh page. The
209
+ * compiler decides which templates qualify (`dom_built_template` in the
210
+ * client transform); every other shape still parses. The nodes are created
211
+ * in the active namespace, as a parsed template would be, and in the inert
212
+ * template document when one of them could load a resource (`inert`), also
213
+ * as parsed; any other master is built in the document itself, which skips
214
+ * creating the inert document on a fresh page.
163
215
  * @param {string} tag
164
216
  * @param {string[] | null} [attributes] - flat name/value pairs
165
- * @param {string} [text]
217
+ * @param {string | TemplateChildren} [children] - one text child, or the
218
+ * child nodes in order
219
+ * @param {number} [inert] - 1 when an element could load a resource from an
220
+ * attribute the master carries
166
221
  * @returns {() => Node}
167
222
  */
168
- export function template_el(tag, attributes = null, text = '') {
223
+ export function template_el(tag, attributes = null, children = '', inert = 0) {
169
224
  /** @type {Element | undefined} */
170
225
  var node;
171
226
  /** @type {string | undefined} */
@@ -177,19 +232,11 @@ export function template_el(tag, attributes = null, text = '') {
177
232
  }
178
233
  var ns = active_namespace;
179
234
  if (node === undefined || node_ns !== ns) {
180
- var doc = (inert_document ??= document.createElement('template').content.ownerDocument);
181
- node =
182
- ns === DEFAULT_NAMESPACE
183
- ? doc.createElement(tag)
184
- : doc.createElementNS(NAMESPACE_URI[ns], tag);
185
- if (attributes !== null) {
186
- for (var i = 0; i < attributes.length; i += 2) {
187
- node.setAttribute(attributes[i], attributes[i + 1]);
188
- }
189
- }
190
- if (text !== '') {
191
- node.textContent = text;
192
- }
235
+ var doc =
236
+ inert === 0
237
+ ? document
238
+ : (inert_document ??= document.createElement('template').content.ownerDocument);
239
+ node = build_template_element(doc, ns, tag, attributes, children);
193
240
  node_ns = ns;
194
241
  }
195
242
  var clone = is_firefox ? document.importNode(node, true) : node.cloneNode(true);
@@ -30,7 +30,13 @@ function MultipleElements_render(__anchor, __block) {
30
30
 
31
31
  MultipleElements[_$_.$r] = MultipleElements_render;
32
32
 
33
- var root_3 = _$_.template(`<div class=outer><div class=inner><span>Nested content`);
33
+ var root_3 = _$_.template_el('div', ['class', 'outer'], [
34
+ [
35
+ 'div',
36
+ ['class', 'inner'],
37
+ [['span', null, 'Nested content']]
38
+ ]
39
+ ]);
34
40
 
35
41
  function NestedElements_render(__anchor, __block) {
36
42
  var div_1 = root_3();
@@ -239,7 +245,7 @@ function NestedTsxTsrxFragment_render(__anchor, __block, { label }) {
239
245
  NestedTsxTsrxFragment[_$_.$r] = NestedTsxTsrxFragment_render;
240
246
 
241
247
  var root_19 = _$_.template_el('div', ['class', 'app-item'], ' ');
242
- var root_18 = _$_.template(`<div class=nested-expression-values><!>`);
248
+ var root_18 = _$_.template_el('div', ['class', 'nested-expression-values'], [null]);
243
249
 
244
250
  function NestedTsxTsrxExpressionValues_render(__anchor, __block) {
245
251
  var div_8 = root_18();
@@ -279,7 +285,7 @@ NestedTsxTsrxExpressionValues[_$_.$r] = NestedTsxTsrxExpressionValues_render;
279
285
  var root_20 = _$_.template_el('strong', ['class', 'middle'], 'beta');
280
286
  var root_21 = _$_.template_el('em', ['class', 'tail'], 'epsilon');
281
287
  var root_22 = _$_.template(` `, 1, 1);
282
- var root_23 = _$_.template(`<div class=mixed-collection><!>`);
288
+ var root_23 = _$_.template_el('div', ['class', 'mixed-collection'], [null]);
283
289
 
284
290
  function MixedTsrxCollectionText_render(__anchor, __block) {
285
291
  const content = _$_.tsrx_element((__anchor, __block) => {
@@ -325,7 +331,7 @@ MixedTsrxCollectionText[_$_.$r] = MixedTsrxCollectionText_render;
325
331
  var root_24 = _$_.template_el('strong', ['class', 'middle'], 'beta');
326
332
  var root_25 = _$_.template_el('em', ['class', 'tail'], 'epsilon');
327
333
  var root_26 = _$_.template(` `, 1, 1);
328
- var root_27 = _$_.template(`<div class=mixed-collection-split><!>`);
334
+ var root_27 = _$_.template_el('div', ['class', 'mixed-collection-split'], [null]);
329
335
 
330
336
  function MixedTsrxCollectionSplitServerText_render(__anchor, __block) {
331
337
  const content = _$_.tsrx_element((__anchor, __block) => {
@@ -371,7 +377,7 @@ MixedTsrxCollectionSplitServerText[_$_.$r] = MixedTsrxCollectionSplitServerText_
371
377
  var root_28 = _$_.template_el('strong', ['class', 'middle'], 'beta');
372
378
  var root_29 = _$_.template_el('em', ['class', 'tail'], 'epsilon');
373
379
  var root_30 = _$_.template(` `, 1, 1);
374
- var root_31 = _$_.template(`<div class=mixed-collection-split><!>`);
380
+ var root_31 = _$_.template_el('div', ['class', 'mixed-collection-split'], [null]);
375
381
 
376
382
  function MixedTsrxCollectionSplitClientText_render(__anchor, __block) {
377
383
  const content = _$_.tsrx_element((__anchor, __block) => {
@@ -416,7 +422,7 @@ MixedTsrxCollectionSplitClientText[_$_.$r] = MixedTsrxCollectionSplitClientText_
416
422
 
417
423
  var root_32 = _$_.template_el('span', ['class', 'primitive-tail'], ' ok');
418
424
  var root_33 = _$_.template(` `, 1, 1);
419
- var root_34 = _$_.template(`<div class=mixed-collection-primitive><!>`);
425
+ var root_34 = _$_.template_el('div', ['class', 'mixed-collection-primitive'], [null]);
420
426
 
421
427
  function MixedTsrxCollectionPrimitiveServerText_render(__anchor, __block) {
422
428
  const content = _$_.tsrx_element((__anchor, __block) => {
@@ -454,7 +460,7 @@ MixedTsrxCollectionPrimitiveServerText[_$_.$r] = MixedTsrxCollectionPrimitiveSer
454
460
 
455
461
  var root_35 = _$_.template_el('span', ['class', 'primitive-tail'], ' ok');
456
462
  var root_36 = _$_.template(` `, 1, 1);
457
- var root_37 = _$_.template(`<div class=mixed-collection-primitive><!>`);
463
+ var root_37 = _$_.template_el('div', ['class', 'mixed-collection-primitive'], [null]);
458
464
 
459
465
  function MixedTsrxCollectionPrimitiveClientText_render(__anchor, __block) {
460
466
  const content = _$_.tsrx_element((__anchor, __block) => {
@@ -568,7 +574,7 @@ function DynamicArrayFromLogical_render(__anchor, __block) {
568
574
 
569
575
  DynamicArrayFromLogical[_$_.$r] = DynamicArrayFromLogical_render;
570
576
 
571
- var root_42 = _$_.template(`<section class=outer><div class=inner>from tsrx`);
577
+ var root_42 = _$_.template_el('section', ['class', 'outer'], [['div', ['class', 'inner'], 'from tsrx']]);
572
578
  var root_44 = _$_.template(`<!>`, 1, 1);
573
579
  var root_43 = _$_.template(`<!>`, 1, 1);
574
580
 
@@ -595,7 +601,14 @@ function NestedTsrxInsideTopLevelTsxExpression_render(__anchor, __block) {
595
601
 
596
602
  NestedTsrxInsideTopLevelTsxExpression[_$_.$r] = NestedTsrxInsideTopLevelTsxExpression_render;
597
603
 
598
- var root_45 = _$_.template(`<div class=wrapper><section class=native><span class=nested-tsrx>inside nested tsrx`);
604
+ var root_45 = _$_.template_el('div', ['class', 'wrapper'], [
605
+ [
606
+ 'section',
607
+ ['class', 'native'],
608
+ [['span', ['class', 'nested-tsrx'], 'inside nested tsrx']]
609
+ ]
610
+ ]);
611
+
599
612
  var root_47 = _$_.template(`<!>`, 1, 1);
600
613
  var root_46 = _$_.template(`<!>`, 1, 1);
601
614
 
@@ -827,7 +840,21 @@ function Header_render(__anchor, __block) {
827
840
  Header[_$_.$r] = Header_render;
828
841
 
829
842
  var root_65 = _$_.template_el('a', ['href', '/playground', 'class', 'playground-link'], 'Playground');
830
- var root_64 = _$_.template(`<div class=social-links><a href=https://github.com class=github-link>GitHub</a><a href=https://discord.com class=discord-link>Discord</a><!>`);
843
+
844
+ var root_64 = _$_.template_el('div', ['class', 'social-links'], [
845
+ [
846
+ 'a',
847
+ ['href', 'https://github.com', 'class', 'github-link'],
848
+ 'GitHub'
849
+ ],
850
+
851
+ [
852
+ 'a',
853
+ ['href', 'https://discord.com', 'class', 'discord-link'],
854
+ 'Discord'
855
+ ],
856
+ null
857
+ ]);
831
858
 
832
859
  function Actions_render(__anchor, __block, { playgroundVisible = false }) {
833
860
  var div_23 = root_64();
@@ -853,7 +880,7 @@ function Actions_render(__anchor, __block, { playgroundVisible = false }) {
853
880
 
854
881
  Actions[_$_.$r] = Actions_render;
855
882
 
856
- var root_66 = _$_.template(`<main><div class=container><!>`);
883
+ var root_66 = _$_.template_el('main', null, [['div', ['class', 'container'], [null]]]);
857
884
 
858
885
  function Layout_render(__anchor, __block, { children }) {
859
886
  var main = root_66();
@@ -874,7 +901,7 @@ function Layout_render(__anchor, __block, { children }) {
874
901
 
875
902
  Layout[_$_.$r] = Layout_render;
876
903
 
877
- var root_67 = _$_.template(`<div class=content><p>Some content here`);
904
+ var root_67 = _$_.template_el('div', ['class', 'content'], [['p', null, 'Some content here']]);
878
905
 
879
906
  function Content_render(__anchor, __block) {
880
907
  var div_25 = root_67();
@@ -922,7 +949,7 @@ function LastChild_render(__anchor, __block) {
922
949
 
923
950
  LastChild[_$_.$r] = LastChild_render;
924
951
 
925
- var root_70 = _$_.template(`<div class=wrapper><h1>Header</h1><p>Some content`);
952
+ var root_70 = _$_.template_el('div', ['class', 'wrapper'], [['h1', null, 'Header'], ['p', null, 'Some content']]);
926
953
 
927
954
  function ComponentAsLastSibling_render(__anchor, __block) {
928
955
  var div_26 = root_70();
@@ -941,7 +968,7 @@ function ComponentAsLastSibling_render(__anchor, __block) {
941
968
 
942
969
  ComponentAsLastSibling[_$_.$r] = ComponentAsLastSibling_render;
943
970
 
944
- var root_71 = _$_.template(`<div class=inner><span>Inner text`);
971
+ var root_71 = _$_.template_el('div', ['class', 'inner'], [['span', null, 'Inner text']]);
945
972
 
946
973
  function InnerContent_render(__anchor, __block) {
947
974
  var div_27 = root_71();
@@ -959,7 +986,7 @@ function InnerContent_render(__anchor, __block) {
959
986
 
960
987
  InnerContent[_$_.$r] = InnerContent_render;
961
988
 
962
- var root_72 = _$_.template(`<section class=outer><h2>Section title`);
989
+ var root_72 = _$_.template_el('section', ['class', 'outer'], [['h2', null, 'Section title']]);
963
990
 
964
991
  function NestedComponentAsLastSibling_render(__anchor, __block) {
965
992
  var section_1 = root_72();
@@ -1017,7 +1044,7 @@ function FragmentTailExpression_render(__anchor, __block) {
1017
1044
 
1018
1045
  FragmentTailExpression[_$_.$r] = FragmentTailExpression_render;
1019
1046
 
1020
- var root_75 = _$_.template(`<div>frag-<span>tail`);
1047
+ var root_75 = _$_.template_el('div', null, ['frag-', ['span', null, 'tail']]);
1021
1048
 
1022
1049
  function FragmentChildOnly_render(__anchor, __block) {
1023
1050
  var div_30 = root_75();
@@ -1,7 +1,7 @@
1
1
  // @ts-nocheck
2
2
  import * as _$_ from 'ripple/internal/client';
3
3
 
4
- var root = _$_.template(`<div class=layout><!>`);
4
+ var root = _$_.template_el('div', ['class', 'layout'], [null]);
5
5
 
6
6
  function Layout_render(__anchor, __block, { children }) {
7
7
  var div = root();
@@ -18,7 +18,7 @@ function Layout_render(__anchor, __block, { children }) {
18
18
 
19
19
  Layout[_$_.$r] = Layout_render;
20
20
 
21
- var root_1 = _$_.template(`<div class=layout>before<!>after`);
21
+ var root_1 = _$_.template_el('div', ['class', 'layout'], ['before', null, 'after']);
22
22
 
23
23
  function TextWrappedLayout_render(__anchor, __block, { children }) {
24
24
  var div_1 = root_1();
@@ -1,7 +1,10 @@
1
1
  // @ts-nocheck
2
2
  import * as _$_ from 'ripple/internal/client';
3
3
 
4
- var root = _$_.template(`<div><button class=increment>Increment</button><span class=count>`);
4
+ var root = _$_.template_el('div', null, [
5
+ ['button', ['class', 'increment'], 'Increment'],
6
+ ['span', ['class', 'count']]
7
+ ]);
5
8
 
6
9
  function render(__prev) {
7
10
  var __a = __prev._a.value;
@@ -32,7 +35,11 @@ function ClickCounter_render(__anchor, __block) {
32
35
 
33
36
  ClickCounter[_$_.$r] = ClickCounter_render;
34
37
 
35
- var root_1 = _$_.template(`<div><button class=decrement>-</button><span class=count></span><button class=increment>+`);
38
+ var root_1 = _$_.template_el('div', null, [
39
+ ['button', ['class', 'decrement'], '-'],
40
+ ['span', ['class', 'count']],
41
+ ['button', ['class', 'increment'], '+']
42
+ ]);
36
43
 
37
44
  function render_1(__prev) {
38
45
  var __a = __prev._a.value;
@@ -68,7 +75,11 @@ function IncrementDecrement_render(__anchor, __block) {
68
75
 
69
76
  IncrementDecrement[_$_.$r] = IncrementDecrement_render;
70
77
 
71
- var root_2 = _$_.template(`<div><button class=target>Target</button><span class=clicks></span><span class=hovers>`);
78
+ var root_2 = _$_.template_el('div', null, [
79
+ ['button', ['class', 'target'], 'Target'],
80
+ ['span', ['class', 'clicks']],
81
+ ['span', ['class', 'hovers']]
82
+ ]);
72
83
 
73
84
  function render_2(__prev) {
74
85
  var __a = __prev._a.value;
@@ -112,7 +123,11 @@ function MultipleEvents_render(__anchor, __block) {
112
123
 
113
124
  MultipleEvents[_$_.$r] = MultipleEvents_render;
114
125
 
115
- var root_3 = _$_.template(`<div><button class=btn>Click</button><span class=count></span><span class=action> `);
126
+ var root_3 = _$_.template_el('div', null, [
127
+ ['button', ['class', 'btn'], 'Click'],
128
+ ['span', ['class', 'count']],
129
+ ['span', ['class', 'action'], ' ']
130
+ ]);
116
131
 
117
132
  function render_3(__prev) {
118
133
  var __a = __prev._a.value;
@@ -156,7 +171,7 @@ function MultiStateUpdate_render(__anchor, __block) {
156
171
 
157
172
  MultiStateUpdate[_$_.$r] = MultiStateUpdate_render;
158
173
 
159
- var root_4 = _$_.template(`<div><button class=toggle>`);
174
+ var root_4 = _$_.template_el('div', null, [['button', ['class', 'toggle']]]);
160
175
 
161
176
  function render_4(__prev) {
162
177
  var __a = __prev._a.value ? 'ON' : 'OFF';
@@ -206,7 +221,7 @@ function ChildButton_render(__anchor, __block, props) {
206
221
 
207
222
  ChildButton[_$_.$r] = ChildButton_render;
208
223
 
209
- var root_6 = _$_.template(`<div><span class=count>`);
224
+ var root_6 = _$_.template_el('div', null, [['span', ['class', 'count']]]);
210
225
 
211
226
  function render_6(__prev) {
212
227
  var __a = __prev._a.value;