ripple 0.2.108 → 0.2.110
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 +31 -10
- package/src/compiler/phases/2-analyze/index.js +90 -1
- package/src/compiler/phases/3-transform/client/index.js +48 -7
- package/src/compiler/phases/3-transform/server/index.js +134 -8
- package/src/compiler/scope.js +15 -1
- package/src/compiler/types/index.d.ts +2 -2
- package/src/compiler/utils.js +2 -0
- package/src/runtime/internal/client/composite.js +37 -5
- package/src/runtime/internal/client/for.js +48 -20
- package/src/runtime/internal/client/index.js +2 -0
- package/src/runtime/internal/client/render.js +47 -20
- package/src/runtime/internal/client/rpc.js +14 -0
- package/src/utils/builders.js +15 -2
- package/tests/client/__snapshots__/compiler.test.ripple.snap +13 -0
- package/tests/client/__snapshots__/for.test.ripple.snap +5 -5
- package/tests/client/compiler.test.ripple +15 -0
- package/tests/client/composite.test.ripple +31 -0
- package/tests/client/dynamic-elements.test.ripple +207 -0
- package/tests/client/for.test.ripple +7 -7
- package/tests/client/switch.test.ripple +152 -0
- package/tests/server/__snapshots__/compiler.test.ripple.snap +37 -0
- package/tests/server/compiler.test.ripple +39 -0
- package/tests/server/switch.test.ripple +27 -0
|
@@ -145,12 +145,12 @@ describe('for statements', () => {
|
|
|
145
145
|
expect(container).toMatchSnapshot();
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
-
it('handles
|
|
148
|
+
it('handles updating with new objects with same key', () => {
|
|
149
149
|
component App() {
|
|
150
150
|
let items = track([
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
151
|
+
{ id: 1, text: 'Item 1' },
|
|
152
|
+
{ id: 2, text: 'Item 2' },
|
|
153
|
+
{ id: 3, text: 'Item 3' },
|
|
154
154
|
]);
|
|
155
155
|
|
|
156
156
|
for (let item of @items; index i; key item.id) {
|
|
@@ -163,9 +163,9 @@ describe('for statements', () => {
|
|
|
163
163
|
@items[2].id = 1;
|
|
164
164
|
|
|
165
165
|
@items = [
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
166
|
+
{...@items[0], text: 'Item 1!'},
|
|
167
|
+
{...@items[1], text: 'Item 2!'},
|
|
168
|
+
{...@items[2], text: 'Item 3!'},
|
|
169
169
|
];
|
|
170
170
|
}}>{"Reverse"}</button>
|
|
171
171
|
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { mount, flushSync, track } from 'ripple';
|
|
3
|
+
|
|
4
|
+
describe('switch statements', () => {
|
|
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('renders simple switch with literal cases', () => {
|
|
24
|
+
component App() {
|
|
25
|
+
let value = track('b');
|
|
26
|
+
|
|
27
|
+
<button onClick={() => @value = 'c'}>{'Change to C'}</button>
|
|
28
|
+
<button onClick={() => @value = 'a'}>{'Change to A'}</button>
|
|
29
|
+
|
|
30
|
+
switch (@value) {
|
|
31
|
+
case 'a':
|
|
32
|
+
<div>{'Case A'}</div>
|
|
33
|
+
break;
|
|
34
|
+
case 'b':
|
|
35
|
+
<div>{'Case B'}</div>
|
|
36
|
+
break;
|
|
37
|
+
case 'c':
|
|
38
|
+
<div>{'Case C'}</div>
|
|
39
|
+
break;
|
|
40
|
+
default:
|
|
41
|
+
<div>{'Default Case'}</div>
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
render(App);
|
|
46
|
+
expect(container.textContent).toBe('Change to CChange to ACase B');
|
|
47
|
+
|
|
48
|
+
container.querySelectorAll('button')[0].click(); // Change to C
|
|
49
|
+
flushSync();
|
|
50
|
+
expect(container.textContent).toBe('Change to CChange to ACase C');
|
|
51
|
+
|
|
52
|
+
container.querySelectorAll('button')[1].click(); // Change to A
|
|
53
|
+
flushSync();
|
|
54
|
+
expect(container.textContent).toBe('Change to CChange to ACase A');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('renders switch with reactive discriminant', () => {
|
|
58
|
+
component App() {
|
|
59
|
+
let count = track(1);
|
|
60
|
+
|
|
61
|
+
<button onClick={() => @count++}>{'Increment'}</button>
|
|
62
|
+
|
|
63
|
+
switch (@count) {
|
|
64
|
+
case 1:
|
|
65
|
+
<div>{'Count is 1'}</div>
|
|
66
|
+
break;
|
|
67
|
+
case 2:
|
|
68
|
+
<div>{'Count is 2'}</div>
|
|
69
|
+
break;
|
|
70
|
+
default:
|
|
71
|
+
<div>{'Count is other'}</div>
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
render(App);
|
|
76
|
+
expect(container.textContent).toBe('IncrementCount is 1');
|
|
77
|
+
|
|
78
|
+
container.querySelector('button').click();
|
|
79
|
+
flushSync();
|
|
80
|
+
expect(container.textContent).toBe('IncrementCount is 2');
|
|
81
|
+
|
|
82
|
+
container.querySelector('button').click();
|
|
83
|
+
flushSync();
|
|
84
|
+
expect(container.textContent).toBe('IncrementCount is other');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('renders switch with default clause only', () => {
|
|
88
|
+
component App() {
|
|
89
|
+
let value = track('x');
|
|
90
|
+
|
|
91
|
+
<button onClick={() => @value = 'y'}>{'Change Value'}</button>
|
|
92
|
+
|
|
93
|
+
switch (@value) {
|
|
94
|
+
default:
|
|
95
|
+
<div>{'Default for ' + @value}</div>
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
render(App);
|
|
100
|
+
expect(container.textContent).toBe('Change ValueDefault for x');
|
|
101
|
+
|
|
102
|
+
container.querySelector('button').click();
|
|
103
|
+
flushSync();
|
|
104
|
+
expect(container.textContent).toBe('Change ValueDefault for y');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('renders switch with template content and JS logic', () => {
|
|
108
|
+
component App() {
|
|
109
|
+
let status = track('active');
|
|
110
|
+
let message = track('');
|
|
111
|
+
|
|
112
|
+
<button onClick={() => @status = 'pending'}>{'Pending'}</button>
|
|
113
|
+
<button onClick={() => @status = 'completed'}>{'Completed'}</button>
|
|
114
|
+
<button onClick={() => @status = 'error'}>{'Error'}</button>
|
|
115
|
+
|
|
116
|
+
switch (@status) {
|
|
117
|
+
case 'active':
|
|
118
|
+
message = 'Currently active.';
|
|
119
|
+
<div>{'Status: ' + @message}</div>
|
|
120
|
+
break;
|
|
121
|
+
case 'pending':
|
|
122
|
+
message = 'Waiting for completion.';
|
|
123
|
+
<div>{'Status: ' + @message}</div>
|
|
124
|
+
break;
|
|
125
|
+
case 'completed':
|
|
126
|
+
message = 'Task finished!';
|
|
127
|
+
<div class="success">{'Status: ' + @message}</div>
|
|
128
|
+
break;
|
|
129
|
+
default:
|
|
130
|
+
message = 'An error occurred.';
|
|
131
|
+
<div class="error">{'Status: ' + @message}</div>
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
render(App);
|
|
136
|
+
expect(container.textContent).toBe('PendingCompletedErrorStatus: Currently active.');
|
|
137
|
+
|
|
138
|
+
container.querySelectorAll('button')[0].click(); // Pending
|
|
139
|
+
flushSync();
|
|
140
|
+
expect(container.textContent).toBe('PendingCompletedErrorStatus: Waiting for completion.');
|
|
141
|
+
|
|
142
|
+
container.querySelectorAll('button')[1].click(); // Completed
|
|
143
|
+
flushSync();
|
|
144
|
+
expect(container.textContent).toBe('PendingCompletedErrorStatus: Task finished!');
|
|
145
|
+
expect(container.querySelector('.success')).toBeTruthy();
|
|
146
|
+
|
|
147
|
+
container.querySelectorAll('button')[2].click(); // Error
|
|
148
|
+
flushSync();
|
|
149
|
+
expect(container.textContent).toBe('PendingCompletedErrorStatus: An error occurred.');
|
|
150
|
+
expect(container.querySelector('.error')).toBeTruthy();
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`compiler success tests > compiles TSInstantiationExpression 1`] = `
|
|
4
|
+
"import * as _$_ from 'ripple/internal/client';
|
|
5
|
+
|
|
6
|
+
function makeBox(value) {
|
|
7
|
+
return { value };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const makeStringBox = (makeBox);
|
|
11
|
+
const stringBox = makeStringBox('abc');
|
|
12
|
+
const ErrorMap = (Map);
|
|
13
|
+
const errorMap = new ErrorMap();"
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
exports[`compiler success tests > compiles imported component with conditional async in SSR 1`] = `
|
|
17
|
+
"import * as _$_ from 'ripple/internal/server';
|
|
18
|
+
|
|
19
|
+
import { ChildComponent } from './Child.ripple';
|
|
20
|
+
|
|
21
|
+
export async function App(__output) {
|
|
22
|
+
return _$_.async(async () => {
|
|
23
|
+
_$_.push_component();
|
|
24
|
+
__output.push('<div');
|
|
25
|
+
__output.push('>');
|
|
26
|
+
|
|
27
|
+
if (ChildComponent.async) {
|
|
28
|
+
await ChildComponent(__output, { message: "hello" });
|
|
29
|
+
} else {
|
|
30
|
+
ChildComponent(__output, { message: "hello" });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
__output.push('</div>');
|
|
34
|
+
_$_.pop_component();
|
|
35
|
+
});
|
|
36
|
+
}"
|
|
37
|
+
`;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { compile } from 'ripple/compiler'
|
|
3
|
+
|
|
4
|
+
describe('compiler success tests', () => {
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
it('compiles TSInstantiationExpression', () => {
|
|
8
|
+
const source =
|
|
9
|
+
`function makeBox<T>(value: T) {
|
|
10
|
+
return { value };
|
|
11
|
+
}
|
|
12
|
+
const makeStringBox = makeBox<string>;
|
|
13
|
+
const stringBox = makeStringBox('abc');
|
|
14
|
+
const ErrorMap = Map<string, Error>;
|
|
15
|
+
const errorMap = new ErrorMap();`;
|
|
16
|
+
|
|
17
|
+
const result = compile(source, 'test.ripple', { mode: 'client' });
|
|
18
|
+
|
|
19
|
+
expect(result.js.code).toMatchSnapshot();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('compiles imported component with conditional async in SSR', () => {
|
|
23
|
+
const source =
|
|
24
|
+
`import { ChildComponent } from './Child.ripple';
|
|
25
|
+
|
|
26
|
+
export component App() {
|
|
27
|
+
<div>
|
|
28
|
+
<ChildComponent message="hello" />
|
|
29
|
+
</div>
|
|
30
|
+
}`;
|
|
31
|
+
|
|
32
|
+
const result = compile(source, 'test.ripple', { mode: 'server' });
|
|
33
|
+
|
|
34
|
+
// Should use if-statement instead of ternary to avoid parser issues
|
|
35
|
+
expect(result.js.code).toContain('if (ChildComponent.async)');
|
|
36
|
+
expect(result.js.code).toContain('await ChildComponent');
|
|
37
|
+
expect(result.js.code).toMatchSnapshot();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { render } from 'ripple/server';
|
|
3
|
+
|
|
4
|
+
describe('SSR: switch statements', () => {
|
|
5
|
+
it('renders simple switch with literal cases', async () => {
|
|
6
|
+
component App() {
|
|
7
|
+
let value = 'b';
|
|
8
|
+
|
|
9
|
+
switch (value) {
|
|
10
|
+
case 'a':
|
|
11
|
+
<div>{'Case A'}</div>
|
|
12
|
+
break;
|
|
13
|
+
case 'b':
|
|
14
|
+
<div>{'Case B'}</div>
|
|
15
|
+
break;
|
|
16
|
+
case 'c':
|
|
17
|
+
<div>{'Case C'}</div>
|
|
18
|
+
break;
|
|
19
|
+
default:
|
|
20
|
+
<div>{'Default Case'}</div>
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { body } = await render(App);
|
|
25
|
+
expect(body).toBe('<div>Case B</div>');
|
|
26
|
+
});
|
|
27
|
+
});
|