ripple 0.2.24 → 0.2.26

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Ripple is a TypeScript UI framework for the web",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.2.24",
6
+ "version": "0.2.26",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index.js",
9
9
  "main": "src/runtime/index.js",
@@ -34,7 +34,7 @@ function visit_function(node, context) {
34
34
  if (binding !== null && is_tracked_name(name)) {
35
35
  const id = context.state.scope.generate('arg');
36
36
  node.params[i] = b.id(id);
37
- binding.kind = 'prop';
37
+ binding.kind = path.has_default_value ? 'prop_fallback' : 'prop';
38
38
 
39
39
  binding.transform = {
40
40
  read: (_) => b.call('$.get_property', b.id(id), b.literal(name)),
@@ -315,30 +315,57 @@ const visitors = {
315
315
  const binding = context.state.scope.get(name);
316
316
 
317
317
  if (binding !== null && is_tracked_name(name)) {
318
- binding.kind = 'prop';
318
+ binding.kind = path.has_default_value ? 'prop_fallback' : 'prop';
319
319
 
320
- binding.transform = {
321
- read: (_) => b.call('$.get_property', b.id('__props'), b.literal(name)),
322
- assign: (node, value) => {
323
- return b.call(
324
- '$.set_property',
325
- b.id('__props'),
326
- b.literal(name),
327
- value,
328
- b.id('__block'),
329
- );
330
- },
331
- update: (_) =>
332
- b.call(
333
- node.prefix ? '$.update_property_pre' : '$.update_property',
334
- b.id('__props'),
335
- b.literal(name),
336
- b.id('__block'),
337
- node.operator === '--' && b.literal(-1),
338
- ),
339
- };
320
+ if (path.has_default_value) {
321
+ binding.transform = {
322
+ read: (_) => b.call('$.get_computed', path.node),
323
+ assign: (node, value) => {
324
+ return b.call(
325
+ '$.set',
326
+ path.node,
327
+ value,
328
+ b.id('__block'),
329
+ );
330
+ },
331
+ update: (_) =>
332
+ b.call(
333
+ node.prefix ? '$.update_pre' : '$.update',
334
+ path.node,
335
+ b.id('__block'),
336
+ node.operator === '--' && b.literal(-1),
337
+ ),
338
+ };
339
+ } else {
340
+ binding.transform = {
341
+ read: (_) => b.call('$.get_property', b.id('__props'), b.literal(name)),
342
+ assign: (node, value) => {
343
+ return b.call(
344
+ '$.set_property',
345
+ b.id('__props'),
346
+ b.literal(name),
347
+ value,
348
+ b.id('__block'),
349
+ );
350
+ },
351
+ update: (_) =>
352
+ b.call(
353
+ node.prefix ? '$.update_property_pre' : '$.update_property',
354
+ b.id('__props'),
355
+ b.literal(name),
356
+ b.id('__block'),
357
+ node.operator === '--' && b.literal(-1),
358
+ ),
359
+ };
360
+ }
340
361
  }
341
362
  }
363
+ } else if (props.type === 'AssignmentPattern') {
364
+ error(
365
+ 'Props are always an object, use destructured props with default values instead',
366
+ context.state.analysis.module.filename,
367
+ props,
368
+ );
342
369
  }
343
370
  }
344
371
  const elements = [];
@@ -724,13 +724,16 @@ const visitors = {
724
724
  return b.function(node.id, node.params, b.block(body_statements));
725
725
  }
726
726
 
727
+ let props = b.id('__props');
728
+
727
729
  if (node.params.length > 0) {
728
- let props = node.params[0];
730
+ let props_param = node.params[0];
729
731
 
730
- if (props.type === 'Identifier') {
731
- delete props.typeAnnotation;
732
- } else if (props.type === 'ObjectPattern') {
733
- const paths = extract_paths(props);
732
+ if (props_param.type === 'Identifier') {
733
+ delete props_param.typeAnnotation;
734
+ props = props_param;
735
+ } else if (props_param.type === 'ObjectPattern') {
736
+ const paths = extract_paths(props_param);
734
737
 
735
738
  for (const path of paths) {
736
739
  const name = path.node.name;
@@ -742,6 +745,15 @@ const visitors = {
742
745
  prop_statements = [];
743
746
  }
744
747
  prop_statements.push(b.var(name, b.member(b.id('__props'), key)));
748
+ } else if (binding !== null && path.has_default_value) {
749
+ if (prop_statements === undefined) {
750
+ prop_statements = [];
751
+ }
752
+ const fallback = path.expression(b.id('__props'));
753
+
754
+ prop_statements.push(
755
+ b.var(name, b.call('$.computed', b.thunk(context.visit(fallback)), b.id('__block'))),
756
+ );
745
757
  }
746
758
  }
747
759
  }
@@ -763,11 +775,7 @@ const visitors = {
763
775
  return b.function(
764
776
  node.id,
765
777
  node.params.length > 0
766
- ? [
767
- b.id('__anchor'),
768
- node.params[0].type === 'Identifier' ? node.params[0] : b.id('__props'),
769
- b.id('__block'),
770
- ]
778
+ ? [b.id('__anchor'), props, b.id('__block')]
771
779
  : [b.id('__anchor'), b.id('_'), b.id('__block')],
772
780
  b.block([
773
781
  ...(prop_statements ?? []),
@@ -291,6 +291,8 @@ function get_hoisted_params(node, context) {
291
291
  if (binding !== null && !scope.declarations.has(reference) && binding.initial !== node) {
292
292
  if (binding.kind === 'prop') {
293
293
  push_unique(b.id('__props'));
294
+ } else if (binding.kind === 'prop_fallback') {
295
+ push_unique(b.id(binding.node.name));
294
296
  } else if (
295
297
  // imports don't need to be hoisted
296
298
  binding.declaration_kind !== 'import'
@@ -41,6 +41,7 @@ export {
41
41
  pop_component,
42
42
  untrack,
43
43
  use_prop,
44
+ fallback,
44
45
  } from './runtime.js';
45
46
 
46
47
  export { for_block as for } from './for.js';
@@ -1053,3 +1053,7 @@ export function pop_component() {
1053
1053
  export function use_prop() {
1054
1054
  return Symbol(USE_PROP);
1055
1055
  }
1056
+
1057
+ export function fallback(value, fallback) {
1058
+ return value === undefined ? fallback : value;
1059
+ }
package/src/utils/ast.js CHANGED
@@ -201,6 +201,10 @@ function _extract_paths(assignments = [], param, expression, update_expression,
201
201
  return assignments;
202
202
  }
203
203
 
204
+ export function build_fallback(expression, fallback) {
205
+ return b.call('$.fallback', expression, fallback);
206
+ }
207
+
204
208
  /**
205
209
  * @param {ESTree.AssignmentOperator} operator
206
210
  * @param {ESTree.Identifier | ESTree.MemberExpression} left
@@ -241,5 +241,59 @@ describe('composite components', () => {
241
241
  render(App);
242
242
 
243
243
  expect(container).toMatchSnapshot();
244
- })
244
+ });
245
+
246
+ it('correctly handles default prop values', () => {
247
+ component Child({ $foo = 456 }) {
248
+ <div>{$foo}</div>
249
+ }
250
+
251
+ component App(props) {
252
+ let $foo = 123;
253
+
254
+ <Child />
255
+ <Child {$foo} />
256
+ }
257
+
258
+ render(App);
259
+
260
+ expect(container.querySelectorAll('div')[0].textContent).toBe('456');
261
+ expect(container.querySelectorAll('div')[1].textContent).toBe('123');
262
+ });
263
+
264
+ it('correctly handles no props', () => {
265
+ component Child(props) {
266
+ <div>{props.$foo}</div>
267
+ }
268
+
269
+ component App(props) {
270
+ let $foo = 123;
271
+
272
+ <Child />
273
+ <Child {$foo} />
274
+ }
275
+
276
+ render(App);
277
+
278
+ expect(container.querySelectorAll('div')[0].textContent).toBe('');
279
+ expect(container.querySelectorAll('div')[1].textContent).toBe('123');
280
+ });
281
+
282
+ it('correctly handles no props #2', () => {
283
+ component Child({ $foo }) {
284
+ <div>{$foo}</div>
285
+ }
286
+
287
+ component App(props) {
288
+ let $foo = 123;
289
+
290
+ <Child />
291
+ <Child {$foo} />
292
+ }
293
+
294
+ render(App);
295
+
296
+ expect(container.querySelectorAll('div')[0].textContent).toBe('');
297
+ expect(container.querySelectorAll('div')[1].textContent).toBe('123');
298
+ });
245
299
  });