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 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 for the web" />
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
- > Currently, this project is still in early development, and should not be used in production.
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 a TypeScript UI framework for the web",
3
+ "description": "Ripple is an elegant TypeScript UI framework",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.2.49",
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 (this.value === 'async') {
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 mark_for_loop_has_template(path) {
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
- (is_tracked_name(node) || node.tracked) &&
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
- if (
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 (is_tracked_name(declarator.id.name)) {
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 (!ref.node.tracked && parent_node?.type === 'MemberExpression' && parent_node.computed) {
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
- const has_tracked = paths.some(
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
- if (has_tracked) {
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
- return path.expression(b.id('__props'))
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
- 'For...of loops must contain a template in their body. Move the for loop into an effect if it does not render anything.',
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
- mark_for_loop_has_template(path);
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
- if (attr.set) {
551
- visit(attr.set, state);
552
- }
473
+ if (attr.set) {
474
+ visit(attr.set, state);
475
+ }
553
476
  } else if (attr.type === 'SpreadAttribute') {
554
- visit(attr.argument, state);
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
- mark_for_loop_has_template(context.path);
537
+ mark_control_flow_has_template(context.path);
627
538
  context.next();
628
539
  },
629
540