ripple 0.2.79 → 0.2.81
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 +0 -34
- package/src/compiler/phases/2-analyze/index.js +0 -12
- package/src/compiler/phases/3-transform/client/index.js +35 -66
- package/src/compiler/phases/3-transform/server/index.js +37 -1
- package/src/compiler/utils.js +16 -0
- package/src/runtime/internal/client/composite.js +22 -0
- package/src/runtime/internal/client/constants.js +3 -3
- package/src/runtime/internal/client/index.js +2 -0
- package/src/runtime/internal/client/runtime.js +739 -711
- package/src/runtime/internal/client/types.d.ts +2 -0
- package/src/runtime/internal/server/index.js +4 -0
- package/tests/client/__snapshots__/basic.test.ripple.snap +8 -26
- package/tests/client/basic.test.ripple +8 -3
- package/tests/client/composite.test.ripple +37 -1
- package/tests/server/basic.test.ripple +15 -0
- package/types/index.d.ts +2 -2
- package/types/server.d.ts +4 -0
- package/tests/client/accessors-props.test.ripple +0 -146
|
@@ -18,6 +18,7 @@ export type Dependency = {
|
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
export type Tracked = {
|
|
21
|
+
a: { get?: Function, set?: Function };
|
|
21
22
|
b: Block;
|
|
22
23
|
c: number;
|
|
23
24
|
f: number;
|
|
@@ -25,6 +26,7 @@ export type Tracked = {
|
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
export type Derived = {
|
|
29
|
+
a: { get?: Function, set?: Function };
|
|
28
30
|
b: Block;
|
|
29
31
|
blocks: null | Block[];
|
|
30
32
|
c: number;
|
|
@@ -114,6 +114,10 @@ export function spread_attrs(attrs, css_hash) {
|
|
|
114
114
|
|
|
115
115
|
if (typeof value === 'function') continue;
|
|
116
116
|
|
|
117
|
+
if (is_tracked_object(value)) {
|
|
118
|
+
value = get(value);
|
|
119
|
+
}
|
|
120
|
+
|
|
117
121
|
if (name === 'class' && css_hash) {
|
|
118
122
|
value = (value == null ? '' : value) + ' ' + css_hash;
|
|
119
123
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
-
exports[`basic > basic operations 1`] = `
|
|
3
|
+
exports[`basic client > basic operations 1`] = `
|
|
4
4
|
<div>
|
|
5
5
|
<div>
|
|
6
6
|
0
|
|
@@ -18,25 +18,7 @@ exports[`basic > basic operations 1`] = `
|
|
|
18
18
|
</div>
|
|
19
19
|
`;
|
|
20
20
|
|
|
21
|
-
exports[`basic >
|
|
22
|
-
<div>
|
|
23
|
-
<div>
|
|
24
|
-
11
|
|
25
|
-
</div>
|
|
26
|
-
<div>
|
|
27
|
-
11
|
|
28
|
-
</div>
|
|
29
|
-
<div>
|
|
30
|
-
truetrue
|
|
31
|
-
</div>
|
|
32
|
-
<div>
|
|
33
|
-
truetrue
|
|
34
|
-
</div>
|
|
35
|
-
|
|
36
|
-
</div>
|
|
37
|
-
`;
|
|
38
|
-
|
|
39
|
-
exports[`basic > handles boolean attributes with no prop value provides 1`] = `
|
|
21
|
+
exports[`basic client > handles boolean attributes with no prop value provides 1`] = `
|
|
40
22
|
<div>
|
|
41
23
|
<div
|
|
42
24
|
class="container"
|
|
@@ -55,7 +37,7 @@ exports[`basic > handles boolean attributes with no prop value provides 1`] = `
|
|
|
55
37
|
</div>
|
|
56
38
|
`;
|
|
57
39
|
|
|
58
|
-
exports[`basic > render semi-dynamic text 1`] = `
|
|
40
|
+
exports[`basic client > render semi-dynamic text 1`] = `
|
|
59
41
|
<div>
|
|
60
42
|
<div>
|
|
61
43
|
Hello World
|
|
@@ -64,7 +46,7 @@ exports[`basic > render semi-dynamic text 1`] = `
|
|
|
64
46
|
</div>
|
|
65
47
|
`;
|
|
66
48
|
|
|
67
|
-
exports[`basic > render spread props without duplication 1`] = `
|
|
49
|
+
exports[`basic client > render spread props without duplication 1`] = `
|
|
68
50
|
<div>
|
|
69
51
|
<div>
|
|
70
52
|
<input
|
|
@@ -78,7 +60,7 @@ exports[`basic > render spread props without duplication 1`] = `
|
|
|
78
60
|
</div>
|
|
79
61
|
`;
|
|
80
62
|
|
|
81
|
-
exports[`basic > render static attributes 1`] = `
|
|
63
|
+
exports[`basic client > render static attributes 1`] = `
|
|
82
64
|
<div>
|
|
83
65
|
<div
|
|
84
66
|
class="foo"
|
|
@@ -91,7 +73,7 @@ exports[`basic > render static attributes 1`] = `
|
|
|
91
73
|
</div>
|
|
92
74
|
`;
|
|
93
75
|
|
|
94
|
-
exports[`basic > render static text 1`] = `
|
|
76
|
+
exports[`basic client > render static text 1`] = `
|
|
95
77
|
<div>
|
|
96
78
|
<div>
|
|
97
79
|
Hello World
|
|
@@ -100,7 +82,7 @@ exports[`basic > render static text 1`] = `
|
|
|
100
82
|
</div>
|
|
101
83
|
`;
|
|
102
84
|
|
|
103
|
-
exports[`basic > renders simple JS expression logic correctly 1`] = `
|
|
85
|
+
exports[`basic client > renders simple JS expression logic correctly 1`] = `
|
|
104
86
|
<div>
|
|
105
87
|
<div>
|
|
106
88
|
{"0":"Test"}
|
|
@@ -112,7 +94,7 @@ exports[`basic > renders simple JS expression logic correctly 1`] = `
|
|
|
112
94
|
</div>
|
|
113
95
|
`;
|
|
114
96
|
|
|
115
|
-
exports[`basic > should handle lexical scopes correctly 1`] = `
|
|
97
|
+
exports[`basic client > should handle lexical scopes correctly 1`] = `
|
|
116
98
|
<div>
|
|
117
99
|
<section>
|
|
118
100
|
Nested scope variable
|
|
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
|
2
2
|
import { mount, flushSync, effect, track } from 'ripple';
|
|
3
3
|
import { compile } from 'ripple/compiler';
|
|
4
4
|
|
|
5
|
-
describe('basic', () => {
|
|
5
|
+
describe('basic client', () => {
|
|
6
6
|
let container;
|
|
7
7
|
|
|
8
8
|
function render(component) {
|
|
@@ -1276,7 +1276,12 @@ describe('basic', () => {
|
|
|
1276
1276
|
let logs = [];
|
|
1277
1277
|
|
|
1278
1278
|
component App() {
|
|
1279
|
-
let count = track(0
|
|
1279
|
+
let count = track(0, {
|
|
1280
|
+
set(v) {
|
|
1281
|
+
logs.push('inside setter');
|
|
1282
|
+
return v;
|
|
1283
|
+
}
|
|
1284
|
+
});
|
|
1280
1285
|
let name = track('Click Me');
|
|
1281
1286
|
|
|
1282
1287
|
function buttonRef(el) {
|
|
@@ -1289,7 +1294,7 @@ describe('basic', () => {
|
|
|
1289
1294
|
<Child
|
|
1290
1295
|
class="my-button"
|
|
1291
1296
|
onClick={() => @name === 'Click Me' ? @name = 'Clicked' : @name = 'Click Me'}
|
|
1292
|
-
count
|
|
1297
|
+
count={@count}
|
|
1293
1298
|
{ref buttonRef}
|
|
1294
1299
|
>{@name}</Child>;
|
|
1295
1300
|
}
|
|
@@ -663,5 +663,41 @@ describe('composite components', () => {
|
|
|
663
663
|
|
|
664
664
|
render(App);
|
|
665
665
|
expect(container).toMatchSnapshot();
|
|
666
|
-
})
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it('handles dynamic component switching', () => {
|
|
669
|
+
component Child1() {
|
|
670
|
+
<div>{"I am child 1"}</div>
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
component Child2() {
|
|
674
|
+
<div>{"I am child 2"}</div>
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
component App() {
|
|
678
|
+
let thing = track(() => Child1)
|
|
679
|
+
|
|
680
|
+
<div id="container">
|
|
681
|
+
<@thing />
|
|
682
|
+
</div>
|
|
683
|
+
|
|
684
|
+
<button onClick={() => @thing = @thing === Child1 ? Child2 : Child1}>{"Change Child"}</button>
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
render(App);
|
|
688
|
+
flushSync();
|
|
689
|
+
|
|
690
|
+
expect(container.querySelector('#container').textContent).toBe('I am child 1');
|
|
691
|
+
|
|
692
|
+
const button = container.querySelector('button');
|
|
693
|
+
button.click();
|
|
694
|
+
flushSync();
|
|
695
|
+
|
|
696
|
+
expect(container.querySelector('#container').textContent).toBe('I am child 2');
|
|
697
|
+
|
|
698
|
+
button.click();
|
|
699
|
+
flushSync();
|
|
700
|
+
|
|
701
|
+
expect(container.querySelector('#container').textContent).toBe('I am child 1');
|
|
702
|
+
});
|
|
667
703
|
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { render } from 'ripple/server';
|
|
3
|
+
|
|
4
|
+
describe('basic client', () => {
|
|
5
|
+
it('render static text', async () => {
|
|
6
|
+
component Basic() {
|
|
7
|
+
<div>{'Hello World'}</div>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { head, body } = await render(Basic);
|
|
11
|
+
|
|
12
|
+
expect(head).toBe('');
|
|
13
|
+
expect(body).toBe('<div>Hello World</div>');
|
|
14
|
+
});
|
|
15
|
+
});
|
package/types/index.d.ts
CHANGED
|
@@ -89,13 +89,13 @@ type RestKeys<T, K extends readonly (keyof T)[]> = Expand<Omit<T, K[number]>>;
|
|
|
89
89
|
type SplitResult<T extends Props, K extends readonly (keyof T)[]> =
|
|
90
90
|
[...PickKeys<T, K>, UnwrapTracked<RestKeys<T, K>>];
|
|
91
91
|
|
|
92
|
-
type TrackOptions = { split?: readonly (string | number | symbol)[] };
|
|
92
|
+
type TrackOptions<T> = { split?: readonly (string | number | symbol)[], get?: (v: T) => T, set?: (v: T) => T };
|
|
93
93
|
|
|
94
94
|
export declare function track<V>(value?: V | (() => V)): Tracked<V>;
|
|
95
95
|
|
|
96
96
|
export declare function track<V extends Props, const K extends readonly (keyof V)[]>(
|
|
97
97
|
value: V,
|
|
98
|
-
options: TrackOptions
|
|
98
|
+
options: TrackOptions<V>
|
|
99
99
|
): SplitResult<V, K>;
|
|
100
100
|
|
|
101
101
|
export function on<Type extends keyof WindowEventMap>(
|
package/types/server.d.ts
CHANGED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { mount, flushSync, effect, track } from 'ripple';
|
|
3
|
-
|
|
4
|
-
describe('prop accessors', () => {
|
|
5
|
-
let container;
|
|
6
|
-
|
|
7
|
-
function render(component) {
|
|
8
|
-
mount(component, {
|
|
9
|
-
target: container
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
container = document.createElement('div');
|
|
15
|
-
document.body.appendChild(container);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
document.body.removeChild(container);
|
|
20
|
-
container = null;
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('should render a basic prop accessor on a composite component', () => {
|
|
24
|
-
const logs = [];
|
|
25
|
-
|
|
26
|
-
component Child(props) {
|
|
27
|
-
effect(() => {
|
|
28
|
-
logs.push('App effect', props.@foo);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
<button onClick={() => props.@foo++}>{"Increment foo"}</button>
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
component App(props) {
|
|
35
|
-
let foo = track(0);
|
|
36
|
-
|
|
37
|
-
<Child {foo} />
|
|
38
|
-
|
|
39
|
-
<div>{"parent foo: " + @foo}</div>
|
|
40
|
-
|
|
41
|
-
<button onClick={() => { @foo++; }}>{"Increment parent foo"}</button>
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
render(App);
|
|
45
|
-
flushSync();
|
|
46
|
-
|
|
47
|
-
expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 0');
|
|
48
|
-
expect(logs).toEqual(['App effect', 0]);
|
|
49
|
-
|
|
50
|
-
const [button, button2] = container.querySelectorAll('button');
|
|
51
|
-
|
|
52
|
-
button.click();
|
|
53
|
-
flushSync();
|
|
54
|
-
|
|
55
|
-
expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 1');
|
|
56
|
-
expect(logs).toEqual(['App effect', 0, 'App effect', 1]);
|
|
57
|
-
|
|
58
|
-
button2.click();
|
|
59
|
-
flushSync();
|
|
60
|
-
|
|
61
|
-
expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 2');
|
|
62
|
-
expect(logs).toEqual(['App effect', 0, 'App effect', 1, 'App effect', 2]);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// Note: Disabled this test for further investigation.
|
|
66
|
-
it.skip('should render a basic prop accessor on a composite component #2', () => {
|
|
67
|
-
const logs = [];
|
|
68
|
-
|
|
69
|
-
component Child({ foo }) {
|
|
70
|
-
effect(() => {
|
|
71
|
-
logs.push('App effect', props.foo);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
<button onClick={() => props.foo++}>{"Increment foo"}</button>
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
component App() {
|
|
78
|
-
let foo = track(0);
|
|
79
|
-
|
|
80
|
-
<Child foo:={() => {
|
|
81
|
-
return @foo;
|
|
82
|
-
}, v => {
|
|
83
|
-
// do not update parent
|
|
84
|
-
}} />
|
|
85
|
-
|
|
86
|
-
<div>{"parent foo: " + @foo}</div>
|
|
87
|
-
|
|
88
|
-
<button onClick={() => { foo++ }}>{"Increment parent foo"}</button>
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
render(App);
|
|
92
|
-
flushSync();
|
|
93
|
-
|
|
94
|
-
expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 0');
|
|
95
|
-
expect(logs).toEqual(['App effect', 0]);
|
|
96
|
-
|
|
97
|
-
const [button, button2] = container.querySelectorAll('button');
|
|
98
|
-
|
|
99
|
-
button.click();
|
|
100
|
-
flushSync();
|
|
101
|
-
|
|
102
|
-
expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 0');
|
|
103
|
-
expect(logs).toEqual(['App effect', 0]);
|
|
104
|
-
|
|
105
|
-
button2.click();
|
|
106
|
-
flushSync();
|
|
107
|
-
|
|
108
|
-
expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 1');
|
|
109
|
-
expect(logs).toEqual(['App effect', 0, 'App effect', 1]);
|
|
110
|
-
|
|
111
|
-
button2.click();
|
|
112
|
-
flushSync();
|
|
113
|
-
|
|
114
|
-
expect(container.querySelectorAll('div')[0].textContent).toBe('parent foo: 2');
|
|
115
|
-
expect(logs).toEqual(['App effect', 0, 'App effect', 1, 'App effect', 2]);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('handles a simple getter prop accessor with no setter', () =>{
|
|
119
|
-
component Parent() {
|
|
120
|
-
let value = track(123);
|
|
121
|
-
|
|
122
|
-
<Child value:={() => @value} />
|
|
123
|
-
|
|
124
|
-
<button onClick={() => { @value++ }}>{"Increment value"}</button>
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
component Child({ value }) {
|
|
128
|
-
<div>{value}</div>
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
render(Parent);
|
|
132
|
-
|
|
133
|
-
expect(container.querySelector('div').textContent).toBe('123');
|
|
134
|
-
|
|
135
|
-
const button = container.querySelector('button');
|
|
136
|
-
button.click();
|
|
137
|
-
flushSync();
|
|
138
|
-
|
|
139
|
-
expect(container.querySelector('div').textContent).toBe('124');
|
|
140
|
-
|
|
141
|
-
button.click();
|
|
142
|
-
flushSync();
|
|
143
|
-
|
|
144
|
-
expect(container.querySelector('div').textContent).toBe('125');
|
|
145
|
-
});
|
|
146
|
-
});
|