ripple 0.2.83 → 0.2.84

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.
@@ -25,7 +25,7 @@ export function mount(component, options) {
25
25
  if (target.firstChild) {
26
26
  target.textContent = '';
27
27
  }
28
-
28
+
29
29
  target.append(anchor);
30
30
 
31
31
  const cleanup_events = handle_root_events(target);
@@ -42,7 +42,13 @@ export function mount(component, options) {
42
42
 
43
43
  export { create_context as createContext } from './internal/client/context.js';
44
44
 
45
- export { flush_sync as flushSync, track, untrack, deferred } from './internal/client/runtime.js';
45
+ export {
46
+ flush_sync as flushSync,
47
+ track,
48
+ trackSplit,
49
+ untrack,
50
+ deferred,
51
+ } from './internal/client/runtime.js';
46
52
 
47
53
  export { TrackedArray } from './array.js';
48
54
 
@@ -7,14 +7,15 @@ export var TRY_BLOCK = 1 << 6;
7
7
  export var IF_BLOCK = 1 << 7;
8
8
  export var COMPOSITE_BLOCK = 1 << 8;
9
9
  export var ASYNC_BLOCK = 1 << 9;
10
- export var CONTAINS_UPDATE = 1 << 10;
11
- export var CONTAINS_TEARDOWN = 1 << 11;
12
- export var BLOCK_HAS_RUN = 1 << 12;
13
- export var TRACKED = 1 << 13;
14
- export var DERIVED = 1 << 14;
15
- export var DEFERRED = 1 << 15;
16
- export var PAUSED = 1 << 16;
17
- export var DESTROYED = 1 << 17;
10
+ export var HEAD_BLOCK = 1 << 10;
11
+ export var CONTAINS_UPDATE = 1 << 11;
12
+ export var CONTAINS_TEARDOWN = 1 << 12;
13
+ export var BLOCK_HAS_RUN = 1 << 13;
14
+ export var TRACKED = 1 << 14;
15
+ export var DERIVED = 1 << 15;
16
+ export var DEFERRED = 1 << 16;
17
+ export var PAUSED = 1 << 17;
18
+ export var DESTROYED = 1 << 18;
18
19
 
19
20
  export var CONTROL_FLOW_BLOCK = FOR_BLOCK | IF_BLOCK | TRY_BLOCK | COMPOSITE_BLOCK;
20
21
 
@@ -0,0 +1,14 @@
1
+ import { render } from './blocks.js';
2
+ import { HEAD_BLOCK } from './constants.js';
3
+ import { create_text } from './operations.js';
4
+
5
+ /**
6
+ * @param {(anchor: Node) => void} render_fn
7
+ * @returns {void}
8
+ */
9
+ export function head(render_fn) {
10
+ /** @type {Comment | Text} */
11
+ var anchor = document.head.appendChild(create_text());
12
+
13
+ render(() => render_fn(anchor), HEAD_BLOCK);
14
+ }
@@ -1,12 +1,17 @@
1
- export { first_child as child, child_frag, next_sibling as sibling } from './operations.js';
1
+ export {
2
+ first_child as child,
3
+ child_frag,
4
+ next_sibling as sibling,
5
+ document,
6
+ } from './operations.js';
2
7
 
3
8
  export {
4
- set_text,
5
- set_class,
6
- set_attribute,
7
- set_value,
8
- set_checked,
9
- set_selected,
9
+ set_text,
10
+ set_class,
11
+ set_attribute,
12
+ set_value,
13
+ set_checked,
14
+ set_selected,
10
15
  } from './render.js';
11
16
 
12
17
  export { render, render_spread, async, ref } from './blocks.js';
@@ -14,33 +19,33 @@ export { render, render_spread, async, ref } from './blocks.js';
14
19
  export { event, delegate } from './events.js';
15
20
 
16
21
  export {
17
- active_block,
18
- scope,
19
- safe_scope,
20
- with_scope,
21
- get,
22
- get_tracked,
23
- get_derived,
24
- set,
25
- async_computed,
26
- tracked,
27
- spread_props,
28
- computed_property,
29
- call_property,
30
- get_property,
31
- set_property,
32
- update,
33
- update_pre,
34
- update_property,
35
- update_pre_property,
36
- push_component,
37
- pop_component,
38
- untrack,
39
- ref_prop,
40
- fallback,
41
- exclude_from_object,
42
- derived,
43
- maybe_tracked,
22
+ active_block,
23
+ scope,
24
+ safe_scope,
25
+ with_scope,
26
+ get,
27
+ get_tracked,
28
+ get_derived,
29
+ set,
30
+ async_computed,
31
+ tracked,
32
+ spread_props,
33
+ computed_property,
34
+ call_property,
35
+ get_property,
36
+ set_property,
37
+ update,
38
+ update_pre,
39
+ update_property,
40
+ update_pre_property,
41
+ push_component,
42
+ pop_component,
43
+ untrack,
44
+ ref_prop,
45
+ fallback,
46
+ exclude_from_object,
47
+ derived,
48
+ maybe_tracked,
44
49
  } from './runtime.js';
45
50
 
46
51
  export { composite } from './composite.js';
@@ -53,4 +58,6 @@ export { try_block as try, aborted } from './try.js';
53
58
 
54
59
  export { template, append } from './template.js';
55
60
 
56
- export { tracked_array } from '../../array.js';
61
+ export { tracked_array } from '../../array.js';
62
+
63
+ export { head } from './head.js';
@@ -5,34 +5,38 @@ var first_child_getter;
5
5
  /** @type {() => Node | null} */
6
6
  var next_sibling_getter;
7
7
 
8
+ /** @type {Document} */
9
+ export var document;
10
+
8
11
  export var is_firefox;
9
12
 
10
13
  export function init_operations() {
11
- var node_prototype = Node.prototype;
12
- var element_prototype = Element.prototype;
13
- var object_prototype = Object.prototype;
14
- var event_target_prototype = EventTarget.prototype;
14
+ var node_prototype = Node.prototype;
15
+ var element_prototype = Element.prototype;
16
+ var object_prototype = Object.prototype;
17
+ var event_target_prototype = EventTarget.prototype;
15
18
 
16
- is_firefox = /Firefox/.test(navigator.userAgent);
19
+ is_firefox = /Firefox/.test(navigator.userAgent);
20
+ document = window.document;
17
21
 
18
- // @ts-ignore
19
- first_child_getter = get_descriptor(node_prototype, 'firstChild').get;
20
- // @ts-ignore
21
- next_sibling_getter = get_descriptor(node_prototype, 'nextSibling').get;
22
+ // @ts-ignore
23
+ first_child_getter = get_descriptor(node_prototype, 'firstChild').get;
24
+ // @ts-ignore
25
+ next_sibling_getter = get_descriptor(node_prototype, 'nextSibling').get;
22
26
 
23
- // the following assignments improve perf of lookups on DOM nodes
24
- // @ts-expect-error
25
- element_prototype.__click = undefined;
26
- // @ts-expect-error
27
- element_prototype.__className = '';
28
- // @ts-expect-error
29
- element_prototype.__attributes = null;
30
- // @ts-expect-error
31
- element_prototype.__styles = null;
32
- // @ts-expect-error
33
- element_prototype.__e = undefined;
34
- // @ts-expect-error
35
- event_target_prototype.__root = undefined;
27
+ // the following assignments improve perf of lookups on DOM nodes
28
+ // @ts-expect-error
29
+ element_prototype.__click = undefined;
30
+ // @ts-expect-error
31
+ element_prototype.__className = '';
32
+ // @ts-expect-error
33
+ element_prototype.__attributes = null;
34
+ // @ts-expect-error
35
+ element_prototype.__styles = null;
36
+ // @ts-expect-error
37
+ element_prototype.__e = undefined;
38
+ // @ts-expect-error
39
+ event_target_prototype.__root = undefined;
36
40
  }
37
41
 
38
42
  /**
@@ -42,16 +46,16 @@ export function init_operations() {
42
46
  */
43
47
  /*@__NO_SIDE_EFFECTS__*/
44
48
  export function first_child(node) {
45
- return first_child_getter.call(node);
49
+ return first_child_getter.call(node);
46
50
  }
47
51
 
48
52
  export function child_frag(node) {
49
- var child = first_child(node);
53
+ var child = first_child(node);
50
54
 
51
- if (child.nodeType === 8 && child.data === '') {
52
- return next_sibling(child);
53
- }
54
- return child;
55
+ if (child.nodeType === 8 && child.data === '') {
56
+ return next_sibling(child);
57
+ }
58
+ return child;
55
59
  }
56
60
 
57
61
  /**
@@ -61,9 +65,9 @@ export function child_frag(node) {
61
65
  */
62
66
  /*@__NO_SIDE_EFFECTS__*/
63
67
  export function next_sibling(node) {
64
- return next_sibling_getter.call(node);
68
+ return next_sibling_getter.call(node);
65
69
  }
66
70
 
67
71
  export function create_text(value = '') {
68
- return document.createTextNode(value);
72
+ return document.createTextNode(value);
69
73
  }
@@ -289,36 +289,35 @@ export function derived(fn, block, get, set) {
289
289
 
290
290
  /**
291
291
  * @param {any} v
292
- * @param {TrackOptions<any> | undefined} o
292
+ * @param {Function | undefined} get
293
+ * @param {Function | undefined} set
293
294
  * @param {Block} b
294
- * @returns {Tracked | Derived | Tracked[]}
295
+ * @returns {Tracked | Derived}
295
296
  */
296
- export function track(v, o, b) {
297
- var is_tracked = is_tracked_object(v);
298
- var get;
299
- var set;
300
-
301
- if (o !== undefined) {
302
- get = o.get;
303
- set = o.set;
297
+ export function track(v, get, set, b) {
298
+ if (is_tracked_object(v)) {
299
+ return v;
304
300
  }
305
301
 
306
- if (o === undefined || get || set) {
307
- if (is_tracked) {
308
- return v;
309
- }
310
-
311
- if (typeof v === 'function') {
312
- return derived(v, b, get, set);
313
- }
314
- return tracked(v, b, get, set);
302
+ if (typeof v === 'function') {
303
+ return derived(v, b, get, set);
315
304
  }
305
+ return tracked(v, b, get, set);
306
+ }
307
+
308
+ /**
309
+ * @param {Record<string|symbol, any>} v
310
+ * @param {(symbol | string)[]} l
311
+ * @param {Block} b
312
+ * @returns {Tracked[]}
313
+ */
314
+ export function trackSplit(v, l, b) {
315
+ var is_tracked = is_tracked_object(v);
316
316
 
317
317
  if (is_tracked || typeof v !== 'object' || v === null || is_array(v)) {
318
318
  throw new TypeError('Invalid value: expected a non-tracked object');
319
319
  }
320
320
 
321
- var list = o.split ?? [];
322
321
  /** @type {Tracked[]} */
323
322
  var out = [];
324
323
  /** @type {Record<string|symbol, any>} */
@@ -326,8 +325,8 @@ export function track(v, o, b) {
326
325
  /** @type {Record<PropertyKey, any | null>} */
327
326
  var descriptors = get_descriptors(v);
328
327
 
329
- for (let i = 0, key, t, exists = true; i < list.length; i++) {
330
- key = list[i];
328
+ for (let i = 0, key, t, exists = true; i < l.length; i++) {
329
+ key = l[i];
331
330
 
332
331
  if (is_tracked_object(v[key])) {
333
332
  t = v[key];
@@ -775,7 +774,7 @@ export function get_tracked(tracked) {
775
774
  }
776
775
  var get = tracked.a.get;
777
776
  if (get) {
778
- tracked.v = get(value);
777
+ value = get(value);
779
778
  }
780
779
  return value;
781
780
  }
@@ -791,10 +790,6 @@ export function set(tracked, value, block) {
791
790
  if (value !== old_value) {
792
791
  var tracked_block = tracked.b;
793
792
 
794
- if (!tracked_block) {
795
- debugger;
796
- }
797
-
798
793
  if ((block.f & CONTAINS_TEARDOWN) !== 0) {
799
794
  if (teardown) {
800
795
  old_values.set(tracked, value);
@@ -1,6 +1,7 @@
1
1
  import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { mount, flushSync, effect, track } from 'ripple';
2
+ import { mount, flushSync, effect, track, trackSplit } from 'ripple';
3
3
  import { compile } from 'ripple/compiler';
4
+ import { TRACKED_ARRAY } from '../../src/runtime/internal/client/constants.js';
4
5
 
5
6
  describe('basic client', () => {
6
7
  let container;
@@ -1271,7 +1272,7 @@ describe('basic client', () => {
1271
1272
  expect(container).toMatchSnapshot();
1272
1273
  });
1273
1274
 
1274
- it('can retain reactivity for destructure rest via track split', () => {
1275
+ it('can retain reactivity for destructure rest via track trackSplit', () => {
1275
1276
  let logs = [];
1276
1277
 
1277
1278
  component App() {
@@ -1296,7 +1297,7 @@ describe('basic client', () => {
1296
1297
  }
1297
1298
 
1298
1299
  component Child(props: PropsWithChildren<{ count: Tracked<number> }>) {
1299
- const [children, count, rest] = track(props, {split: ['children', 'count']});
1300
+ const [children, count, rest] = trackSplit(props, ['children', 'count']);
1300
1301
 
1301
1302
  if (@count < 2) {
1302
1303
  <button {...@rest}><@children /></button>
@@ -1329,12 +1330,12 @@ describe('basic client', () => {
1329
1330
  expect(logs).toEqual(['ref called','cleanup ref']);
1330
1331
  });
1331
1332
 
1332
- it('errors on invalid value as null for track with split', () => {
1333
+ it('errors on invalid value as null for track with trackSplit', () => {
1333
1334
  component App() {
1334
1335
  let message = track('');
1335
1336
 
1336
1337
  try{
1337
- const [a, b, rest] = track(null, { split: ['a', 'b'] });
1338
+ const [a, b, rest] = trackSplit(null, ['a', 'b']);
1338
1339
  } catch(e) {
1339
1340
  @message = e.message;
1340
1341
  }
@@ -1348,12 +1349,12 @@ describe('basic client', () => {
1348
1349
  expect(pre.textContent).toBe('Invalid value: expected a non-tracked object');
1349
1350
  });
1350
1351
 
1351
- it('errors on invalid value as array for track with split', () => {
1352
+ it('errors on invalid value as array for track with trackSplit', () => {
1352
1353
  component App() {
1353
1354
  let message = track('');
1354
1355
 
1355
1356
  try{
1356
- const [a, b, rest] = track([1, 2, 3], { split: ['a', 'b'] });
1357
+ const [a, b, rest] = trackSplit([1, 2, 3], ['a', 'b']);
1357
1358
  } catch(e) {
1358
1359
  @message = e.message;
1359
1360
  }
@@ -1367,13 +1368,13 @@ describe('basic client', () => {
1367
1368
  expect(pre.textContent).toBe('Invalid value: expected a non-tracked object');
1368
1369
  });
1369
1370
 
1370
- it('errors on invalid value as tracked for track with split', () => {
1371
+ it('errors on invalid value as tracked for track with trackSplit', () => {
1371
1372
  component App() {
1372
1373
  const t = track({a: 1, b: 2, c: 3});
1373
1374
  let message = track('');
1374
1375
 
1375
1376
  try{
1376
- const [a, b, rest] = track(t, { split: ['a', 'b'] });
1377
+ const [a, b, rest] = trackSplit(t, ['a', 'b']);
1377
1378
  } catch(e) {
1378
1379
  @message = e.message;
1379
1380
  }
@@ -1387,9 +1388,9 @@ describe('basic client', () => {
1387
1388
  expect(pre.textContent).toBe('Invalid value: expected a non-tracked object');
1388
1389
  });
1389
1390
 
1390
- it('returns undefined for non-existent props in track with split', () => {
1391
+ it('returns undefined for non-existent props in track with trackSplit', () => {
1391
1392
  component App() {
1392
- const [a, b, rest] = track({a: 1, c: 1}, { split: ['a', 'b'] });
1393
+ const [a, b, rest] = trackSplit({a: 1, c: 1}, ['a', 'b']);
1393
1394
 
1394
1395
  <pre>{@a}</pre>
1395
1396
  <pre>{String(@b)}</pre>
@@ -1433,4 +1434,97 @@ describe('basic client', () => {
1433
1434
  render(App);
1434
1435
  expect(container).toMatchSnapshot();
1435
1436
  });
1437
+
1438
+ it('uses track get and set where both mutate value', () => {
1439
+ component App() {
1440
+ let count = track(0, v => v + 1, v => v * 2);
1441
+
1442
+
1443
+ <div class='count'>{@count}</div>
1444
+ <button onClick={() => { @count++ }}>{'Increment'}</button>
1445
+ }
1446
+
1447
+ render(App);
1448
+
1449
+ const countDiv = container.querySelector('.count');
1450
+ const button = container.querySelector('button');
1451
+
1452
+ expect(countDiv.textContent).toBe('1');
1453
+
1454
+ button.click();
1455
+ flushSync();
1456
+ expect(countDiv.textContent).toBe('5');
1457
+ });
1458
+
1459
+ it('uses track get and set where set only mutates value', () => {
1460
+ component App() {
1461
+ let count = track(1, v => v, v => v * 2);
1462
+
1463
+
1464
+ <div class='count'>{@count}</div>
1465
+ <button onClick={() => { @count++ }}>{'Increment'}</button>
1466
+ }
1467
+
1468
+ render(App);
1469
+
1470
+ const countDiv = container.querySelector('.count');
1471
+ const button = container.querySelector('button');
1472
+
1473
+ expect(countDiv.textContent).toBe('1');
1474
+
1475
+ button.click();
1476
+ flushSync();
1477
+ expect(countDiv.textContent).toBe('4');
1478
+ });
1479
+
1480
+ it('uses track get and set where get only mutates value', () => {
1481
+ component App() {
1482
+ let count = track(0, v => v + 1, v => v);
1483
+
1484
+
1485
+ <div class='count'>{@count}</div>
1486
+ <button onClick={() => { @count++ }}>{'Increment'}</button>
1487
+ }
1488
+
1489
+ render(App);
1490
+
1491
+ const countDiv = container.querySelector('.count');
1492
+ const button = container.querySelector('button');
1493
+
1494
+ expect(countDiv.textContent).toBe('1');
1495
+
1496
+ button.click();
1497
+ flushSync();
1498
+ expect(countDiv.textContent).toBe('3');
1499
+ });
1500
+
1501
+ it('works as a reactive TrackedArray when constructed using # syntactic sugar', () => {
1502
+ component App() {
1503
+ const array = #[1, 2, 3];
1504
+
1505
+ <pre>{String(array[3])}</pre>
1506
+ <pre>{array[0]}</pre>
1507
+ <pre>{TRACKED_ARRAY in array}</pre>
1508
+
1509
+ <button onClick={() => { array.push(array.length + 1); array[0] = array[0] + 1 }}>{'Add'}</button>
1510
+ }
1511
+
1512
+ render(App);
1513
+
1514
+ const pre1 = container.querySelectorAll('pre')[0];
1515
+ const pre2 = container.querySelectorAll('pre')[1];
1516
+ const pre3 = container.querySelectorAll('pre')[2];
1517
+ const button = container.querySelector('button');
1518
+
1519
+ expect(pre1.textContent).toBe('undefined');
1520
+ expect(pre2.textContent).toBe('1');
1521
+ expect(pre3.textContent).toBe('true');
1522
+
1523
+ button.click();
1524
+ flushSync();
1525
+
1526
+ expect(pre1.textContent).toBe('4');
1527
+ expect(pre2.textContent).toBe('2');
1528
+ });
1436
1529
  });
1530
+
package/types/index.d.ts CHANGED
@@ -76,26 +76,21 @@ export type PropsWithExtras<T extends object> = Props & T & Record<string, unkno
76
76
  export type PropsWithChildren<T extends object = {}> =
77
77
  Expand<Omit<Props, 'children'> & { children: Component } & T>;
78
78
 
79
- type UnwrapTracked<T> = [T] extends [Tracked<infer V>] ? T : Tracked<T>;
80
-
81
- // force ts to evaluate and expand a type fully
82
79
  type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
83
80
 
84
81
  type PickKeys<T, K extends readonly (keyof T)[]> =
85
- { [I in keyof K]: UnwrapTracked<T[K[I] & keyof T]> };
82
+ { [I in keyof K]: Tracked<T[K[I] & keyof T]> };
86
83
 
87
84
  type RestKeys<T, K extends readonly (keyof T)[]> = Expand<Omit<T, K[number]>>;
88
85
 
89
86
  type SplitResult<T extends Props, K extends readonly (keyof T)[]> =
90
- [...PickKeys<T, K>, UnwrapTracked<RestKeys<T, K>>];
91
-
92
- type TrackOptions<T> = { split?: readonly (string | number | symbol)[], get?: (v: T) => T, set?: (v: T) => T };
87
+ [...PickKeys<T, K>, Tracked<RestKeys<T, K>>];
93
88
 
94
- export declare function track<V>(value?: V | (() => V)): Tracked<V>;
89
+ export declare function track<V>(value?: V | (() => V), get?: (v: V) => V, set?: (v: V) => V): Tracked<V>;
95
90
 
96
- export declare function track<V extends Props, const K extends readonly (keyof V)[]>(
97
- value: V,
98
- options: TrackOptions<V>
91
+ export declare function trackSplit<V extends Props, const K extends readonly (keyof V)[]>(
92
+ value: V,
93
+ splitKeys: K,
99
94
  ): SplitResult<V, K>;
100
95
 
101
96
  export function on<Type extends keyof WindowEventMap>(