ripple 0.2.87 → 0.2.89

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.
@@ -9,38 +9,38 @@ import { IF_BLOCK, UNINITIALIZED } from './constants.js';
9
9
  * @returns {void}
10
10
  */
11
11
  export function if_block(node, fn) {
12
- var anchor = node;
13
- var has_branch = false;
14
- /** @type {any} */
15
- var condition = UNINITIALIZED;
16
- /** @type {Block | null} */
17
- var b = null;
12
+ var anchor = node;
13
+ var has_branch = false;
14
+ /** @type {any} */
15
+ var condition = UNINITIALIZED;
16
+ /** @type {Block | null} */
17
+ var b = null;
18
18
 
19
- /** @type {(fn: (anchor: Node) => void, flag?: boolean) => void} */
20
- var set_branch = (fn, flag = true) => {
21
- has_branch = true;
22
- update_branch(flag, fn);
23
- };
19
+ /** @type {(fn: (anchor: Node) => void, flag?: boolean) => void} */
20
+ var set_branch = (fn, flag = true) => {
21
+ has_branch = true;
22
+ update_branch(flag, fn);
23
+ };
24
24
 
25
- /** @type {(new_condition: any, fn: ((anchor: Node) => void) | null) => void} */
26
- var update_branch = (new_condition, fn) => {
27
- if (condition === (condition = new_condition)) return;
25
+ /** @type {(new_condition: any, fn: ((anchor: Node) => void) | null) => void} */
26
+ var update_branch = (new_condition, fn) => {
27
+ if (condition === (condition = new_condition)) return;
28
28
 
29
- if (b !== null) {
30
- destroy_block(b);
31
- b = null;
32
- }
29
+ if (b !== null) {
30
+ destroy_block(b);
31
+ b = null;
32
+ }
33
33
 
34
- if (fn !== null) {
35
- b = branch(() => fn(anchor));
36
- }
37
- };
34
+ if (fn !== null) {
35
+ b = branch(() => fn(anchor));
36
+ }
37
+ };
38
38
 
39
- render(() => {
40
- has_branch = false;
41
- fn(set_branch);
42
- if (!has_branch) {
43
- update_branch(null, null);
44
- }
45
- }, IF_BLOCK);
39
+ render(() => {
40
+ has_branch = false;
41
+ fn(set_branch);
42
+ if (!has_branch) {
43
+ update_branch(null, null);
44
+ }
45
+ }, IF_BLOCK);
46
46
  }
@@ -60,4 +60,8 @@ export { template, append } from './template.js';
60
60
 
61
61
  export { tracked_array } from '../../array.js';
62
62
 
63
+ export { tracked_object } from '../../object.js';
64
+
63
65
  export { head } from './head.js';
66
+
67
+ export { script } from './script.js';
@@ -14,7 +14,6 @@ export var is_firefox;
14
14
  export function init_operations() {
15
15
  var node_prototype = Node.prototype;
16
16
  var element_prototype = Element.prototype;
17
- var object_prototype = Object.prototype;
18
17
  var event_target_prototype = EventTarget.prototype;
19
18
 
20
19
  is_firefox = /Firefox/.test(navigator.userAgent);
@@ -15,6 +15,7 @@ import {
15
15
  is_event_attribute,
16
16
  } from '../../../utils/events.js';
17
17
  import { get } from './runtime.js';
18
+ import { clsx } from 'clsx';
18
19
 
19
20
  /**
20
21
  * @param {Text} text
@@ -131,18 +132,17 @@ export function set_attributes(element, attributes) {
131
132
  }
132
133
 
133
134
  /**
134
- * @template V
135
- * @param {V} value
135
+ * @param {import('clsx').ClassValue} value
136
136
  * @param {string} [hash]
137
- * @returns {string | V}
137
+ * @returns {string}
138
138
  */
139
139
  function to_class(value, hash) {
140
- return (value == null ? '' : value) + (hash ? ' ' + hash : '');
140
+ return value == null ? hash ?? '' : clsx([value, hash]);
141
141
  }
142
142
 
143
143
  /**
144
144
  * @param {HTMLElement} dom
145
- * @param {string} value
145
+ * @param {import('clsx').ClassValue} value
146
146
  * @param {string} [hash]
147
147
  * @param {boolean} [is_html]
148
148
  * @returns {void}
@@ -325,7 +325,7 @@ export function track(v, get, set, b) {
325
325
  * @param {Block} b
326
326
  * @returns {Tracked[]}
327
327
  */
328
- export function trackSplit(v, l, b) {
328
+ export function track_split(v, l, b) {
329
329
  var is_tracked = is_tracked_object(v);
330
330
 
331
331
  if (is_tracked || typeof v !== 'object' || v === null || is_array(v)) {
@@ -1089,7 +1089,7 @@ export function exclude_from_object(obj, exclude_keys) {
1089
1089
  var keys = object_keys(obj);
1090
1090
  /** @type {Record<string | symbol, unknown>} */
1091
1091
  var new_obj = {};
1092
-
1092
+
1093
1093
  for (const key of keys) {
1094
1094
  if (!exclude_keys.includes(key)) {
1095
1095
  new_obj[key] = obj[key];
@@ -0,0 +1,16 @@
1
+ import { effect } from './blocks';
2
+ /**
3
+ * @param {Text | Comment} node
4
+ * @param {string} content
5
+ */
6
+ export function script(node, content) {
7
+ effect(() => {
8
+ var script = document.createElement('script');
9
+ script.textContent = content;
10
+ node.before(script);
11
+
12
+ return () => {
13
+ script.remove();
14
+ };
15
+ });
16
+ }
@@ -20,6 +20,10 @@ export var object_keys = Object.keys;
20
20
  export var get_own_property_symbols = Object.getOwnPropertySymbols;
21
21
  /** @type {typeof structuredClone} */
22
22
  export var structured_clone = structuredClone;
23
+ /** @type {typeof Object.prototype} */
24
+ export var object_prototype = Object.prototype;
25
+ /** @type {typeof Array.prototype} */
26
+ export var array_prototype = Array.prototype;
23
27
 
24
28
  /**
25
29
  * Creates a text node that serves as an anchor point in the DOM.
@@ -6,6 +6,8 @@ import { is_tracked_object } from '../client/utils';
6
6
  import { escape } from '../../../utils/escaping.js';
7
7
  import { is_boolean_attribute } from '../../../compiler/utils';
8
8
 
9
+ import { clsx } from 'clsx';
10
+
9
11
  export { escape };
10
12
 
11
13
  /** @type {Component | null} */
@@ -132,7 +134,8 @@ export function attr(name, value, is_boolean = false) {
132
134
  }
133
135
  if (value == null || (!value && is_boolean)) return '';
134
136
  const normalized = (name in replacements && replacements[name].get(value)) || value;
135
- const assignment = is_boolean ? '' : `="${escape(normalized, true)}"`;
137
+ const value_to_escape = name === 'class' ? clsx(normalized) : normalized;
138
+ const assignment = is_boolean ? '' : `="${escape(value_to_escape, true)}"`;
136
139
  return ` ${name}${assignment}`;
137
140
  }
138
141
 
@@ -155,7 +158,7 @@ export function spread_attrs(attrs, css_hash) {
155
158
  }
156
159
 
157
160
  if (name === 'class' && css_hash) {
158
- value = (value == null ? '' : value) + ' ' + css_hash;
161
+ value = value == null ? css_hash : [value, css_hash];
159
162
  }
160
163
 
161
164
  attr_str += attr(name, value, is_boolean_attribute(name));
@@ -0,0 +1,29 @@
1
+ /** @import { Block } from '#client' */
2
+ import { safe_scope } from './internal/client/runtime.js';
3
+ import { object_proxy } from './proxy.js';
4
+
5
+ /**
6
+ * @template {object} T
7
+ * @constructor
8
+ * @param {T} obj
9
+ * @returns {TrackedObject<T>}
10
+ */
11
+ export function TrackedObject(obj) {
12
+ if (!new.target) {
13
+ throw new Error("TrackedObject must be called with 'new'");
14
+ }
15
+
16
+ var block = safe_scope();
17
+
18
+ return object_proxy(obj, block);
19
+ }
20
+
21
+ /**
22
+ * @template {object} T
23
+ * @param {T} obj
24
+ * @param {Block} block
25
+ * @returns {TrackedObject<T>}
26
+ */
27
+ export function tracked_object(obj, block) {
28
+ return object_proxy(obj, block);
29
+ }
@@ -0,0 +1,341 @@
1
+ /** @import { Block, Tracked } from '#client' */
2
+ /** @import { TrackedArray } from './array.js' */
3
+ /** @import { TrackedObject } from './object.js' */
4
+
5
+ import { get, set, tracked } from './internal/client/runtime.js';
6
+ import {
7
+ array_prototype,
8
+ get_descriptor,
9
+ get_prototype_of,
10
+ is_array,
11
+ object_prototype,
12
+ } from './internal/client/utils.js';
13
+ import {
14
+ MAX_ARRAY_LENGTH,
15
+ TRACKED_ARRAY,
16
+ TRACKED_OBJECT,
17
+ UNINITIALIZED,
18
+ } from './internal/client/constants.js';
19
+
20
+ /**
21
+ * @template T
22
+ * @param {T[] | Record<PropertyKey, any>} value
23
+ * @param {Block} block
24
+ * @returns {TrackedArray<T> | TrackedObject<T>}
25
+ */
26
+ export function proxy(value, block) {
27
+ // if non-proxyable, or is already a proxy, return `value`
28
+ if (
29
+ typeof value !== 'object'
30
+ || value === null
31
+ || TRACKED_ARRAY in value
32
+ || TRACKED_OBJECT in value
33
+ ) {
34
+ return value;
35
+ }
36
+
37
+ const prototype = get_prototype_of(value);
38
+
39
+ if (prototype !== object_prototype && prototype !== array_prototype) {
40
+ return value;
41
+ }
42
+
43
+ var tracked_elements = new Map();
44
+ var is_proxied_array = is_array(value);
45
+ /** @type {Tracked} */
46
+ var tracked_len;
47
+
48
+ if (is_proxied_array) {
49
+ tracked_len = tracked(value.length, block);
50
+ tracked_elements.set('length', tracked_len);
51
+ }
52
+
53
+ return new Proxy(value, {
54
+ /**
55
+ * @param {PropertyKey} prop
56
+ */
57
+ get(target, prop, receiver) {
58
+ var t = tracked_elements.get(prop);
59
+ var exists = prop in target;
60
+
61
+ if (t === undefined && (!exists || get_descriptor(target, prop)?.writable)) {
62
+ t = tracked(exists ? /** @type {any} */ (target)[prop] : UNINITIALIZED, block);
63
+ tracked_elements.set(prop, t);
64
+ }
65
+
66
+ if (t !== undefined) {
67
+ var v = get(t);
68
+ return v === UNINITIALIZED ? undefined : v;
69
+ }
70
+
71
+ var result = Reflect.get(target, prop, receiver);
72
+
73
+ if (typeof result === 'function') {
74
+ if (methods_returning_arrays.has(prop)) {
75
+ /** @type {(this: any, ...args: any[]) => any} */
76
+ return function (...args) {
77
+ var output = Reflect.apply(result, receiver, args);
78
+
79
+ if (Array.isArray(output) && output !== target) {
80
+ return array_proxy({ elements: output, block, use_array: true });
81
+ }
82
+
83
+ return output;
84
+ };
85
+ }
86
+
87
+ // When generating an iterator, we need to ensure that length is tracked
88
+ if (is_proxied_array && (prop === 'entries' || prop === 'values' || prop === 'keys')) {
89
+ receiver.length;
90
+ }
91
+ }
92
+
93
+ return result;
94
+ },
95
+
96
+ set(target, prop, value, receiver) {
97
+ var t = tracked_elements.get(prop);
98
+ var exists = prop in target;
99
+
100
+ if (is_proxied_array && prop === 'length') {
101
+ for (var i = value; i < t.v; i += 1) {
102
+ var other_t = tracked_elements.get(i + '');
103
+ if (other_t !== undefined) {
104
+ set(other_t, UNINITIALIZED, block);
105
+ } else if (i in target) {
106
+ // If the item exists in the original, we need to create a uninitialized tracked,
107
+ // else a later read of the property would result in a tracked being created with
108
+ // the value of the original item at that index.
109
+ other_t = tracked(UNINITIALIZED, block);
110
+ tracked_elements.set(i + '', other_t);
111
+ }
112
+ }
113
+ }
114
+
115
+ // If we haven't yet created a tracked for this property, we need to ensure
116
+ // we do so otherwise if we read it later, then the write won't be tracked and
117
+ // the heuristics of effects will be different vs if we had read the proxied
118
+ // object property before writing to that property.
119
+ if (t === undefined) {
120
+ if (!exists || get_descriptor(target, prop)?.writable) {
121
+ t = tracked(undefined, block);
122
+ set(t, value, block);
123
+
124
+ tracked_elements.set(prop, t);
125
+ }
126
+ } else {
127
+ exists = t.v !== UNINITIALIZED;
128
+
129
+ set(t, value, block);
130
+ }
131
+
132
+ var descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
133
+
134
+ // Set the new value before updating any tracked's so that any listeners get the new value
135
+ if (descriptor?.set) {
136
+ descriptor.set.call(receiver, value);
137
+ }
138
+
139
+ if (!exists && is_proxied_array && typeof prop === 'string' ) {
140
+ // If we have mutated an array directly, we might need to
141
+ // signal that length has also changed. Do it before updating metadata
142
+ // to ensure that iterating over the array as a result of a metadata update
143
+ // will not cause the length to be out of sync.
144
+ var n = Number(prop);
145
+
146
+ if (Number.isInteger(n) && n >= tracked_len.v) {
147
+ set(tracked_len, n + 1, block);
148
+ }
149
+ }
150
+
151
+ return true;
152
+ },
153
+
154
+ setPrototypeOf() {
155
+ throw new Error(`Cannot set prototype of ${is_proxied_array ? '\`TrackedArray\`' : '\`TrackedObject\`'}`);
156
+ },
157
+
158
+ deleteProperty(target, prop) {
159
+ var t = tracked_elements.get(prop);
160
+
161
+ if (t === undefined) {
162
+ if (prop in target) {
163
+ const t = tracked(UNINITIALIZED, block);
164
+ tracked_elements.set(prop, t);
165
+ }
166
+ } else {
167
+ set(t, UNINITIALIZED, block);
168
+ }
169
+
170
+ return Reflect.deleteProperty(target, prop);
171
+ },
172
+
173
+ has(target, prop) {
174
+ if (is_proxied_array && prop === TRACKED_ARRAY) {
175
+ return true;
176
+ }
177
+
178
+ if (prop === TRACKED_OBJECT) {
179
+ return true;
180
+ }
181
+
182
+ var t = tracked_elements.get(prop);
183
+ var exists = (t !== undefined && t.v !== UNINITIALIZED) || Reflect.has(target, prop);
184
+
185
+ if (t !== undefined || !exists || get_descriptor(target, prop)?.writable) {
186
+ if (t === undefined) {
187
+ t = tracked(exists ? /** @type {any} */ (target)[prop] : UNINITIALIZED, block);
188
+
189
+ tracked_elements.set(prop, t);
190
+ }
191
+
192
+ var value = get(t);
193
+ if (value === UNINITIALIZED) {
194
+ return false;
195
+ }
196
+ }
197
+
198
+ return exists;
199
+ },
200
+
201
+ defineProperty(_, prop, descriptor) {
202
+ if (
203
+ !('value' in descriptor) ||
204
+ descriptor.configurable === false ||
205
+ descriptor.enumerable === false ||
206
+ descriptor.writable === false
207
+ ) {
208
+ // we disallow non-basic descriptors, because unless they are applied to the
209
+ // target object — which we avoid, so that state can be forked — we will run
210
+ // afoul of the various invariants
211
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/getOwnPropertyDescriptor#invariants
212
+ throw new Error(
213
+ 'Only basic property descriptors are supported with value and configurable, enumerable, and writable set to true',
214
+ );
215
+ }
216
+
217
+ var t = tracked_elements.get(prop);
218
+
219
+ if (t === undefined) {
220
+ t = tracked(descriptor.value, block);
221
+ tracked_elements.set(prop, t);
222
+ } else {
223
+ set(t, descriptor.value, block);
224
+ }
225
+
226
+ return true;
227
+ },
228
+
229
+ ownKeys(target) {
230
+ var own_keys = Reflect.ownKeys(target).filter((key) => {
231
+ var t = tracked_elements.get(key);
232
+ return t === undefined || t.v !== UNINITIALIZED;
233
+ });
234
+
235
+ for (var [key, t] of tracked_elements) {
236
+ if (t.v !== UNINITIALIZED && !(key in target)) {
237
+ own_keys.push(key);
238
+ }
239
+ }
240
+
241
+ return own_keys;
242
+ },
243
+
244
+ getOwnPropertyDescriptor(target, prop) {
245
+ var descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
246
+
247
+ if (descriptor && 'value' in descriptor) {
248
+ var t = tracked_elements.get(prop);
249
+ if (t) descriptor.value = get(t);
250
+ } else if (descriptor === undefined) {
251
+ var t = tracked_elements.get(prop);
252
+ var value = t?.v;
253
+
254
+ if (t !== undefined && value !== UNINITIALIZED) {
255
+ return {
256
+ enumerable: true,
257
+ configurable: true,
258
+ value,
259
+ writable: true
260
+ };
261
+ }
262
+ }
263
+
264
+ return descriptor;
265
+ },
266
+
267
+ });
268
+ }
269
+
270
+ /**
271
+ * @template T
272
+ * @param {{
273
+ * elements: Iterable<T>,
274
+ * block: Block,
275
+ * from_static?: boolean,
276
+ * use_array?: boolean
277
+ * }} params
278
+ * @returns {TrackedArray<T>}
279
+ */
280
+ export function array_proxy({ elements, block, from_static = false, use_array = false }) {
281
+ var arr;
282
+ var first;
283
+
284
+ if (
285
+ from_static &&
286
+ (first = get_first_if_length(/** @type {Array<T>} */ (elements))) !== undefined
287
+ ) {
288
+ arr = new Array();
289
+ arr[0] = first;
290
+ } else if (use_array) {
291
+ arr = elements;
292
+ } else {
293
+ arr = new Array(...elements);
294
+ }
295
+
296
+ return proxy(arr, block);
297
+ }
298
+
299
+ /**
300
+ * @template {object} T
301
+ * @param {T} obj
302
+ * @param {Block} block
303
+ * @returns {TrackedObject<T>}
304
+ */
305
+ export function object_proxy(obj, block) {
306
+ return proxy(obj, block);
307
+ }
308
+
309
+ /** @type {Set<PropertyKey>} */
310
+ const methods_returning_arrays = new Set([
311
+ 'concat',
312
+ 'filter',
313
+ 'flat',
314
+ 'flatMap',
315
+ 'map',
316
+ 'slice',
317
+ 'splice',
318
+ 'toReversed',
319
+ 'toSorted',
320
+ 'toSpliced',
321
+ 'with',
322
+ ]);
323
+
324
+ /**
325
+ * @template T
326
+ * @param {Array<T>} array
327
+ * @returns {number | void}
328
+ */
329
+ function get_first_if_length(array) {
330
+ var first = array[0];
331
+
332
+ if (
333
+ array.length === 1 &&
334
+ 0 in array &&
335
+ Number.isInteger(first) &&
336
+ /** @type {number} */ (first) >= 0 &&
337
+ /** @type {number} */ (first) <= MAX_ARRAY_LENGTH
338
+ ) {
339
+ return /** @type {number} */ (first);
340
+ }
341
+ }
@@ -58,11 +58,11 @@ export function is_capture_event(name) {
58
58
  * @param {string} event_name
59
59
  */
60
60
  export function get_attribute_event_name(event_name) {
61
- event_name = event_name.slice(2);
61
+ event_name = event_name.slice(2); // strip "on"
62
62
  if (is_capture_event(event_name)) {
63
- event_name = event_name.slice(0, -7);
63
+ event_name = event_name.slice(0, -7); // strip "Capture"
64
64
  }
65
- return event_name[0].toLowerCase() + event_name.slice(1);
65
+ return event_name.toLowerCase();
66
66
  }
67
67
 
68
68
  const PASSIVE_EVENTS = ['touchstart', 'touchmove'];
@@ -37,6 +37,19 @@ exports[`basic client > handles boolean attributes with no prop value provides 1
37
37
  </div>
38
38
  `;
39
39
 
40
+ exports[`basic client > handles boolean props correctly 1`] = `
41
+ <div>
42
+ <div
43
+ data-disabled=""
44
+ />
45
+ <input
46
+ disabled=""
47
+ />
48
+ <!---->
49
+
50
+ </div>
51
+ `;
52
+
40
53
  exports[`basic client > render semi-dynamic text 1`] = `
41
54
  <div>
42
55
  <div>
@@ -7,7 +7,7 @@ describe('TrackedArray', () => {
7
7
 
8
8
  function render(component) {
9
9
  mount(component, {
10
- target: container
10
+ target: container
11
11
  });
12
12
  }
13
13
 
@@ -877,7 +877,7 @@ describe('TrackedArray', () => {
877
877
  expect(container.querySelectorAll('pre')[1].textContent).toBe('[5,4,3,2,1]');
878
878
  });
879
879
 
880
- it('handles toSorted method with reactivity', () => {
880
+ it('handles toSorted method with reactivity', (context) => {
881
881
  if (!('toSorted' in Array.prototype)) {
882
882
  context.skip();
883
883
  }
@@ -907,7 +907,7 @@ describe('TrackedArray', () => {
907
907
  expect(container.querySelectorAll('pre')[1].textContent).toBe('[0,1,2,3,4]');
908
908
  });
909
909
 
910
- it('handles toSpliced method with reactivity', () => {
910
+ it('handles toSpliced method with reactivity', (context) => {
911
911
  if (!('toSpliced' in Array.prototype)) {
912
912
  context.skip();
913
913
  }