ripple 0.2.6 → 0.2.8
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/README.md +1 -1
- package/package.json +1 -1
- package/src/compiler/errors.js +20 -22
- package/src/compiler/phases/1-parse/index.js +21 -19
- package/src/compiler/phases/1-parse/style.js +27 -27
- package/src/compiler/phases/2-analyze/index.js +25 -25
- package/src/compiler/phases/2-analyze/prune.js +64 -27
- package/src/compiler/phases/3-transform/index.js +150 -113
- package/src/compiler/phases/3-transform/segments.js +25 -20
- package/src/compiler/phases/3-transform/stylesheet.js +28 -28
- package/src/compiler/scope.js +3 -3
- package/src/compiler/utils.js +7 -9
- package/src/constants.js +1 -2
- package/src/jsx-runtime.d.ts +59 -59
- package/src/jsx-runtime.js +13 -13
- package/src/runtime/array.js +15 -15
- package/src/runtime/index.js +2 -0
- package/src/runtime/internal/client/blocks.js +16 -5
- package/src/runtime/internal/client/constants.js +1 -1
- package/src/runtime/internal/client/events.js +2 -2
- package/src/runtime/internal/client/for.js +6 -7
- package/src/runtime/internal/client/operations.js +1 -1
- package/src/runtime/internal/client/runtime.js +41 -20
- package/src/runtime/internal/client/template.js +1 -1
- package/src/runtime/internal/client/try.js +2 -2
- package/src/utils/ast.js +9 -9
- package/src/utils/builders.js +66 -28
- package/tests/__snapshots__/for.test.ripple.snap +81 -0
- package/tests/basic.test.ripple +292 -263
- package/tests/composite.test.ripple +151 -0
- package/tests/for.test.ripple +58 -0
- package/tests/ref.test.ripple +52 -0
- package/tests/use.test.ripple +24 -22
- package/types/index.d.ts +7 -1
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { mount, flushSync } from 'ripple';
|
|
4
|
+
|
|
5
|
+
describe('composite components', () => {
|
|
6
|
+
let container;
|
|
7
|
+
|
|
8
|
+
function render(component) {
|
|
9
|
+
mount(component, {
|
|
10
|
+
target: container
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
container = document.createElement('div');
|
|
16
|
+
document.body.appendChild(container);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
document.body.removeChild(container);
|
|
21
|
+
container = null;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('renders composite components', () => {
|
|
25
|
+
component Button({ $count }) {
|
|
26
|
+
<div>{$count}</div>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
component App() {
|
|
30
|
+
let $count = 0;
|
|
31
|
+
<button onClick={() => $count++}>{'Increment'}</button>
|
|
32
|
+
<Button $count={$count} />
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
render(App);
|
|
36
|
+
|
|
37
|
+
const button = container.querySelector('button');
|
|
38
|
+
|
|
39
|
+
button.click();
|
|
40
|
+
flushSync();
|
|
41
|
+
|
|
42
|
+
expect(container.querySelector('div').textContent).toBe('1');
|
|
43
|
+
|
|
44
|
+
button.click();
|
|
45
|
+
flushSync();
|
|
46
|
+
|
|
47
|
+
expect(container.querySelector('div').textContent).toBe('2');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('renders composite components with object state', () => {
|
|
51
|
+
component Button({ obj }) {
|
|
52
|
+
<button class='count2' onClick={() => {
|
|
53
|
+
obj.$count++;
|
|
54
|
+
}}>{obj.$count}</button>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
component App() {
|
|
58
|
+
<div>
|
|
59
|
+
let obj = {
|
|
60
|
+
$count: 0
|
|
61
|
+
};
|
|
62
|
+
let $button = true;
|
|
63
|
+
|
|
64
|
+
<span class='count'>{obj.$count}</span>
|
|
65
|
+
<span>{' '}</span>
|
|
66
|
+
<Button obj={obj} />
|
|
67
|
+
</div>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
render(App);
|
|
71
|
+
|
|
72
|
+
const button = container.querySelector('button');
|
|
73
|
+
|
|
74
|
+
button.click();
|
|
75
|
+
flushSync();
|
|
76
|
+
|
|
77
|
+
expect(container.querySelector('.count').textContent).toBe('1');
|
|
78
|
+
expect(container.querySelector('.count2').textContent).toBe('1');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('renders composite components with object state', () => {
|
|
82
|
+
component Button({ obj }) {
|
|
83
|
+
<button class='count2' onClick={() => {
|
|
84
|
+
obj.$count++;
|
|
85
|
+
}}>{obj.$count}</button>
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
component App() {
|
|
89
|
+
<div>
|
|
90
|
+
let obj = {
|
|
91
|
+
$count: 0
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
<span class='count'>{obj.$count}</span>
|
|
95
|
+
<Button obj={obj} />
|
|
96
|
+
</div>
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
render(App);
|
|
100
|
+
|
|
101
|
+
const button = container.querySelector('button');
|
|
102
|
+
|
|
103
|
+
button.click();
|
|
104
|
+
flushSync();
|
|
105
|
+
|
|
106
|
+
expect(container.querySelector('.count').textContent).toBe('1');
|
|
107
|
+
expect(container.querySelector('.count2').textContent).toBe('1');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('renders composite components with object state wrapped in an if statement', () => {
|
|
111
|
+
component Button({ obj }) {
|
|
112
|
+
<button class='count2' onClick={() => {
|
|
113
|
+
obj.$count++;
|
|
114
|
+
}}>{obj.$count}</button>
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
component OtherComponent({ obj }) {
|
|
118
|
+
<div class='count3'>{obj.$count}</div>
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
component App() {
|
|
122
|
+
<div>
|
|
123
|
+
let obj = {
|
|
124
|
+
$count: 0
|
|
125
|
+
};
|
|
126
|
+
let $button = true;
|
|
127
|
+
|
|
128
|
+
<span class='count'>{obj.$count}</span>
|
|
129
|
+
<span>{' '}</span>
|
|
130
|
+
if (obj) {
|
|
131
|
+
<Button obj={obj} />
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (obj) {
|
|
135
|
+
<OtherComponent obj={obj} />
|
|
136
|
+
}
|
|
137
|
+
</div>
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
render(App);
|
|
141
|
+
|
|
142
|
+
const button = container.querySelector('button');
|
|
143
|
+
|
|
144
|
+
button.click();
|
|
145
|
+
flushSync();
|
|
146
|
+
|
|
147
|
+
expect(container.querySelector('.count').textContent).toBe('1');
|
|
148
|
+
expect(container.querySelector('.count2').textContent).toBe('1');
|
|
149
|
+
expect(container.querySelector('.count3').textContent).toBe('1');
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { mount, flushSync, array } from 'ripple';
|
|
4
|
+
|
|
5
|
+
describe('for statements', () => {
|
|
6
|
+
let container;
|
|
7
|
+
|
|
8
|
+
function render(component) {
|
|
9
|
+
mount(component, {
|
|
10
|
+
target: container
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
container = document.createElement('div');
|
|
16
|
+
document.body.appendChild(container);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
document.body.removeChild(container);
|
|
21
|
+
container = null;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('render a simple static array', () => {
|
|
25
|
+
component App() {
|
|
26
|
+
const items = ['Item 1', 'Item 2', 'Item 3'];
|
|
27
|
+
|
|
28
|
+
for (const item of items) {
|
|
29
|
+
<div class={item}>{item}</div>
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
render(App);
|
|
34
|
+
|
|
35
|
+
expect(container).toMatchSnapshot();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('render a simple dynamic array', () => {
|
|
39
|
+
component App() {
|
|
40
|
+
const items = array('Item 1', 'Item 2', 'Item 3');
|
|
41
|
+
|
|
42
|
+
for (const item of items) {
|
|
43
|
+
<div class={item}>{item}</div>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
<button onClick={() => items.push(`Item ${items.$length + 1}`)}>{"Add Item"}</button>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
render(App);
|
|
50
|
+
expect(container).toMatchSnapshot();
|
|
51
|
+
|
|
52
|
+
const button = container.querySelector('button');
|
|
53
|
+
button.click();
|
|
54
|
+
flushSync();
|
|
55
|
+
|
|
56
|
+
expect(container).toMatchSnapshot();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { mount, flushSync, ref } from 'ripple';
|
|
4
|
+
|
|
5
|
+
describe('ref', () => {
|
|
6
|
+
let container;
|
|
7
|
+
|
|
8
|
+
function render(component) {
|
|
9
|
+
mount(component, {
|
|
10
|
+
target: container
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
container = document.createElement('div');
|
|
16
|
+
document.body.appendChild(container);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
document.body.removeChild(container);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('creates a reactive ref with initial value', () => {
|
|
24
|
+
component TestRef() {
|
|
25
|
+
let $count = 5;
|
|
26
|
+
<div><span id='count'>{$count}</span></div>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
render(TestRef);
|
|
30
|
+
|
|
31
|
+
expect(container.querySelector('#count').textContent).toBe('5');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('updates when ref value changes', () => {
|
|
35
|
+
component TestRef() {
|
|
36
|
+
let $count = 0;
|
|
37
|
+
<div>
|
|
38
|
+
<span id='count'>{$count}</span>
|
|
39
|
+
<button id='btn' onClick={() => $count++}>{'Increment'}</button>
|
|
40
|
+
</div>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
render(TestRef);
|
|
44
|
+
|
|
45
|
+
expect(container.querySelector('#count').textContent).toBe('0');
|
|
46
|
+
|
|
47
|
+
container.querySelector('#btn').click();
|
|
48
|
+
flushSync();
|
|
49
|
+
|
|
50
|
+
expect(container.querySelector('#count').textContent).toBe('1');
|
|
51
|
+
});
|
|
52
|
+
});
|
package/tests/use.test.ripple
CHANGED
|
@@ -1,32 +1,34 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
|
|
2
3
|
import { mount, flushSync } from 'ripple';
|
|
3
4
|
|
|
4
5
|
describe('@use element decorators', () => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function render(component) {
|
|
8
|
-
mount(component, { target: container });
|
|
9
|
-
}
|
|
6
|
+
let container;
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
function render(component) {
|
|
9
|
+
mount(component, {
|
|
10
|
+
target: container
|
|
11
|
+
});
|
|
12
|
+
}
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
container = document.createElement('div');
|
|
16
|
+
document.body.appendChild(container);
|
|
17
|
+
});
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<div {@use (node) => { div = node; }}>{"Hello World"}</div>
|
|
26
|
-
}
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
document.body.removeChild(container);
|
|
21
|
+
container = null;
|
|
22
|
+
});
|
|
27
23
|
|
|
28
|
-
|
|
24
|
+
it('capture a <div>', () => {
|
|
25
|
+
let div;
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
component Component() {
|
|
28
|
+
<div {@use (node) => { div = node; }}>{'Hello World'}</div>
|
|
29
|
+
}
|
|
30
|
+
render(Component);
|
|
31
|
+
flushSync();
|
|
32
|
+
expect(div.textContent).toBe('Hello World');
|
|
33
|
+
});
|
|
32
34
|
});
|
package/types/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export type Component<T = Record<string, any>> = (props: T) => void;
|
|
|
2
2
|
|
|
3
3
|
export declare function mount(
|
|
4
4
|
component: () => void,
|
|
5
|
-
options: { target: HTMLElement; props?: Record<string, any> }
|
|
5
|
+
options: { target: HTMLElement; props?: Record<string, any> },
|
|
6
6
|
): () => void;
|
|
7
7
|
|
|
8
8
|
export declare function untrack<T>(fn: () => T): T;
|
|
@@ -10,3 +10,9 @@ export declare function untrack<T>(fn: () => T): T;
|
|
|
10
10
|
export declare function flushSync<T>(fn: () => T): T;
|
|
11
11
|
|
|
12
12
|
export declare function effect(fn: (() => void) | (() => () => void)): void;
|
|
13
|
+
|
|
14
|
+
export interface Ref<T> {
|
|
15
|
+
$current: T;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export declare function ref<T>(value: T): Ref<T>;
|