ripple 0.2.49 → 0.2.51
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 +2 -4
- package/package.json +2 -2
- package/src/compiler/phases/1-parse/index.js +9 -8
- package/src/compiler/phases/2-analyze/index.js +99 -188
- package/src/compiler/phases/3-transform/index.js +46 -242
- package/src/compiler/utils.js +24 -11
- package/src/runtime/internal/client/index.js +2 -19
- package/src/runtime/internal/client/runtime.js +24 -4
- package/src/runtime/internal/client/try.js +1 -1
- package/tests/accessors-props.test.ripple +19 -23
- package/tests/array.test.ripple +123 -112
- package/tests/basic.test.ripple +184 -152
- package/tests/map.test.ripple +13 -13
- package/tests/set.test.ripple +3 -3
- package/types/index.d.ts +1 -1
- package/src/runtime/internal/client/array.js +0 -352
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
<picture>
|
|
2
2
|
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/trueadm/ripple/master/assets/ripple-dark.png">
|
|
3
|
-
<img src="https://raw.githubusercontent.com/trueadm/ripple/master/assets/ripple-light.png" alt="Ripple - the elegant UI framework
|
|
3
|
+
<img src="https://raw.githubusercontent.com/trueadm/ripple/master/assets/ripple-light.png" alt="Ripple - the elegant TypeScript UI framework" />
|
|
4
4
|
</picture>
|
|
5
5
|
|
|
6
6
|
# What is Ripple?
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
Ripple is a TypeScript UI framework for the web. To find out more, view [Ripple's Github README](https://github.com/trueadm/ripple).
|
|
8
|
+
Ripple is an elegant TypeScript UI framework. To find out more, view [Ripple's Github README](https://github.com/trueadm/ripple).
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ripple",
|
|
3
|
-
"description": "Ripple is
|
|
3
|
+
"description": "Ripple is an elegant TypeScript UI framework",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Dominic Gannaway",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.51",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"module": "src/runtime/index.js",
|
|
9
9
|
"main": "src/runtime/index.js",
|
|
@@ -292,6 +292,14 @@ function RipplePlugin(config) {
|
|
|
292
292
|
this.next();
|
|
293
293
|
node.block = this.parseBlock();
|
|
294
294
|
node.handler = null;
|
|
295
|
+
|
|
296
|
+
if (this.value === 'pending') {
|
|
297
|
+
this.next();
|
|
298
|
+
node.pending = this.parseBlock();
|
|
299
|
+
} else {
|
|
300
|
+
node.pending = null;
|
|
301
|
+
}
|
|
302
|
+
|
|
295
303
|
if (this.type === tt._catch) {
|
|
296
304
|
var clause = this.startNode();
|
|
297
305
|
this.next();
|
|
@@ -310,14 +318,7 @@ function RipplePlugin(config) {
|
|
|
310
318
|
}
|
|
311
319
|
node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null;
|
|
312
320
|
|
|
313
|
-
if (
|
|
314
|
-
this.next();
|
|
315
|
-
node.async = this.parseBlock();
|
|
316
|
-
} else {
|
|
317
|
-
node.async = null;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (!node.handler && !node.finalizer && !node.async) {
|
|
321
|
+
if (!node.handler && !node.finalizer && !node.pending) {
|
|
321
322
|
this.raise(node.start, 'Missing catch or finally clause');
|
|
322
323
|
}
|
|
323
324
|
return this.finishNode(node, 'TryStatement');
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
is_inside_component,
|
|
9
9
|
is_ripple_import,
|
|
10
10
|
is_tracked_computed_property,
|
|
11
|
-
is_tracked_name,
|
|
12
11
|
is_void_element,
|
|
13
12
|
} from '../../utils.js';
|
|
14
13
|
import { extract_paths } from '../../../utils/ast.js';
|
|
@@ -16,7 +15,7 @@ import is_reference from 'is-reference';
|
|
|
16
15
|
import { prune_css } from './prune.js';
|
|
17
16
|
import { error } from '../../errors.js';
|
|
18
17
|
|
|
19
|
-
function
|
|
18
|
+
function mark_control_flow_has_template(path) {
|
|
20
19
|
for (let i = path.length - 1; i >= 0; i -= 1) {
|
|
21
20
|
const node = path[i];
|
|
22
21
|
|
|
@@ -31,10 +30,11 @@ function mark_for_loop_has_template(path) {
|
|
|
31
30
|
if (
|
|
32
31
|
node.type === 'ForStatement' ||
|
|
33
32
|
node.type === 'ForInStatement' ||
|
|
34
|
-
node.type === 'ForOfStatement'
|
|
33
|
+
node.type === 'ForOfStatement' ||
|
|
34
|
+
node.type === 'TryStatement' ||
|
|
35
|
+
node.type === 'IfStatement'
|
|
35
36
|
) {
|
|
36
37
|
node.metadata.has_template = true;
|
|
37
|
-
break;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -47,34 +47,6 @@ function visit_function(node, context) {
|
|
|
47
47
|
tracked: false,
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
if (node.params.length > 0) {
|
|
51
|
-
for (let i = 0; i < node.params.length; i += 1) {
|
|
52
|
-
const param = node.params[i];
|
|
53
|
-
|
|
54
|
-
if (param.type === 'ObjectPattern' || param.type === 'ArrayPattern') {
|
|
55
|
-
const paths = extract_paths(param);
|
|
56
|
-
const id = context.state.scope.generate('__arg');
|
|
57
|
-
const arg_id = b.id(id);
|
|
58
|
-
|
|
59
|
-
for (const path of paths) {
|
|
60
|
-
const name = path.node.name;
|
|
61
|
-
|
|
62
|
-
const expression = path.expression(arg_id);
|
|
63
|
-
const binding = context.state.scope.get(name);
|
|
64
|
-
|
|
65
|
-
if (binding !== null && is_tracked_name(name)) {
|
|
66
|
-
node.params[i] = b.id(id);
|
|
67
|
-
binding.kind = path.has_default_value ? 'prop_fallback' : 'prop';
|
|
68
|
-
|
|
69
|
-
binding.transform = {
|
|
70
|
-
read: (_, __, visit) => visit(expression),
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
50
|
context.next({
|
|
79
51
|
...context.state,
|
|
80
52
|
function_depth: context.state.function_depth + 1,
|
|
@@ -129,7 +101,7 @@ const visitors = {
|
|
|
129
101
|
|
|
130
102
|
if (
|
|
131
103
|
is_reference(node, /** @type {Node} */ (parent)) &&
|
|
132
|
-
|
|
104
|
+
node.tracked &&
|
|
133
105
|
binding?.node !== node
|
|
134
106
|
) {
|
|
135
107
|
mark_as_tracked(context.path);
|
|
@@ -155,18 +127,7 @@ const visitors = {
|
|
|
155
127
|
const parent = context.path.at(-1);
|
|
156
128
|
|
|
157
129
|
if (context.state.metadata?.tracking === false && parent.type !== 'AssignmentExpression') {
|
|
158
|
-
|
|
159
|
-
node.property.type === 'Identifier' &&
|
|
160
|
-
!node.computed &&
|
|
161
|
-
is_tracked_name(node.property.name)
|
|
162
|
-
) {
|
|
163
|
-
context.state.metadata.tracking = true;
|
|
164
|
-
} else if (
|
|
165
|
-
node.computed &&
|
|
166
|
-
is_tracked_computed_property(node.object, node.property, context)
|
|
167
|
-
) {
|
|
168
|
-
context.state.metadata.tracking = true;
|
|
169
|
-
}
|
|
130
|
+
context.state.metadata.tracking = true;
|
|
170
131
|
}
|
|
171
132
|
|
|
172
133
|
context.next();
|
|
@@ -184,32 +145,6 @@ const visitors = {
|
|
|
184
145
|
context.next();
|
|
185
146
|
},
|
|
186
147
|
|
|
187
|
-
ObjectExpression(node, context) {
|
|
188
|
-
for (const property of node.properties) {
|
|
189
|
-
if (
|
|
190
|
-
property.type === 'Property' &&
|
|
191
|
-
!property.computed &&
|
|
192
|
-
property.key.type === 'Identifier' &&
|
|
193
|
-
property.kind === 'init' &&
|
|
194
|
-
is_tracked_name(property.key.name)
|
|
195
|
-
) {
|
|
196
|
-
mark_as_tracked(context.path);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
context.next();
|
|
201
|
-
},
|
|
202
|
-
|
|
203
|
-
ArrayExpression(node, context) {
|
|
204
|
-
for (const element of node.elements) {
|
|
205
|
-
if (element !== null && element.type === 'Identifier' && is_tracked_name(element.name)) {
|
|
206
|
-
mark_as_tracked(context.path);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
context.next();
|
|
211
|
-
},
|
|
212
|
-
|
|
213
148
|
VariableDeclaration(node, context) {
|
|
214
149
|
const { state, visit, path } = context;
|
|
215
150
|
|
|
@@ -234,42 +169,17 @@ const visitors = {
|
|
|
234
169
|
const binding = state.scope.get(declarator.id.name);
|
|
235
170
|
|
|
236
171
|
if (binding !== null && parent?.type !== 'ForOfStatement') {
|
|
237
|
-
if (
|
|
238
|
-
binding.kind = 'tracked';
|
|
239
|
-
|
|
240
|
-
mark_as_tracked(path);
|
|
241
|
-
|
|
242
|
-
visit(declarator, { ...state, metadata });
|
|
243
|
-
|
|
244
|
-
if (init_is_untracked && metadata.tracking) {
|
|
245
|
-
metadata.tracking = false;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
binding.transform = {
|
|
249
|
-
read: (node) => {
|
|
250
|
-
return metadata.tracking && !metadata.await
|
|
251
|
-
? b.call('$.get_derived', node)
|
|
252
|
-
: b.call('$.get_tracked', node);
|
|
253
|
-
},
|
|
254
|
-
assign: (node, value) => {
|
|
255
|
-
return b.call('$.set', node, value, b.id('__block'));
|
|
256
|
-
},
|
|
257
|
-
update: (node) => {
|
|
258
|
-
return b.call(
|
|
259
|
-
node.prefix ? '$.update_pre' : '$.update',
|
|
260
|
-
node.argument,
|
|
261
|
-
b.id('__block'),
|
|
262
|
-
node.operator === '--' && b.literal(-1),
|
|
263
|
-
);
|
|
264
|
-
},
|
|
265
|
-
};
|
|
266
|
-
} else if (binding.initial?.type !== 'Literal') {
|
|
172
|
+
if (binding.initial?.type !== 'Literal') {
|
|
267
173
|
for (const ref of binding.references) {
|
|
268
174
|
const path = ref.path;
|
|
269
175
|
const parent_node = path?.at(-1);
|
|
270
176
|
|
|
271
177
|
// We're reading a computed property, which might mean it's a reactive property
|
|
272
|
-
if (
|
|
178
|
+
if (
|
|
179
|
+
!ref.node.tracked &&
|
|
180
|
+
parent_node?.type === 'MemberExpression' &&
|
|
181
|
+
parent_node.computed
|
|
182
|
+
) {
|
|
273
183
|
binding.transform = {
|
|
274
184
|
assign: (node, value, computed) => {
|
|
275
185
|
if (!computed) {
|
|
@@ -289,9 +199,7 @@ const visitors = {
|
|
|
289
199
|
}
|
|
290
200
|
} else {
|
|
291
201
|
const paths = extract_paths(declarator.id);
|
|
292
|
-
|
|
293
|
-
(path) => path.node.type === 'Identifier' && is_tracked_name(path.node.name),
|
|
294
|
-
);
|
|
202
|
+
|
|
295
203
|
for (const path of paths) {
|
|
296
204
|
if (path.node.tracked) {
|
|
297
205
|
error(
|
|
@@ -302,68 +210,7 @@ const visitors = {
|
|
|
302
210
|
}
|
|
303
211
|
}
|
|
304
212
|
|
|
305
|
-
|
|
306
|
-
const tmp = state.scope.generate('tmp');
|
|
307
|
-
declarator.transformed = b.id(tmp);
|
|
308
|
-
|
|
309
|
-
if (declarator.init !== null) {
|
|
310
|
-
visit(declarator.init, { ...state, metadata });
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (init_is_untracked && metadata.tracking) {
|
|
314
|
-
metadata.tracking = false;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
for (const path of paths) {
|
|
318
|
-
const binding = state.scope.get(path.node.name);
|
|
319
|
-
|
|
320
|
-
binding.transform = {
|
|
321
|
-
read: (node) => {
|
|
322
|
-
const value = path.expression?.(b.id(tmp));
|
|
323
|
-
|
|
324
|
-
if (metadata.tracking && metadata.await) {
|
|
325
|
-
// TODO
|
|
326
|
-
debugger;
|
|
327
|
-
} else if (metadata.tracking && !metadata.await) {
|
|
328
|
-
if (is_tracked_name(path.node.name) && value.type === 'MemberExpression') {
|
|
329
|
-
return b.call(
|
|
330
|
-
'$.old_get_property',
|
|
331
|
-
b.call('$.get_derived', value.object),
|
|
332
|
-
value.property.type === 'Identifier'
|
|
333
|
-
? b.literal(value.property.name)
|
|
334
|
-
: value.property,
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const key =
|
|
339
|
-
value.property.type === 'Identifier'
|
|
340
|
-
? b.key(value.property.name)
|
|
341
|
-
: value.property;
|
|
342
|
-
|
|
343
|
-
return b.member(
|
|
344
|
-
b.call('$.get_derived', value.object),
|
|
345
|
-
key,
|
|
346
|
-
key.type === 'Literal',
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if (is_tracked_name(path.node.name) && value.type === 'MemberExpression') {
|
|
351
|
-
return b.call(
|
|
352
|
-
'$.old_get_property',
|
|
353
|
-
value.object,
|
|
354
|
-
value.property.type === 'Identifier'
|
|
355
|
-
? b.literal(value.property.name)
|
|
356
|
-
: value.property,
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return value;
|
|
361
|
-
},
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
} else {
|
|
365
|
-
visit(declarator, state);
|
|
366
|
-
}
|
|
213
|
+
visit(declarator, state);
|
|
367
214
|
}
|
|
368
215
|
|
|
369
216
|
declarator.metadata = metadata;
|
|
@@ -398,8 +245,8 @@ const visitors = {
|
|
|
398
245
|
|
|
399
246
|
binding.transform = {
|
|
400
247
|
read: (_) => {
|
|
401
|
-
|
|
402
|
-
|
|
248
|
+
return path.expression(b.id('__props'));
|
|
249
|
+
},
|
|
403
250
|
assign: (node, value) => {
|
|
404
251
|
return b.assignment('=', path.expression(b.id('__props')), value);
|
|
405
252
|
},
|
|
@@ -450,15 +297,91 @@ const visitors = {
|
|
|
450
297
|
has_template: false,
|
|
451
298
|
};
|
|
452
299
|
context.next();
|
|
300
|
+
|
|
453
301
|
if (!node.metadata.has_template) {
|
|
454
302
|
error(
|
|
455
|
-
'
|
|
303
|
+
'Component for...of loops must contain a template in their body. Move the for loop into an effect if it does not render anything.',
|
|
456
304
|
context.state.analysis.module.filename,
|
|
457
305
|
node,
|
|
458
306
|
);
|
|
459
307
|
}
|
|
460
308
|
},
|
|
461
309
|
|
|
310
|
+
IfStatement(node, context) {
|
|
311
|
+
if (!is_inside_component(context)) {
|
|
312
|
+
return context.next();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
node.metadata = {
|
|
316
|
+
has_template: false,
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
context.visit(node.consequent, context.state);
|
|
320
|
+
|
|
321
|
+
if (!node.metadata.has_template) {
|
|
322
|
+
error(
|
|
323
|
+
'Component if statements must contain a template in their "then" body. Move the if statement into an effect if it does not render anything.',
|
|
324
|
+
context.state.analysis.module.filename,
|
|
325
|
+
node,
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (node.alternate) {
|
|
330
|
+
node.metadata = {
|
|
331
|
+
has_template: false,
|
|
332
|
+
};
|
|
333
|
+
context.visit(node.alternate, context.state);
|
|
334
|
+
|
|
335
|
+
if (!node.metadata.has_template) {
|
|
336
|
+
error(
|
|
337
|
+
'Component if statements must contain a template in their "else" body. Move the if statement into an effect if it does not render anything.',
|
|
338
|
+
context.state.analysis.module.filename,
|
|
339
|
+
node,
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
|
|
345
|
+
TryStatement(node, context) {
|
|
346
|
+
if (!is_inside_component(context)) {
|
|
347
|
+
return context.next();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (node.pending) {
|
|
351
|
+
node.metadata = {
|
|
352
|
+
has_template: false,
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
context.visit(node.block, context.state);
|
|
356
|
+
|
|
357
|
+
if (!node.metadata.has_template) {
|
|
358
|
+
error(
|
|
359
|
+
'Component try statements must contain a template in their main body. Move the try statement into an effect if it does not render anything.',
|
|
360
|
+
context.state.analysis.module.filename,
|
|
361
|
+
node,
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
node.metadata = {
|
|
366
|
+
has_template: false,
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
context.visit(node.pending, context.state);
|
|
370
|
+
|
|
371
|
+
if (!node.metadata.has_template) {
|
|
372
|
+
error(
|
|
373
|
+
'Component try statements must contain a template in their "pending" body. Rendering a pending fallback is required to have a template.',
|
|
374
|
+
context.state.analysis.module.filename,
|
|
375
|
+
node,
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (node.finalizer) {
|
|
381
|
+
context.visit(node.finalizer, context.state);
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
|
|
462
385
|
ForInStatement(node, context) {
|
|
463
386
|
if (is_inside_component(context)) {
|
|
464
387
|
error(
|
|
@@ -486,7 +409,7 @@ const visitors = {
|
|
|
486
409
|
const is_dom_element = is_element_dom_element(node, context);
|
|
487
410
|
const attribute_names = new Set();
|
|
488
411
|
|
|
489
|
-
|
|
412
|
+
mark_control_flow_has_template(path);
|
|
490
413
|
|
|
491
414
|
if (is_dom_element) {
|
|
492
415
|
const is_void = is_void_element(node.id.name);
|
|
@@ -547,11 +470,11 @@ const visitors = {
|
|
|
547
470
|
} else if (attr.type === 'AccessorAttribute') {
|
|
548
471
|
attribute_names.add(attr.name);
|
|
549
472
|
visit(attr.get, state);
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
473
|
+
if (attr.set) {
|
|
474
|
+
visit(attr.set, state);
|
|
475
|
+
}
|
|
553
476
|
} else if (attr.type === 'SpreadAttribute') {
|
|
554
|
-
|
|
477
|
+
visit(attr.argument, state);
|
|
555
478
|
} else if (attr.type === 'RefAttribute') {
|
|
556
479
|
visit(attr.argument, state);
|
|
557
480
|
}
|
|
@@ -602,18 +525,6 @@ const visitors = {
|
|
|
602
525
|
);
|
|
603
526
|
}
|
|
604
527
|
}
|
|
605
|
-
|
|
606
|
-
if (is_tracked_name(name)) {
|
|
607
|
-
attribute_names.forEach((n) => {
|
|
608
|
-
if (n.name.slice(1) === name) {
|
|
609
|
-
error(
|
|
610
|
-
`Cannot have both ${name} and ${name.slice(1)} on the same element`,
|
|
611
|
-
state.analysis.module.filename,
|
|
612
|
-
n,
|
|
613
|
-
);
|
|
614
|
-
}
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
528
|
}
|
|
618
529
|
|
|
619
530
|
return {
|
|
@@ -623,7 +534,7 @@ const visitors = {
|
|
|
623
534
|
},
|
|
624
535
|
|
|
625
536
|
Text(node, context) {
|
|
626
|
-
|
|
537
|
+
mark_control_flow_has_template(context.path);
|
|
627
538
|
context.next();
|
|
628
539
|
},
|
|
629
540
|
|