ripple 0.2.27 → 0.2.28

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.27",
6
+ "version": "0.2.28",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index.js",
9
9
  "main": "src/runtime/index.js",
@@ -90,6 +90,41 @@ function RipplePlugin(config) {
90
90
 
91
91
  jsx_parseAttribute() {
92
92
  let node = this.startNode();
93
+ const lookahead = this.lookahead();
94
+
95
+ if (lookahead.type?.label === ':') {
96
+ let id = this.startNode();
97
+ id.name = this.value;
98
+ node.name = id;
99
+ this.next();
100
+ this.finishNode(id, 'Identifier');
101
+
102
+ if (this.lookahead().value !== '=') {
103
+ this.unexpected();
104
+ }
105
+ this.next();
106
+ if (this.lookahead().type !== tt.braceL) {
107
+ this.unexpected();
108
+ }
109
+ this.next();
110
+ const value = this.jsx_parseAttributeValue();
111
+ const expression = value.expression;
112
+ node.get = null;
113
+ node.set = null;
114
+
115
+ if (expression.type == 'SequenceExpression') {
116
+ node.get = expression.expressions[0];
117
+ node.set = expression.expressions[1];
118
+ if (expression.expressions.length > 2) {
119
+ this.unexpected();
120
+ }
121
+ } else {
122
+ node.get = expression;
123
+ }
124
+
125
+ return this.finishNode(node, 'AccessorAttribute');
126
+ }
127
+
93
128
  if (this.eat(tt.braceL)) {
94
129
  if (this.type.label === '@') {
95
130
  this.next();
@@ -321,12 +321,7 @@ const visitors = {
321
321
  binding.transform = {
322
322
  read: (_) => b.call('$.get_computed', path.node),
323
323
  assign: (node, value) => {
324
- return b.call(
325
- '$.set',
326
- path.node,
327
- value,
328
- b.id('__block'),
329
- );
324
+ return b.call('$.set', path.node, value, b.id('__block'));
330
325
  },
331
326
  update: (_) =>
332
327
  b.call(
@@ -451,6 +446,12 @@ const visitors = {
451
446
  }
452
447
  }
453
448
  }
449
+ } else if (attr.type === 'AccessorAttribute') {
450
+ error(
451
+ 'Accessor props are not supported on DOM elements',
452
+ state.analysis.module.filename,
453
+ attr,
454
+ );
454
455
  }
455
456
  }
456
457
  } else {
@@ -459,6 +460,15 @@ const visitors = {
459
460
  if (attr.name.type === 'Identifier') {
460
461
  attribute_names.add(attr.name);
461
462
  }
463
+ } else if (attr.type === 'AccessorAttribute') {
464
+ if (!attr.name.name.startsWith('$')) {
465
+ error(
466
+ 'Accessor props must always be $ prefixed as they are reactive',
467
+ state.analysis.module.filename,
468
+ attr,
469
+ );
470
+ }
471
+ attribute_names.add(attr.name);
462
472
  }
463
473
  }
464
474
 
@@ -472,7 +482,7 @@ const visitors = {
472
482
  if (implicit_children) {
473
483
  error(
474
484
  'Cannot have both implicit and explicit children',
475
- context.state.analysis.module.filename,
485
+ state.analysis.module.filename,
476
486
  node,
477
487
  );
478
488
  }
@@ -482,7 +492,7 @@ const visitors = {
482
492
  if (explicit_children) {
483
493
  error(
484
494
  'Cannot have both implicit and explicit children',
485
- context.state.analysis.module.filename,
495
+ state.analysis.module.filename,
486
496
  node,
487
497
  );
488
498
  }
@@ -617,6 +617,52 @@ const visitors = {
617
617
  );
618
618
  } else if (attr.type === 'UseAttribute') {
619
619
  props.push(b.prop('init', b.call('$.use_prop'), visit(attr.argument, state), true));
620
+ } else if (attr.type === 'AccessorAttribute') {
621
+ // # means it's an accessor to the runtime
622
+ tracked.push(b.literal('#' + attr.name.name));
623
+ let get_expr;
624
+
625
+ if (
626
+ attr.get.type === 'FunctionExpression' ||
627
+ attr.get.type === 'ArrowFunctionExpression'
628
+ ) {
629
+ get_expr = context.state.scope.generate(attr.name.name + '_get');
630
+
631
+ state.init.push(b.const(get_expr, visit(attr.get, state)));
632
+ } else {
633
+ get_expr = visit(attr.get, state);
634
+ }
635
+
636
+ props.push(
637
+ b.prop('get', attr.name, b.function(null, [], b.block([b.return(b.call(get_expr))]))),
638
+ );
639
+
640
+ if (attr.set) {
641
+ let set_expr;
642
+
643
+ if (
644
+ attr.set.type === 'FunctionExpression' ||
645
+ attr.set.type === 'ArrowFunctionExpression'
646
+ ) {
647
+ set_expr = context.state.scope.generate(attr.name.name + '_set');
648
+
649
+ state.init.push(b.const(set_expr, visit(attr.set, state)));
650
+ } else {
651
+ set_expr = visit(attr.set, state);
652
+ }
653
+
654
+ props.push(
655
+ b.prop(
656
+ 'set',
657
+ attr.name,
658
+ b.function(
659
+ null,
660
+ [b.id('__value')],
661
+ b.block([b.return(b.call(set_expr, b.id('__value')))]),
662
+ ),
663
+ ),
664
+ );
665
+ }
620
666
  } else {
621
667
  throw new Error('TODO');
622
668
  }
@@ -802,7 +848,8 @@ const visitors = {
802
848
  if (
803
849
  left.type === 'MemberExpression' &&
804
850
  left.property.type === 'Identifier' &&
805
- is_tracked_name(left.property.name)
851
+ is_tracked_name(left.property.name) &&
852
+ left.property.name !== '$length'
806
853
  ) {
807
854
  return b.call(
808
855
  '$.set_property',
@@ -1,3 +1,5 @@
1
+ /** @import { Block } from '#client' */
2
+
1
3
  import { TRACKED_OBJECT } from './internal/client/constants.js';
2
4
  import { get, increment, scope, set, tracked } from './internal/client/runtime.js';
3
5
 
@@ -35,6 +37,16 @@ const introspect_methods = [
35
37
 
36
38
  let init = false;
37
39
 
40
+ /**
41
+ * @param {Block | null} block
42
+ * @throws {Error}
43
+ */
44
+ function check_block(block) {
45
+ if (block === null) {
46
+ throw new Error('Cannot set $length outside of a reactive context');
47
+ }
48
+ }
49
+
38
50
  export class RippleArray extends Array {
39
51
  #tracked_elements = [];
40
52
  #tracked_index;
@@ -51,6 +63,7 @@ export class RippleArray extends Array {
51
63
  super(...elements);
52
64
 
53
65
  var block = scope();
66
+ check_block(block);
54
67
  var tracked_elements = this.#tracked_elements;
55
68
 
56
69
  for (var i = 0; i < this.length; i++) {
@@ -79,6 +92,7 @@ export class RippleArray extends Array {
79
92
 
80
93
  fill() {
81
94
  var block = scope();
95
+ check_block(block);
82
96
  var tracked_elements = this.#tracked_elements;
83
97
 
84
98
  super.fill();
@@ -89,6 +103,7 @@ export class RippleArray extends Array {
89
103
 
90
104
  reverse() {
91
105
  var block = scope();
106
+ check_block(block);
92
107
  var tracked_elements = this.#tracked_elements;
93
108
 
94
109
  super.reverse();
@@ -99,6 +114,7 @@ export class RippleArray extends Array {
99
114
 
100
115
  sort(fn) {
101
116
  var block = scope();
117
+ check_block(block);
102
118
  var tracked_elements = this.#tracked_elements;
103
119
 
104
120
  super.sort(fn);
@@ -107,8 +123,13 @@ export class RippleArray extends Array {
107
123
  }
108
124
  }
109
125
 
126
+ /**
127
+ * @param {...any} elements
128
+ * @returns {number}
129
+ */
110
130
  unshift(...elements) {
111
131
  var block = scope();
132
+ check_block(block);
112
133
  var tracked_elements = this.#tracked_elements;
113
134
 
114
135
  super.unshift(...elements);
@@ -117,11 +138,14 @@ export class RippleArray extends Array {
117
138
  }
118
139
  tracked_elements.unshift(...elements.map(() => tracked(0, block)));
119
140
 
120
- set(this.#tracked_index, this.length, block);
141
+ var length = this.length;
142
+ set(this.#tracked_index, length, block);
143
+ return length;
121
144
  }
122
145
 
123
146
  shift() {
124
147
  var block = scope();
148
+ check_block(block);
125
149
  var tracked_elements = this.#tracked_elements;
126
150
 
127
151
  super.shift();
@@ -133,8 +157,13 @@ export class RippleArray extends Array {
133
157
  set(this.#tracked_index, this.length, block);
134
158
  }
135
159
 
160
+ /**
161
+ * @param {...any} elements
162
+ * @returns {number}
163
+ */
136
164
  push(...elements) {
137
165
  var block = scope();
166
+ check_block(block);
138
167
  var start_index = this.length;
139
168
  var tracked_elements = this.#tracked_elements;
140
169
 
@@ -143,11 +172,14 @@ export class RippleArray extends Array {
143
172
  for (var i = 0; i < elements.length; i++) {
144
173
  tracked_elements[start_index + i] = tracked(0, block);
145
174
  }
175
+ var length = this.length;
146
176
  set(this.#tracked_index, this.length, block);
177
+ return length;
147
178
  }
148
179
 
149
180
  pop() {
150
181
  var block = scope();
182
+ check_block(block);
151
183
  var tracked_elements = this.#tracked_elements;
152
184
 
153
185
  super.pop();
@@ -159,8 +191,15 @@ export class RippleArray extends Array {
159
191
  set(this.#tracked_index, this.length, block);
160
192
  }
161
193
 
194
+ /**
195
+ * @param {number} start
196
+ * @param {number} [delete_count]
197
+ * @param {...any} elements
198
+ * @returns {any[]}
199
+ */
162
200
  splice(start, delete_count, ...elements) {
163
201
  var block = scope();
202
+ check_block(block);
164
203
  var tracked_elements = this.#tracked_elements;
165
204
 
166
205
  super.splice(start, delete_count, ...elements);
@@ -182,6 +221,7 @@ export class RippleArray extends Array {
182
221
 
183
222
  set $length(length) {
184
223
  var block = scope();
224
+ check_block(block);
185
225
  var tracked_elements = this.#tracked_elements;
186
226
 
187
227
  if (length !== this.$length) {
@@ -189,13 +229,12 @@ export class RippleArray extends Array {
189
229
  increment(tracked_elements[i], block);
190
230
  }
191
231
  this.length = length;
232
+ set(this.#tracked_index, length, block);
192
233
  tracked_elements.length = length;
193
-
194
- return true;
195
234
  }
196
- return false;
197
235
  }
198
236
 
237
+ /** @param {number} _ */
199
238
  set length(_) {
200
239
  throw new Error('Cannot set length on RippleArray, use $length instead');
201
240
  }
@@ -206,6 +245,10 @@ export class RippleArray extends Array {
206
245
  }
207
246
  }
208
247
 
248
+ /**
249
+ * @param {RippleArray} array
250
+ * @returns {any[]}
251
+ */
209
252
  export function get_all_elements(array) {
210
253
  var tracked_elements = array[TRACKED_OBJECT];
211
254
  var arr = [];
@@ -18,6 +18,7 @@ import {
18
18
  active_block,
19
19
  active_component,
20
20
  active_reaction,
21
+ is_block_dirty,
21
22
  run_block,
22
23
  run_teardown,
23
24
  schedule_update,
@@ -93,21 +94,28 @@ export function async(fn) {
93
94
  });
94
95
  }
95
96
 
97
+ /**
98
+ * @param {Element} element
99
+ * @param {() => (element: Element) => (void | (() => void))} get_fn
100
+ * @returns {Block}
101
+ */
96
102
  export function use(element, get_fn) {
97
- var use_obj = undefined;
103
+ /** @type {(element: Element) => (void | (() => void) | undefined)} */
104
+ var use_fn;
105
+ /** @type {Block | null} */
98
106
  var e;
99
107
 
100
108
  return block(RENDER_BLOCK, () => {
101
- if (use_obj !== (use_obj = get_fn())) {
109
+ if (use_fn !== (use_fn = get_fn())) {
102
110
  if (e) {
103
111
  destroy_block(e);
104
112
  e = null;
105
113
  }
106
114
 
107
- if (use_obj) {
115
+ if (use_fn) {
108
116
  e = branch(() => {
109
117
  effect(() => {
110
- return use_obj(element);
118
+ return use_fn(element);
111
119
  });
112
120
  });
113
121
  }
@@ -115,14 +123,27 @@ export function use(element, get_fn) {
115
123
  });
116
124
  }
117
125
 
126
+ /**
127
+ * @param {() => void} fn
128
+ * @returns {Block}
129
+ */
118
130
  export function root(fn) {
119
131
  return block(ROOT_BLOCK, fn);
120
132
  }
121
133
 
134
+ /**
135
+ * @param {() => void} fn
136
+ * @param {any} state
137
+ * @returns {Block}
138
+ */
122
139
  export function create_try_block(fn, state) {
123
140
  return block(TRY_BLOCK, fn, state);
124
141
  }
125
142
 
143
+ /**
144
+ * @param {Block} block
145
+ * @param {Block} parent_block
146
+ */
126
147
  function push_block(block, parent_block) {
127
148
  var parent_last = parent_block.last;
128
149
  if (parent_last === null) {
@@ -174,6 +195,10 @@ export function block(flags, fn, state = null) {
174
195
  return block;
175
196
  }
176
197
 
198
+ /**
199
+ * @param {Block} parent
200
+ * @param {boolean} [remove_dom]
201
+ */
177
202
  export function destroy_block_children(parent, remove_dom = false) {
178
203
  var block = parent.first;
179
204
  parent.first = parent.last = null;
@@ -187,6 +212,10 @@ export function destroy_block_children(parent, remove_dom = false) {
187
212
  }
188
213
  }
189
214
 
215
+ /**
216
+ * @param {Block} parent
217
+ * @param {boolean} [remove_dom]
218
+ */
190
219
  export function destroy_non_branch_children(parent, remove_dom = false) {
191
220
  var block = parent.first;
192
221
 
@@ -207,6 +236,9 @@ export function destroy_non_branch_children(parent, remove_dom = false) {
207
236
  }
208
237
  }
209
238
 
239
+ /**
240
+ * @param {Block} block
241
+ */
210
242
  export function unlink_block(block) {
211
243
  var parent = block.p;
212
244
  var prev = block.prev;
@@ -221,6 +253,9 @@ export function unlink_block(block) {
221
253
  }
222
254
  }
223
255
 
256
+ /**
257
+ * @param {Block} block
258
+ */
224
259
  export function pause_block(block) {
225
260
  if ((block.f & PAUSED) !== 0) {
226
261
  return;
@@ -238,6 +273,9 @@ export function pause_block(block) {
238
273
  run_teardown(block);
239
274
  }
240
275
 
276
+ /**
277
+ * @param {Block} block
278
+ */
241
279
  export function resume_block(block) {
242
280
  if ((block.f & PAUSED) === 0) {
243
281
  return;
@@ -257,7 +295,12 @@ export function resume_block(block) {
257
295
  }
258
296
  }
259
297
 
298
+ /**
299
+ * @param {Block} target_block
300
+ * @returns {boolean}
301
+ */
260
302
  export function is_destroyed(target_block) {
303
+ /** @type {Block | null} */
261
304
  var block = target_block;
262
305
 
263
306
  while (block !== null) {
@@ -274,6 +317,10 @@ export function is_destroyed(target_block) {
274
317
  return true;
275
318
  }
276
319
 
320
+ /**
321
+ * @param {Block} block
322
+ * @param {boolean} [remove_dom]
323
+ */
277
324
  export function destroy_block(block, remove_dom = true) {
278
325
  block.f ^= DESTROYED;
279
326
 
@@ -28,7 +28,7 @@ import {
28
28
  USE_PROP,
29
29
  } from './constants';
30
30
  import { capture, suspend } from './try.js';
31
- import { define_property, is_array } from './utils';
31
+ import { define_property, get_descriptor, is_array } from './utils';
32
32
  import {
33
33
  object_keys as original_object_keys,
34
34
  object_values as original_object_values,
@@ -192,6 +192,7 @@ function run_computed(computed) {
192
192
  * @param {Block} block
193
193
  */
194
194
  export function handle_error(error, block) {
195
+ /** @type {Block | null} */
195
196
  var current = block;
196
197
 
197
198
  while (current !== null) {
@@ -230,6 +231,7 @@ export function run_block(block) {
230
231
 
231
232
  if (typeof res === 'function') {
232
233
  block.t = res;
234
+ /** @type {Block | null} */
233
235
  let current = block;
234
236
 
235
237
  while (current !== null && (current.f & CONTAINS_TEARDOWN) === 0) {
@@ -332,7 +334,7 @@ function is_tracking_dirty(tracking) {
332
334
  /**
333
335
  * @param {Block} block
334
336
  */
335
- function is_block_dirty(block) {
337
+ export function is_block_dirty(block) {
336
338
  var flags = block.f;
337
339
 
338
340
  if ((flags & (ROOT_BLOCK | BRANCH_BLOCK)) !== 0) {
@@ -407,6 +409,7 @@ export function deferred(fn) {
407
409
  var parent = active_block;
408
410
  var block = active_scope;
409
411
  var res = [UNINITIALIZED];
412
+ // TODO implement DEFERRED flag on tracked
410
413
  var t = tracked(UNINITIALIZED, block, DEFERRED);
411
414
  var tracked_properties = [t];
412
415
  var prev_value = UNINITIALIZED;
@@ -535,7 +538,7 @@ function flush_updates(root_block) {
535
538
  }
536
539
  }
537
540
 
538
- /** @type {Block} */
541
+ /** @type {Block | null} */
539
542
  var parent = current.p;
540
543
  current = current.next;
541
544
 
@@ -626,7 +629,7 @@ export function schedule_update(block) {
626
629
  if ((flags & ROOT_BLOCK) !== 0) {
627
630
  break;
628
631
  }
629
- current = current.p;
632
+ current = /** @type {Block} */ (current.p);
630
633
  }
631
634
 
632
635
  queued_root_blocks.push(current);
@@ -773,6 +776,11 @@ export function flush_sync(fn) {
773
776
  }
774
777
  }
775
778
 
779
+ /**
780
+ * @template T
781
+ * @param {() => T} fn
782
+ * @returns {T & { [SPREAD_OBJECT]: () => T }}
783
+ */
776
784
  export function tracked_spread_object(fn) {
777
785
  var obj = fn();
778
786
 
@@ -784,7 +792,14 @@ export function tracked_spread_object(fn) {
784
792
  return obj;
785
793
  }
786
794
 
795
+ /**
796
+ * @param {any} obj
797
+ * @param {string[]} properties
798
+ * @param {Block} block
799
+ * @returns {object}
800
+ */
787
801
  export function tracked_object(obj, properties, block) {
802
+ /** @type {Record<string, Tracked | Computed>} */
788
803
  var tracked_properties = obj[TRACKED_OBJECT];
789
804
 
790
805
  if (tracked_properties === undefined) {
@@ -797,27 +812,42 @@ export function tracked_object(obj, properties, block) {
797
812
 
798
813
  for (var i = 0; i < properties.length; i++) {
799
814
  var property = properties[i];
800
- var initial = obj[property];
815
+ /** @type {Tracked | Computed} */
801
816
  var tracked_property;
802
817
 
803
- if (typeof initial === 'function' && initial[COMPUTED_PROPERTY] === true) {
804
- tracked_property = computed(initial, block);
805
- initial = run_computed(tracked_property);
818
+ // accessor passed in, to avoid an expensive get_descriptor call
819
+ // in the fast path
820
+ if (property[0] === '#') {
821
+ property = property.slice(1);
822
+ var descriptor = /** @type {PropertyDescriptor} */ (get_descriptor(obj, property));
823
+ var desc_get = descriptor.get;
824
+ tracked_property = computed(desc_get, block);
825
+ /** @type {any} */
826
+ var initial = run_computed(/** @type {Computed} */ (tracked_property));
806
827
  obj[property] = initial;
807
- // TODO If nothing is tracked in the computed function, we can make it a standard tracked
808
- // however this is more allocations, so we probably want to minimize this
809
- // if (tracked_property.d === null) {
810
- // tracked_property = tracked(initial, block);
811
- // }
812
828
  } else {
813
- tracked_property = tracked(initial, block);
829
+ var initial = obj[property];
830
+
831
+ if (typeof initial === 'function' && initial[COMPUTED_PROPERTY] === true) {
832
+ tracked_property = computed(initial, block);
833
+ initial = run_computed(/** @type {Computed} */ (tracked_property));
834
+ obj[property] = initial;
835
+ } else {
836
+ tracked_property = tracked(initial, block);
837
+ }
814
838
  }
839
+
815
840
  tracked_properties[property] = tracked_property;
816
841
  }
817
842
 
818
843
  return obj;
819
844
  }
820
845
 
846
+ /**
847
+ * @template T
848
+ * @param {() => T} fn
849
+ * @returns {() => T}
850
+ */
821
851
  export function computed_property(fn) {
822
852
  define_property(fn, COMPUTED_PROPERTY, {
823
853
  value: true,
@@ -1002,6 +1032,12 @@ export function spread_object(obj) {
1002
1032
  return values;
1003
1033
  }
1004
1034
 
1035
+ /**
1036
+ * @template T
1037
+ * @param {Block} block
1038
+ * @param {() => T} fn
1039
+ * @returns {T}
1040
+ */
1005
1041
  export function with_scope(block, fn) {
1006
1042
  var previous_scope = active_scope;
1007
1043
  try {
@@ -1054,6 +1090,12 @@ export function use_prop() {
1054
1090
  return Symbol(USE_PROP);
1055
1091
  }
1056
1092
 
1093
+ /**
1094
+ * @template T
1095
+ * @param {T | undefined} value
1096
+ * @param {T} fallback
1097
+ * @returns {T}
1098
+ */
1057
1099
  export function fallback(value, fallback) {
1058
1100
  return value === undefined ? fallback : value;
1059
- }
1101
+ }
@@ -5,7 +5,7 @@ export type Component = {
5
5
  e: null | Array<{
6
6
  b: Block;
7
7
  fn: Function;
8
- r: null | Block;
8
+ r: null | Block | Computed;
9
9
  }>;
10
10
  p: null | Component;
11
11
  m: boolean;
@@ -40,10 +40,11 @@ export type Block = {
40
40
  first: null | Block;
41
41
  f: number;
42
42
  fn: any;
43
- last: null;
44
- next: null;
43
+ last: null | Block;
44
+ next: null | Block;
45
45
  p: null | Block;
46
- prev: null;
46
+ prev: null | Block;
47
47
  s: any;
48
- t: Tracked | null;
48
+ // teardown function
49
+ t: (() => {}) | null;
49
50
  };