ripple 0.2.82 → 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.
- package/package.json +1 -1
- package/src/compiler/phases/1-parse/index.js +1151 -1062
- package/src/compiler/phases/2-analyze/index.js +561 -523
- package/src/compiler/phases/3-transform/client/index.js +105 -28
- package/src/compiler/phases/3-transform/server/index.js +1 -1
- package/src/compiler/utils.js +613 -595
- package/src/runtime/array.js +4 -0
- package/src/runtime/index-client.js +8 -2
- package/src/runtime/internal/client/constants.js +9 -8
- package/src/runtime/internal/client/head.js +14 -0
- package/src/runtime/internal/client/index.js +43 -34
- package/src/runtime/internal/client/operations.js +34 -30
- package/src/runtime/internal/client/runtime.js +30 -29
- package/src/utils/builders.js +1 -0
- package/tests/client/basic.test.ripple +130 -22
- package/types/index.d.ts +6 -11
package/src/runtime/array.js
CHANGED
|
@@ -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 {
|
|
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
|
|
11
|
-
export var
|
|
12
|
-
export var
|
|
13
|
-
export var
|
|
14
|
-
export var
|
|
15
|
-
export var
|
|
16
|
-
export var
|
|
17
|
-
export var
|
|
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 {
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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';
|
|
@@ -52,3 +57,7 @@ export { if_block as if } from './if.js';
|
|
|
52
57
|
export { try_block as try, aborted } from './try.js';
|
|
53
58
|
|
|
54
59
|
export { template, append } from './template.js';
|
|
60
|
+
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
19
|
+
is_firefox = /Firefox/.test(navigator.userAgent);
|
|
20
|
+
document = window.document;
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
49
|
+
return first_child_getter.call(node);
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
export function child_frag(node) {
|
|
49
|
-
|
|
53
|
+
var child = first_child(node);
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
68
|
+
return next_sibling_getter.call(node);
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
export function create_text(value = '') {
|
|
68
|
-
|
|
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 {
|
|
292
|
+
* @param {Function | undefined} get
|
|
293
|
+
* @param {Function | undefined} set
|
|
293
294
|
* @param {Block} b
|
|
294
|
-
* @returns {Tracked | Derived
|
|
295
|
+
* @returns {Tracked | Derived}
|
|
295
296
|
*/
|
|
296
|
-
export function track(v,
|
|
297
|
-
|
|
298
|
-
|
|
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 (
|
|
307
|
-
|
|
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,17 +325,23 @@ 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; i <
|
|
330
|
-
key =
|
|
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];
|
|
334
333
|
} else {
|
|
335
|
-
t =
|
|
334
|
+
t = tracked(undefined, b);
|
|
335
|
+
exists = !!descriptors[key];
|
|
336
|
+
if (exists) {
|
|
337
|
+
t = define_property(t, 'v', descriptors[key]);
|
|
338
|
+
}
|
|
336
339
|
}
|
|
337
340
|
|
|
338
341
|
out[i] = t;
|
|
339
|
-
|
|
342
|
+
if (exists) {
|
|
343
|
+
descriptors[key] = null;
|
|
344
|
+
}
|
|
340
345
|
}
|
|
341
346
|
|
|
342
347
|
var props = Reflect.ownKeys(descriptors);
|
|
@@ -769,7 +774,7 @@ export function get_tracked(tracked) {
|
|
|
769
774
|
}
|
|
770
775
|
var get = tracked.a.get;
|
|
771
776
|
if (get) {
|
|
772
|
-
|
|
777
|
+
value = get(value);
|
|
773
778
|
}
|
|
774
779
|
return value;
|
|
775
780
|
}
|
|
@@ -785,10 +790,6 @@ export function set(tracked, value, block) {
|
|
|
785
790
|
if (value !== old_value) {
|
|
786
791
|
var tracked_block = tracked.b;
|
|
787
792
|
|
|
788
|
-
if (!tracked_block) {
|
|
789
|
-
debugger;
|
|
790
|
-
}
|
|
791
|
-
|
|
792
793
|
if ((block.f & CONTAINS_TEARDOWN) !== 0) {
|
|
793
794
|
if (teardown) {
|
|
794
795
|
old_values.set(tracked, value);
|
package/src/utils/builders.js
CHANGED
|
@@ -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;
|
|
@@ -379,8 +380,7 @@ describe('basic client', () => {
|
|
|
379
380
|
expect(container.querySelector('.count').textContent).toBe('0');
|
|
380
381
|
});
|
|
381
382
|
|
|
382
|
-
|
|
383
|
-
it.skip('renders multiple reactive lexical blocks with complexity', () => {
|
|
383
|
+
it('renders multiple reactive lexical blocks with complexity', () => {
|
|
384
384
|
|
|
385
385
|
component Basic() {
|
|
386
386
|
const count = 'count';
|
|
@@ -1272,16 +1272,11 @@ describe('basic client', () => {
|
|
|
1272
1272
|
expect(container).toMatchSnapshot();
|
|
1273
1273
|
});
|
|
1274
1274
|
|
|
1275
|
-
it('can retain reactivity for destructure rest via track
|
|
1275
|
+
it('can retain reactivity for destructure rest via track trackSplit', () => {
|
|
1276
1276
|
let logs = [];
|
|
1277
1277
|
|
|
1278
1278
|
component App() {
|
|
1279
|
-
let count = track(0
|
|
1280
|
-
set(v) {
|
|
1281
|
-
logs.push('inside setter');
|
|
1282
|
-
return v;
|
|
1283
|
-
}
|
|
1284
|
-
});
|
|
1279
|
+
let count = track(0);
|
|
1285
1280
|
let name = track('Click Me');
|
|
1286
1281
|
|
|
1287
1282
|
function buttonRef(el) {
|
|
@@ -1294,19 +1289,20 @@ describe('basic client', () => {
|
|
|
1294
1289
|
<Child
|
|
1295
1290
|
class="my-button"
|
|
1296
1291
|
onClick={() => @name === 'Click Me' ? @name = 'Clicked' : @name = 'Click Me'}
|
|
1297
|
-
{count}
|
|
1292
|
+
{@count}
|
|
1298
1293
|
{ref buttonRef}
|
|
1299
|
-
>{@name}</Child
|
|
1294
|
+
>{@name}</Child>
|
|
1295
|
+
|
|
1296
|
+
<button onClick={() => @count++}>{'Increment Count'}</button>
|
|
1300
1297
|
}
|
|
1301
1298
|
|
|
1302
1299
|
component Child(props: PropsWithChildren<{ count: Tracked<number> }>) {
|
|
1303
|
-
const [children, count, rest] =
|
|
1300
|
+
const [children, count, rest] = trackSplit(props, ['children', 'count']);
|
|
1304
1301
|
|
|
1305
1302
|
if (@count < 2) {
|
|
1306
1303
|
<button {...@rest}><@children /></button>
|
|
1307
1304
|
}
|
|
1308
1305
|
<pre>{@count}</pre>
|
|
1309
|
-
<button onClick={() => @count++}>{'Increment Count'}</button>
|
|
1310
1306
|
}
|
|
1311
1307
|
|
|
1312
1308
|
render(App);
|
|
@@ -1327,20 +1323,19 @@ describe('basic client', () => {
|
|
|
1327
1323
|
|
|
1328
1324
|
expect(buttonClickMe.textContent).toBe('Clicked');
|
|
1329
1325
|
expect(countPre.textContent).toBe('1');
|
|
1330
|
-
expect(logs).toEqual(['ref called','inside setter']);
|
|
1331
1326
|
|
|
1332
1327
|
buttonIncrement.click();
|
|
1333
1328
|
flushSync();
|
|
1334
1329
|
|
|
1335
|
-
expect(logs).toEqual(['ref called','
|
|
1330
|
+
expect(logs).toEqual(['ref called','cleanup ref']);
|
|
1336
1331
|
});
|
|
1337
1332
|
|
|
1338
|
-
it('errors on invalid value as null for track with
|
|
1333
|
+
it('errors on invalid value as null for track with trackSplit', () => {
|
|
1339
1334
|
component App() {
|
|
1340
1335
|
let message = track('');
|
|
1341
1336
|
|
|
1342
1337
|
try{
|
|
1343
|
-
const [a, b, rest] =
|
|
1338
|
+
const [a, b, rest] = trackSplit(null, ['a', 'b']);
|
|
1344
1339
|
} catch(e) {
|
|
1345
1340
|
@message = e.message;
|
|
1346
1341
|
}
|
|
@@ -1354,12 +1349,12 @@ describe('basic client', () => {
|
|
|
1354
1349
|
expect(pre.textContent).toBe('Invalid value: expected a non-tracked object');
|
|
1355
1350
|
});
|
|
1356
1351
|
|
|
1357
|
-
it('errors on invalid value as array for track with
|
|
1352
|
+
it('errors on invalid value as array for track with trackSplit', () => {
|
|
1358
1353
|
component App() {
|
|
1359
1354
|
let message = track('');
|
|
1360
1355
|
|
|
1361
1356
|
try{
|
|
1362
|
-
const [a, b, rest] =
|
|
1357
|
+
const [a, b, rest] = trackSplit([1, 2, 3], ['a', 'b']);
|
|
1363
1358
|
} catch(e) {
|
|
1364
1359
|
@message = e.message;
|
|
1365
1360
|
}
|
|
@@ -1373,13 +1368,13 @@ describe('basic client', () => {
|
|
|
1373
1368
|
expect(pre.textContent).toBe('Invalid value: expected a non-tracked object');
|
|
1374
1369
|
});
|
|
1375
1370
|
|
|
1376
|
-
it('errors on invalid value as tracked for track with
|
|
1371
|
+
it('errors on invalid value as tracked for track with trackSplit', () => {
|
|
1377
1372
|
component App() {
|
|
1378
1373
|
const t = track({a: 1, b: 2, c: 3});
|
|
1379
1374
|
let message = track('');
|
|
1380
1375
|
|
|
1381
1376
|
try{
|
|
1382
|
-
const [a, b, rest] =
|
|
1377
|
+
const [a, b, rest] = trackSplit(t, ['a', 'b']);
|
|
1383
1378
|
} catch(e) {
|
|
1384
1379
|
@message = e.message;
|
|
1385
1380
|
}
|
|
@@ -1393,6 +1388,26 @@ describe('basic client', () => {
|
|
|
1393
1388
|
expect(pre.textContent).toBe('Invalid value: expected a non-tracked object');
|
|
1394
1389
|
});
|
|
1395
1390
|
|
|
1391
|
+
it('returns undefined for non-existent props in track with trackSplit', () => {
|
|
1392
|
+
component App() {
|
|
1393
|
+
const [a, b, rest] = trackSplit({a: 1, c: 1}, ['a', 'b']);
|
|
1394
|
+
|
|
1395
|
+
<pre>{@a}</pre>
|
|
1396
|
+
<pre>{String(@b)}</pre>
|
|
1397
|
+
<pre>{@rest.c}</pre>
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
render(App);
|
|
1401
|
+
|
|
1402
|
+
const preA = container.querySelectorAll('pre')[0];
|
|
1403
|
+
const preB = container.querySelectorAll('pre')[1];
|
|
1404
|
+
const preC = container.querySelectorAll('pre')[2];
|
|
1405
|
+
|
|
1406
|
+
expect(preA.textContent).toBe('1');
|
|
1407
|
+
expect(preB.textContent).toBe('undefined');
|
|
1408
|
+
expect(preC.textContent).toBe('1');
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1396
1411
|
it('returns the same tracked object if plain track is called with a tracked object', () => {
|
|
1397
1412
|
component App() {
|
|
1398
1413
|
const t = track({a: 1, b: 2, c: 3});
|
|
@@ -1419,4 +1434,97 @@ describe('basic client', () => {
|
|
|
1419
1434
|
render(App);
|
|
1420
1435
|
expect(container).toMatchSnapshot();
|
|
1421
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
|
+
});
|
|
1422
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]:
|
|
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>,
|
|
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
|
|
97
|
-
|
|
98
|
-
|
|
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>(
|