ripple 0.2.28 → 0.2.31

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.28",
6
+ "version": "0.2.31",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index.js",
9
9
  "main": "src/runtime/index.js",
@@ -424,12 +424,22 @@ function RipplePlugin(config) {
424
424
  // This node is used for Prettier, we don't actually need
425
425
  // the node for Ripple's transform process
426
426
  element.children = [component.css];
427
+ // Ensure we escape JSX <tag></tag> context
428
+ const tokContexts = this.acornTypeScript.tokContexts;
429
+ const curContext = this.curContext();
430
+
431
+ if (curContext === tokContexts.tc_expr) {
432
+ this.context.pop();
433
+ }
434
+
427
435
  return element;
428
436
  } else {
437
+ this.enterScope(0);
429
438
  this.parseTemplateBody(element.children);
439
+ this.exitScope();
430
440
  }
441
+ // Ensure we escape JSX <tag></tag> context
431
442
  const tokContexts = this.acornTypeScript.tokContexts;
432
-
433
443
  const curContext = this.curContext();
434
444
 
435
445
  if (curContext === tokContexts.tc_expr) {
@@ -6,6 +6,7 @@ import {
6
6
  is_event_attribute,
7
7
  is_inside_component,
8
8
  is_ripple_import,
9
+ is_tracked_computed_property,
9
10
  is_tracked_name,
10
11
  } from '../../utils.js';
11
12
  import { extract_paths } from '../../../utils/ast.js';
@@ -101,15 +102,21 @@ const visitors = {
101
102
  MemberExpression(node, context) {
102
103
  const parent = context.path.at(-1);
103
104
 
104
- if (
105
- context.state.metadata?.tracking === false &&
106
- node.property.type === 'Identifier' &&
107
- !node.computed &&
108
- is_tracked_name(node.property.name) &&
109
- parent.type !== 'AssignmentExpression'
110
- ) {
111
- context.state.metadata.tracking = true;
105
+ if (context.state.metadata?.tracking === false && parent.type !== 'AssignmentExpression') {
106
+ if (
107
+ node.property.type === 'Identifier' &&
108
+ !node.computed &&
109
+ is_tracked_name(node.property.name)
110
+ ) {
111
+ context.state.metadata.tracking = true;
112
+ } else if (
113
+ node.computed &&
114
+ is_tracked_computed_property(node.object, node.property, context)
115
+ ) {
116
+ context.state.metadata.tracking = true;
117
+ }
112
118
  }
119
+
113
120
  context.next();
114
121
  },
115
122
 
@@ -151,6 +158,13 @@ const visitors = {
151
158
  const { state, visit, path } = context;
152
159
 
153
160
  for (const declarator of node.declarations) {
161
+ if (is_inside_component(context) && node.kind === 'var') {
162
+ error(
163
+ 'var declarations are not allowed in components, use let or const instead',
164
+ state.analysis.module.filename,
165
+ declarator,
166
+ );
167
+ }
154
168
  const metadata = { tracking: false, await: false };
155
169
  const parent = path.at(-1);
156
170
  const init_is_untracked =
@@ -205,7 +219,7 @@ const visitors = {
205
219
  if (!computed) {
206
220
  return node;
207
221
  }
208
- return b.call('$.set_property', node, visit(computed), value, b.id('__block'));
222
+ return b.call('$.set_property', node, computed, value, b.id('__block'));
209
223
  },
210
224
  };
211
225
  break;
@@ -19,6 +19,8 @@ import {
19
19
  is_ripple_import,
20
20
  is_declared_within_component,
21
21
  is_inside_call_expression,
22
+ is_tracked_computed_property,
23
+ is_value_static,
22
24
  } from '../../utils.js';
23
25
  import is_reference from 'is-reference';
24
26
  import { extract_paths, object } from '../../../utils/ast.js';
@@ -133,6 +135,7 @@ const visitors = {
133
135
  }
134
136
 
135
137
  if (
138
+ !is_inside_component(context, true) ||
136
139
  context.state.to_ts ||
137
140
  (parent?.type === 'MemberExpression' && parent.property === node) ||
138
141
  is_inside_call_expression(context) ||
@@ -164,6 +167,14 @@ const visitors = {
164
167
  context.state.metadata.tracking = true;
165
168
  }
166
169
 
170
+ if (!is_inside_component(context, true) || is_inside_call_expression(context)) {
171
+ return context.next();
172
+ }
173
+
174
+ if (is_value_static(node)) {
175
+ return context.next();
176
+ }
177
+
167
178
  return b.call(
168
179
  '$.with_scope',
169
180
  b.id('__block'),
@@ -187,7 +198,10 @@ const visitors = {
187
198
  : property.type === 'Literal' && is_tracked_name(property.value);
188
199
 
189
200
  // TODO should we enforce that the identifier is tracked too?
190
- if ((node.computed && property.type === 'Identifier') || tracked_name) {
201
+ if (
202
+ (node.computed && is_tracked_computed_property(node.object, node.property, context)) ||
203
+ tracked_name
204
+ ) {
191
205
  if (context.state.metadata?.tracking === false) {
192
206
  context.state.metadata.tracking = true;
193
207
  }
@@ -572,7 +586,18 @@ const visitors = {
572
586
  );
573
587
  }
574
588
 
575
- transform_children(node.children, { visit, state, root: false });
589
+ const init = [];
590
+ const update = [];
591
+
592
+ transform_children(node.children, { visit, state: { ...state, init, update }, root: false });
593
+
594
+ if (init.length > 0) {
595
+ state.init.push(b.block(init));
596
+ }
597
+
598
+ if (update.length > 0) {
599
+ state.init.push(b.stmt(b.call('$.render', b.thunk(b.block(update)))));
600
+ }
576
601
 
577
602
  state.template.push(`</${node.id.name}>`);
578
603
  } else {
@@ -845,19 +870,20 @@ const visitors = {
845
870
 
846
871
  const left = node.left;
847
872
 
848
- if (
849
- left.type === 'MemberExpression' &&
850
- left.property.type === 'Identifier' &&
851
- is_tracked_name(left.property.name) &&
852
- left.property.name !== '$length'
853
- ) {
854
- return b.call(
855
- '$.set_property',
856
- context.visit(left.object),
857
- left.computed ? context.visit(left.property) : b.literal(left.property.name),
858
- visit_assignment_expression(node, context, build_assignment) ?? context.next(),
859
- b.id('__block'),
860
- );
873
+ if (left.type === 'MemberExpression') {
874
+ if (left.property.type === 'Identifier' && is_tracked_name(left.property.name)) {
875
+ if (left.property.name !== '$length') {
876
+ return b.call(
877
+ '$.set_property',
878
+ context.visit(left.object),
879
+ left.computed ? context.visit(left.property) : b.literal(left.property.name),
880
+ visit_assignment_expression(node, context, build_assignment) ?? context.next(),
881
+ b.id('__block'),
882
+ );
883
+ }
884
+ } else if (!is_tracked_computed_property(left.object, left.property, context)) {
885
+ return context.next();
886
+ }
861
887
  }
862
888
 
863
889
  const visited = visit_assignment_expression(node, context, build_assignment) ?? context.next();
@@ -884,7 +910,8 @@ const visitors = {
884
910
  if (
885
911
  argument.type === 'MemberExpression' &&
886
912
  ((argument.property.type === 'Identifier' && is_tracked_name(argument.property.name)) ||
887
- argument.computed)
913
+ (argument.computed &&
914
+ is_tracked_computed_property(argument.object, argument.property, context)))
888
915
  ) {
889
916
  return b.call(
890
917
  node.prefix ? '$.update_pre_property' : '$.update_property',
@@ -1258,7 +1285,7 @@ function transform_ts_child(node, context) {
1258
1285
  .filter((attr) => {
1259
1286
  if (attr.type === 'UseAttribute') {
1260
1287
  use_attributes.push(attr);
1261
- return false; // Filter out from JSX attributes
1288
+ return false;
1262
1289
  }
1263
1290
  return true;
1264
1291
  })
@@ -1415,7 +1442,12 @@ function transform_ts_child(node, context) {
1415
1442
  ),
1416
1443
  ),
1417
1444
  );
1445
+ } else if (node.type === 'Component') {
1446
+ const component = visit(node, context.state);
1447
+
1448
+ state.init.push(component);
1418
1449
  } else {
1450
+ debugger;
1419
1451
  throw new Error('TODO');
1420
1452
  }
1421
1453
  }
@@ -327,15 +327,16 @@ export function build_hoisted_params(node, context) {
327
327
  return params;
328
328
  }
329
329
 
330
- export function is_inside_component(context) {
330
+ export function is_inside_component(context, includes_functions = false) {
331
331
  for (let i = context.path.length - 1; i >= 0; i -= 1) {
332
332
  const context_node = context.path[i];
333
333
  const type = context_node.type;
334
334
 
335
335
  if (
336
- type === 'FunctionExpression' ||
337
- type === 'ArrowFunctionExpression' ||
338
- type === 'FunctionDeclaration'
336
+ !includes_functions &&
337
+ (type === 'FunctionExpression' ||
338
+ type === 'ArrowFunctionExpression' ||
339
+ type === 'FunctionDeclaration')
339
340
  ) {
340
341
  return false;
341
342
  }
@@ -369,6 +370,47 @@ export function is_tracked_name(name) {
369
370
  return typeof name === 'string' && name.startsWith('$') && name.length > 1 && name[1] !== '$';
370
371
  }
371
372
 
373
+ export function is_value_static(node) {
374
+ if (node.type === 'Literal') {
375
+ return true;
376
+ }
377
+ if (node.type === 'ArrayExpression') {
378
+ return true;
379
+ }
380
+ if (node.type === 'NewExpression') {
381
+ if (node.callee.type === 'Identifier' && node.callee.name === 'Array') {
382
+ return true;
383
+ }
384
+ return false;
385
+ }
386
+
387
+ return false;
388
+ }
389
+
390
+ export function is_tracked_computed_property(object, property, context) {
391
+ const binding = context.state.scope.get(object.name);
392
+
393
+ if (binding) {
394
+ const initial = binding.initial;
395
+ if (initial && is_value_static(initial)) {
396
+ return false;
397
+ }
398
+ }
399
+ if (property.type === 'Identifier') {
400
+ return true;
401
+ }
402
+ if (
403
+ property.type === 'Literal' &&
404
+ typeof property.value === 'string' &&
405
+ is_tracked_name(property.value)
406
+ ) {
407
+ return true;
408
+ }
409
+
410
+ // TODO: do we need to handle more logic here? default to false for now
411
+ return true;
412
+ }
413
+
372
414
  export function is_ripple_import(callee, context) {
373
415
  if (callee.type === 'Identifier') {
374
416
  const binding = context.state.scope.get(callee.name);
@@ -506,7 +548,7 @@ export function build_assignment(operator, left, right, context) {
506
548
  return transform.assign(
507
549
  object,
508
550
  value,
509
- left.type === 'MemberExpression' && left.computed ? left.property : undefined,
551
+ left.type === 'MemberExpression' && left.computed ? context.visit(left.property) : undefined,
510
552
  );
511
553
  }
512
554
 
@@ -1,7 +1,7 @@
1
1
  /** @import { Block } from '#client' */
2
2
 
3
3
  import { TRACKED_OBJECT } from './internal/client/constants.js';
4
- import { get, increment, scope, set, tracked } from './internal/client/runtime.js';
4
+ import { get, increment, safe_scope, set, tracked } from './internal/client/runtime.js';
5
5
 
6
6
  var symbol_iterator = Symbol.iterator;
7
7
 
@@ -36,17 +36,6 @@ const introspect_methods = [
36
36
  ];
37
37
 
38
38
  let init = false;
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
-
50
39
  export class RippleArray extends Array {
51
40
  #tracked_elements = [];
52
41
  #tracked_index;
@@ -62,12 +51,11 @@ export class RippleArray extends Array {
62
51
  constructor(...elements) {
63
52
  super(...elements);
64
53
 
65
- var block = scope();
66
- check_block(block);
54
+ var block = safe_scope();
67
55
  var tracked_elements = this.#tracked_elements;
68
56
 
69
57
  for (var i = 0; i < this.length; i++) {
70
- tracked_elements[i] = tracked(0, block);
58
+ tracked_elements[i] = tracked(elements[i], block);
71
59
  }
72
60
  this.#tracked_index = tracked(this.length, block);
73
61
 
@@ -90,20 +78,18 @@ export class RippleArray extends Array {
90
78
  }
91
79
  }
92
80
 
93
- fill() {
94
- var block = scope();
95
- check_block(block);
81
+ fill(val, start, end) {
82
+ var block = safe_scope();
96
83
  var tracked_elements = this.#tracked_elements;
97
84
 
98
- super.fill();
85
+ super.fill(val, start, end);
99
86
  for (var i = 0; i < this.length; i++) {
100
87
  increment(tracked_elements[i], block);
101
88
  }
102
89
  }
103
90
 
104
91
  reverse() {
105
- var block = scope();
106
- check_block(block);
92
+ var block = safe_scope();
107
93
  var tracked_elements = this.#tracked_elements;
108
94
 
109
95
  super.reverse();
@@ -113,8 +99,7 @@ export class RippleArray extends Array {
113
99
  }
114
100
 
115
101
  sort(fn) {
116
- var block = scope();
117
- check_block(block);
102
+ var block = safe_scope();
118
103
  var tracked_elements = this.#tracked_elements;
119
104
 
120
105
  super.sort(fn);
@@ -128,8 +113,7 @@ export class RippleArray extends Array {
128
113
  * @returns {number}
129
114
  */
130
115
  unshift(...elements) {
131
- var block = scope();
132
- check_block(block);
116
+ var block = safe_scope();
133
117
  var tracked_elements = this.#tracked_elements;
134
118
 
135
119
  super.unshift(...elements);
@@ -144,15 +128,14 @@ export class RippleArray extends Array {
144
128
  }
145
129
 
146
130
  shift() {
147
- var block = scope();
148
- check_block(block);
131
+ var block = safe_scope();
149
132
  var tracked_elements = this.#tracked_elements;
150
133
 
151
134
  super.shift();
152
- for (var i = 0; i < tracked_elements.length; i++) {
153
- increment(tracked_elements[i], block);
135
+ for (var i = 0; i < tracked_elements.length - 1; i++) {
136
+ set(tracked_elements[i], tracked_elements[i + 1].v, block);
154
137
  }
155
- tracked_elements.shift();
138
+ tracked_elements.pop();
156
139
 
157
140
  set(this.#tracked_index, this.length, block);
158
141
  }
@@ -162,8 +145,7 @@ export class RippleArray extends Array {
162
145
  * @returns {number}
163
146
  */
164
147
  push(...elements) {
165
- var block = scope();
166
- check_block(block);
148
+ var block = safe_scope();
167
149
  var start_index = this.length;
168
150
  var tracked_elements = this.#tracked_elements;
169
151
 
@@ -178,8 +160,7 @@ export class RippleArray extends Array {
178
160
  }
179
161
 
180
162
  pop() {
181
- var block = scope();
182
- check_block(block);
163
+ var block = safe_scope();
183
164
  var tracked_elements = this.#tracked_elements;
184
165
 
185
166
  super.pop();
@@ -198,8 +179,7 @@ export class RippleArray extends Array {
198
179
  * @returns {any[]}
199
180
  */
200
181
  splice(start, delete_count, ...elements) {
201
- var block = scope();
202
- check_block(block);
182
+ var block = safe_scope();
203
183
  var tracked_elements = this.#tracked_elements;
204
184
 
205
185
  super.splice(start, delete_count, ...elements);
@@ -220,11 +200,10 @@ export class RippleArray extends Array {
220
200
  }
221
201
 
222
202
  set $length(length) {
223
- var block = scope();
224
- check_block(block);
203
+ var block = safe_scope();
225
204
  var tracked_elements = this.#tracked_elements;
226
205
 
227
- if (length !== this.$length) {
206
+ if (length !== this.length) {
228
207
  for (var i = 0; i < tracked_elements.length; i++) {
229
208
  increment(tracked_elements[i], block);
230
209
  }
@@ -260,4 +239,3 @@ export function get_all_elements(array) {
260
239
 
261
240
  return arr;
262
241
  }
263
-
@@ -42,6 +42,8 @@ export { RippleArray } from './array.js';
42
42
 
43
43
  export { RippleSet } from './set.js';
44
44
 
45
+ export { RippleMap } from './map.js';
46
+
45
47
  export { keyed } from './internal/client/for.js';
46
48
 
47
49
  export { user_effect as effect } from './internal/client/blocks.js';
@@ -16,6 +16,7 @@ export { event, delegate } from './events.js';
16
16
  export {
17
17
  active_block,
18
18
  scope,
19
+ safe_scope,
19
20
  with_scope,
20
21
  get_tracked,
21
22
  get_computed,