ripple 0.3.4 → 0.3.5
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/CHANGELOG.md +18 -0
- package/package.json +2 -2
- package/src/compiler/phases/2-analyze/index.js +36 -3
- package/src/compiler/phases/3-transform/client/index.js +8 -0
- package/src/compiler/types/index.d.ts +1 -1
- package/src/runtime/internal/server/index.js +11 -0
- package/tests/client/lazy-destructuring.test.ripple +24 -0
- package/tests/server/lazy-destructuring.test.ripple +103 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# ripple
|
|
2
2
|
|
|
3
|
+
## 0.3.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#827](https://github.com/Ripple-TS/ripple/pull/827)
|
|
8
|
+
[`218a72c`](https://github.com/Ripple-TS/ripple/commit/218a72c3e663910636eec1d065c58afe30813c84)
|
|
9
|
+
Thanks [@trueadm](https://github.com/trueadm)! - fix(compiler): handle
|
|
10
|
+
UpdateExpression on lazy bindings with default values
|
|
11
|
+
|
|
12
|
+
Update expressions (`++`/`--`) on lazy destructured bindings with default values
|
|
13
|
+
now work correctly. For postfix operations (`count++`), an IIFE captures the
|
|
14
|
+
fallback value before incrementing. Also added `fallback` function to server
|
|
15
|
+
runtime.
|
|
16
|
+
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
[[`218a72c`](https://github.com/Ripple-TS/ripple/commit/218a72c3e663910636eec1d065c58afe30813c84)]:
|
|
19
|
+
- ripple@0.3.5
|
|
20
|
+
|
|
3
21
|
## 0.3.4
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Ripple is an elegant TypeScript UI framework",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Dominic Gannaway",
|
|
6
|
-
"version": "0.3.
|
|
6
|
+
"version": "0.3.5",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"module": "src/runtime/index-client.js",
|
|
9
9
|
"main": "src/runtime/index-client.js",
|
|
@@ -105,6 +105,6 @@
|
|
|
105
105
|
"vscode-languageserver-types": "^3.17.5"
|
|
106
106
|
},
|
|
107
107
|
"peerDependencies": {
|
|
108
|
-
"ripple": "0.3.
|
|
108
|
+
"ripple": "0.3.5"
|
|
109
109
|
}
|
|
110
110
|
}
|
|
@@ -85,7 +85,8 @@ function setup_lazy_transforms(pattern, source_id, state, writable) {
|
|
|
85
85
|
const binding = state.scope.get(name);
|
|
86
86
|
|
|
87
87
|
if (binding !== null) {
|
|
88
|
-
|
|
88
|
+
const has_fallback = path.has_default_value;
|
|
89
|
+
binding.kind = has_fallback ? 'lazy_fallback' : 'lazy';
|
|
89
90
|
|
|
90
91
|
binding.transform = {
|
|
91
92
|
read: (_) => {
|
|
@@ -101,8 +102,40 @@ function setup_lazy_transforms(pattern, source_id, state, writable) {
|
|
|
101
102
|
value,
|
|
102
103
|
);
|
|
103
104
|
};
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
|
|
106
|
+
if (has_fallback) {
|
|
107
|
+
// For bindings with default values, generate proper fallback-aware update
|
|
108
|
+
// e.g., count++ with default 0 becomes:
|
|
109
|
+
// (() => { var _v = _$_.fallback(obj.count, 0); obj.count = _v + 1; return _v; })() for postfix
|
|
110
|
+
// (obj.count = _$_.fallback(obj.count, 0) + 1) for prefix
|
|
111
|
+
binding.transform.update = (node) => {
|
|
112
|
+
const member = path.update_expression(source_id);
|
|
113
|
+
const fallback_read = path.expression(source_id);
|
|
114
|
+
const delta = node.operator === '++' ? b.literal(1) : b.literal(-1);
|
|
115
|
+
|
|
116
|
+
if (node.prefix) {
|
|
117
|
+
// ++count: return new value
|
|
118
|
+
return b.assignment('=', member, b.binary('+', fallback_read, delta));
|
|
119
|
+
} else {
|
|
120
|
+
// count++: return old value, write new value
|
|
121
|
+
// Use IIFE to declare temp variable
|
|
122
|
+
const temp = b.id('_v');
|
|
123
|
+
return b.call(
|
|
124
|
+
b.arrow(
|
|
125
|
+
[],
|
|
126
|
+
b.block([
|
|
127
|
+
b.var(temp, fallback_read),
|
|
128
|
+
b.stmt(b.assignment('=', member, b.binary('+', temp, delta))),
|
|
129
|
+
b.return(temp),
|
|
130
|
+
]),
|
|
131
|
+
),
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
} else {
|
|
136
|
+
binding.transform.update = (node) =>
|
|
137
|
+
b.update(node.operator, path.update_expression(source_id), node.prefix);
|
|
138
|
+
}
|
|
106
139
|
}
|
|
107
140
|
}
|
|
108
141
|
}
|
|
@@ -2078,6 +2078,14 @@ const visitors = {
|
|
|
2078
2078
|
}
|
|
2079
2079
|
const argument = node.argument;
|
|
2080
2080
|
|
|
2081
|
+
// Handle lazy binding updates (e.g., a++ where a is from let &{a} = obj)
|
|
2082
|
+
if (argument.type === 'Identifier') {
|
|
2083
|
+
const binding = context.state.scope?.get(argument.name);
|
|
2084
|
+
if (binding?.transform?.update && binding.node !== argument) {
|
|
2085
|
+
return binding.transform.update(node);
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2081
2089
|
if (
|
|
2082
2090
|
argument.type === 'MemberExpression' &&
|
|
2083
2091
|
(argument.tracked || (argument.property.type === 'Identifier' && argument.property.tracked))
|
|
@@ -1158,7 +1158,7 @@ export interface Binding {
|
|
|
1158
1158
|
transform?: {
|
|
1159
1159
|
read: (node?: AST.Identifier) => AST.Expression;
|
|
1160
1160
|
assign?: (node: AST.Pattern, value: AST.Expression) => AST.AssignmentExpression;
|
|
1161
|
-
update?: (node: AST.UpdateExpression) => AST.
|
|
1161
|
+
update?: (node: AST.UpdateExpression) => AST.Expression;
|
|
1162
1162
|
};
|
|
1163
1163
|
}
|
|
1164
1164
|
|
|
@@ -792,3 +792,14 @@ ripple_array.from_async = async function (arrayLike, map_fn, thisArg) {
|
|
|
792
792
|
export function ripple_object(obj) {
|
|
793
793
|
return obj;
|
|
794
794
|
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Returns the fallback value if the given value is undefined.
|
|
798
|
+
* @template T
|
|
799
|
+
* @param {T | undefined} value
|
|
800
|
+
* @param {T} fallback
|
|
801
|
+
* @returns {T}
|
|
802
|
+
*/
|
|
803
|
+
export function fallback(value, fallback) {
|
|
804
|
+
return value === undefined ? fallback : value;
|
|
805
|
+
}
|
|
@@ -182,4 +182,28 @@ describe('lazy destructuring', () => {
|
|
|
182
182
|
render(Test);
|
|
183
183
|
expect(container.querySelector('pre')!.textContent).toBe('1-99');
|
|
184
184
|
});
|
|
185
|
+
|
|
186
|
+
it('supports update expressions on lazy bindings with default values', () => {
|
|
187
|
+
component Test() {
|
|
188
|
+
const obj: { count?: number } = {};
|
|
189
|
+
let &{ count = 0 } = obj;
|
|
190
|
+
count++;
|
|
191
|
+
count++;
|
|
192
|
+
<pre>{obj.count}</pre>
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
render(Test);
|
|
196
|
+
expect(container.querySelector('pre')!.textContent).toBe('2');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('supports member access on lazy destructured objects', () => {
|
|
200
|
+
component Test() {
|
|
201
|
+
const obj = { user: { name: 'Alice', age: 30 } };
|
|
202
|
+
const &{ user } = obj;
|
|
203
|
+
<pre>{`${user.name}-${user.age}`}</pre>
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
render(Test);
|
|
207
|
+
expect(container.querySelector('pre')!.textContent).toBe('Alice-30');
|
|
208
|
+
});
|
|
185
209
|
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
describe('lazy destructuring', () => {
|
|
2
|
+
it('lazily accesses object properties', async () => {
|
|
3
|
+
component Inner(&{ a, b }: { a: number; b: string }) {
|
|
4
|
+
<pre>{`${a}-${b}`}</pre>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
component Test() {
|
|
8
|
+
<Inner a={1} b="hello" />
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { body } = await render(Test);
|
|
12
|
+
expect(body).toBeHtml('<pre>1-hello</pre>');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('supports default values in lazy object destructuring', async () => {
|
|
16
|
+
component Test() {
|
|
17
|
+
const obj = { a: 5 };
|
|
18
|
+
const &{ a, b = 99 } = obj;
|
|
19
|
+
<pre>{`${a}-${b}`}</pre>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { body } = await render(Test);
|
|
23
|
+
expect(body).toBeHtml('<pre>5-99</pre>');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('supports let lazy destructuring with assignment writeback', async () => {
|
|
27
|
+
component Test() {
|
|
28
|
+
const obj = { a: 1, b: 2 };
|
|
29
|
+
let &{ a, b } = obj;
|
|
30
|
+
a = 10;
|
|
31
|
+
b = 20;
|
|
32
|
+
<pre>{`${obj.a}-${obj.b}`}</pre>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const { body } = await render(Test);
|
|
36
|
+
expect(body).toBeHtml('<pre>10-20</pre>');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('supports compound assignment operators on lazy bindings', async () => {
|
|
40
|
+
component Test() {
|
|
41
|
+
const obj = { a: 5, b: 10 };
|
|
42
|
+
let &{ a, b } = obj;
|
|
43
|
+
a += 3;
|
|
44
|
+
b *= 2;
|
|
45
|
+
<pre>{`${obj.a}-${obj.b}`}</pre>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const { body } = await render(Test);
|
|
49
|
+
expect(body).toBeHtml('<pre>8-20</pre>');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('supports update expressions on lazy bindings', async () => {
|
|
53
|
+
component Test() {
|
|
54
|
+
const obj = { count: 0 };
|
|
55
|
+
let &{ count } = obj;
|
|
56
|
+
count++;
|
|
57
|
+
count++;
|
|
58
|
+
count--;
|
|
59
|
+
<pre>{obj.count}</pre>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const { body } = await render(Test);
|
|
63
|
+
expect(body).toBeHtml('<pre>1</pre>');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('supports update expressions on lazy bindings with default values', async () => {
|
|
67
|
+
component Test() {
|
|
68
|
+
const obj: { count?: number } = {};
|
|
69
|
+
let &{ count = 0 } = obj;
|
|
70
|
+
count++;
|
|
71
|
+
count++;
|
|
72
|
+
<pre>{obj.count}</pre>
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const { body } = await render(Test);
|
|
76
|
+
expect(body).toBeHtml('<pre>2</pre>');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('supports function params with lazy destructuring and default values', async () => {
|
|
80
|
+
component Test() {
|
|
81
|
+
function calc(&{ x, y = 100 }: { x: number; y?: number }) {
|
|
82
|
+
return x + y;
|
|
83
|
+
}
|
|
84
|
+
const a = calc({ x: 5, y: 10 });
|
|
85
|
+
const b = calc({ x: 5 });
|
|
86
|
+
<pre>{`${a}-${b}`}</pre>
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const { body } = await render(Test);
|
|
90
|
+
expect(body).toBeHtml('<pre>15-105</pre>');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('supports member access on lazy destructured objects', async () => {
|
|
94
|
+
component Test() {
|
|
95
|
+
const obj = { user: { name: 'Alice', age: 30 } };
|
|
96
|
+
const &{ user } = obj;
|
|
97
|
+
<pre>{`${user.name}-${user.age}`}</pre>
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const { body } = await render(Test);
|
|
101
|
+
expect(body).toBeHtml('<pre>Alice-30</pre>');
|
|
102
|
+
});
|
|
103
|
+
});
|