ripple 0.2.82 → 0.2.83
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 +59 -0
- package/src/compiler/phases/3-transform/client/index.js +21 -0
- package/src/runtime/array.js +4 -0
- package/src/runtime/internal/client/index.js +2 -0
- package/src/runtime/internal/client/runtime.js +9 -3
- package/src/utils/builders.js +1 -0
- package/tests/client/basic.test.ripple +27 -13
package/package.json
CHANGED
|
@@ -75,6 +75,21 @@ function RipplePlugin(config) {
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
if (code === 35) {
|
|
79
|
+
// # character
|
|
80
|
+
// Look ahead to see if this is followed by [ for tuple syntax
|
|
81
|
+
if (this.pos + 1 < this.input.length) {
|
|
82
|
+
const nextChar = this.input.charCodeAt(this.pos + 1);
|
|
83
|
+
if (nextChar === 91) { // [ character
|
|
84
|
+
// This is a tuple literal #[
|
|
85
|
+
// Consume both # and [
|
|
86
|
+
++this.pos; // consume #
|
|
87
|
+
++this.pos; // consume [
|
|
88
|
+
return this.finishToken(tt.bracketL, '#[');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
78
93
|
if (code === 64) {
|
|
79
94
|
// @ character
|
|
80
95
|
// Look ahead to see if this is followed by a valid identifier character
|
|
@@ -161,6 +176,50 @@ function RipplePlugin(config) {
|
|
|
161
176
|
return node;
|
|
162
177
|
}
|
|
163
178
|
|
|
179
|
+
parseExprAtom(refDestructuringErrors, forNew, forInit) {
|
|
180
|
+
// Check if this is a tuple literal starting with #[
|
|
181
|
+
if (this.type === tt.bracketL && this.value === '#[') {
|
|
182
|
+
return this.parseTrackedArrayExpression();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return super.parseExprAtom(refDestructuringErrors, forNew, forInit);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
parseTrackedArrayExpression() {
|
|
189
|
+
const node = this.startNode();
|
|
190
|
+
this.next(); // consume the '#['
|
|
191
|
+
|
|
192
|
+
node.elements = [];
|
|
193
|
+
|
|
194
|
+
// Parse array elements similar to regular array parsing
|
|
195
|
+
let first = true;
|
|
196
|
+
while (!this.eat(tt.bracketR)) {
|
|
197
|
+
if (!first) {
|
|
198
|
+
this.expect(tt.comma);
|
|
199
|
+
if (this.afterTrailingComma(tt.bracketR)) break;
|
|
200
|
+
} else {
|
|
201
|
+
first = false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (this.type === tt.comma) {
|
|
205
|
+
// Hole in array
|
|
206
|
+
node.elements.push(null);
|
|
207
|
+
} else if (this.type === tt.ellipsis) {
|
|
208
|
+
// Spread element
|
|
209
|
+
const element = this.parseSpread();
|
|
210
|
+
node.elements.push(element);
|
|
211
|
+
if (this.type === tt.comma && this.input.charCodeAt(this.pos) === 93) {
|
|
212
|
+
this.raise(this.pos, "Trailing comma is not permitted after the rest element");
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
// Regular element
|
|
216
|
+
node.elements.push(this.parseMaybeAssign(false));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return this.finishNode(node, 'TrackedArrayExpression');
|
|
221
|
+
}
|
|
222
|
+
|
|
164
223
|
parseExportDefaultDeclaration() {
|
|
165
224
|
// Check if this is "export default component"
|
|
166
225
|
if (this.value === 'component') {
|
|
@@ -269,6 +269,27 @@ const visitors = {
|
|
|
269
269
|
);
|
|
270
270
|
},
|
|
271
271
|
|
|
272
|
+
TrackedArrayExpression(node, context) {
|
|
273
|
+
if (context.state.to_ts) {
|
|
274
|
+
if (!context.state.imports.has(`import { TrackedArray } from 'ripple'`)) {
|
|
275
|
+
context.state.imports.add(`import { TrackedArray } from 'ripple'`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return b.new(
|
|
279
|
+
b.call(
|
|
280
|
+
b.member(b.id('TrackedArray'), b.id('from')),
|
|
281
|
+
node.elements.map((el) => context.visit(el)),
|
|
282
|
+
),
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return b.call(
|
|
287
|
+
'_$_.tracked_array',
|
|
288
|
+
b.array(node.elements.map((el) => context.visit(el))),
|
|
289
|
+
b.id('__block'),
|
|
290
|
+
);
|
|
291
|
+
},
|
|
292
|
+
|
|
272
293
|
MemberExpression(node, context) {
|
|
273
294
|
const parent = context.path.at(-1);
|
|
274
295
|
|
package/src/runtime/array.js
CHANGED
|
@@ -326,17 +326,23 @@ export function track(v, o, b) {
|
|
|
326
326
|
/** @type {Record<PropertyKey, any | null>} */
|
|
327
327
|
var descriptors = get_descriptors(v);
|
|
328
328
|
|
|
329
|
-
for (let i = 0, key, t; i < list.length; i++) {
|
|
329
|
+
for (let i = 0, key, t, exists = true; i < list.length; i++) {
|
|
330
330
|
key = list[i];
|
|
331
331
|
|
|
332
332
|
if (is_tracked_object(v[key])) {
|
|
333
333
|
t = v[key];
|
|
334
334
|
} else {
|
|
335
|
-
t =
|
|
335
|
+
t = tracked(undefined, b);
|
|
336
|
+
exists = !!descriptors[key];
|
|
337
|
+
if (exists) {
|
|
338
|
+
t = define_property(t, 'v', descriptors[key]);
|
|
339
|
+
}
|
|
336
340
|
}
|
|
337
341
|
|
|
338
342
|
out[i] = t;
|
|
339
|
-
|
|
343
|
+
if (exists) {
|
|
344
|
+
descriptors[key] = null;
|
|
345
|
+
}
|
|
340
346
|
}
|
|
341
347
|
|
|
342
348
|
var props = Reflect.ownKeys(descriptors);
|
package/src/utils/builders.js
CHANGED
|
@@ -379,8 +379,7 @@ describe('basic client', () => {
|
|
|
379
379
|
expect(container.querySelector('.count').textContent).toBe('0');
|
|
380
380
|
});
|
|
381
381
|
|
|
382
|
-
|
|
383
|
-
it.skip('renders multiple reactive lexical blocks with complexity', () => {
|
|
382
|
+
it('renders multiple reactive lexical blocks with complexity', () => {
|
|
384
383
|
|
|
385
384
|
component Basic() {
|
|
386
385
|
const count = 'count';
|
|
@@ -1276,12 +1275,7 @@ describe('basic client', () => {
|
|
|
1276
1275
|
let logs = [];
|
|
1277
1276
|
|
|
1278
1277
|
component App() {
|
|
1279
|
-
let count = track(0
|
|
1280
|
-
set(v) {
|
|
1281
|
-
logs.push('inside setter');
|
|
1282
|
-
return v;
|
|
1283
|
-
}
|
|
1284
|
-
});
|
|
1278
|
+
let count = track(0);
|
|
1285
1279
|
let name = track('Click Me');
|
|
1286
1280
|
|
|
1287
1281
|
function buttonRef(el) {
|
|
@@ -1294,9 +1288,11 @@ describe('basic client', () => {
|
|
|
1294
1288
|
<Child
|
|
1295
1289
|
class="my-button"
|
|
1296
1290
|
onClick={() => @name === 'Click Me' ? @name = 'Clicked' : @name = 'Click Me'}
|
|
1297
|
-
{count}
|
|
1291
|
+
{@count}
|
|
1298
1292
|
{ref buttonRef}
|
|
1299
|
-
>{@name}</Child
|
|
1293
|
+
>{@name}</Child>
|
|
1294
|
+
|
|
1295
|
+
<button onClick={() => @count++}>{'Increment Count'}</button>
|
|
1300
1296
|
}
|
|
1301
1297
|
|
|
1302
1298
|
component Child(props: PropsWithChildren<{ count: Tracked<number> }>) {
|
|
@@ -1306,7 +1302,6 @@ describe('basic client', () => {
|
|
|
1306
1302
|
<button {...@rest}><@children /></button>
|
|
1307
1303
|
}
|
|
1308
1304
|
<pre>{@count}</pre>
|
|
1309
|
-
<button onClick={() => @count++}>{'Increment Count'}</button>
|
|
1310
1305
|
}
|
|
1311
1306
|
|
|
1312
1307
|
render(App);
|
|
@@ -1327,12 +1322,11 @@ describe('basic client', () => {
|
|
|
1327
1322
|
|
|
1328
1323
|
expect(buttonClickMe.textContent).toBe('Clicked');
|
|
1329
1324
|
expect(countPre.textContent).toBe('1');
|
|
1330
|
-
expect(logs).toEqual(['ref called','inside setter']);
|
|
1331
1325
|
|
|
1332
1326
|
buttonIncrement.click();
|
|
1333
1327
|
flushSync();
|
|
1334
1328
|
|
|
1335
|
-
expect(logs).toEqual(['ref called','
|
|
1329
|
+
expect(logs).toEqual(['ref called','cleanup ref']);
|
|
1336
1330
|
});
|
|
1337
1331
|
|
|
1338
1332
|
it('errors on invalid value as null for track with split', () => {
|
|
@@ -1393,6 +1387,26 @@ describe('basic client', () => {
|
|
|
1393
1387
|
expect(pre.textContent).toBe('Invalid value: expected a non-tracked object');
|
|
1394
1388
|
});
|
|
1395
1389
|
|
|
1390
|
+
it('returns undefined for non-existent props in track with split', () => {
|
|
1391
|
+
component App() {
|
|
1392
|
+
const [a, b, rest] = track({a: 1, c: 1}, { split: ['a', 'b'] });
|
|
1393
|
+
|
|
1394
|
+
<pre>{@a}</pre>
|
|
1395
|
+
<pre>{String(@b)}</pre>
|
|
1396
|
+
<pre>{@rest.c}</pre>
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
render(App);
|
|
1400
|
+
|
|
1401
|
+
const preA = container.querySelectorAll('pre')[0];
|
|
1402
|
+
const preB = container.querySelectorAll('pre')[1];
|
|
1403
|
+
const preC = container.querySelectorAll('pre')[2];
|
|
1404
|
+
|
|
1405
|
+
expect(preA.textContent).toBe('1');
|
|
1406
|
+
expect(preB.textContent).toBe('undefined');
|
|
1407
|
+
expect(preC.textContent).toBe('1');
|
|
1408
|
+
});
|
|
1409
|
+
|
|
1396
1410
|
it('returns the same tracked object if plain track is called with a tracked object', () => {
|
|
1397
1411
|
component App() {
|
|
1398
1412
|
const t = track({a: 1, b: 2, c: 3});
|