ripple 0.2.140 → 0.2.141

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 an elegant TypeScript UI framework",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.2.140",
6
+ "version": "0.2.141",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index-client.js",
9
9
  "main": "src/runtime/index-client.js",
@@ -81,6 +81,6 @@
81
81
  "typescript": "^5.9.2"
82
82
  },
83
83
  "peerDependencies": {
84
- "ripple": "0.2.140"
84
+ "ripple": "0.2.141"
85
85
  }
86
86
  }
@@ -169,14 +169,18 @@ function RipplePlugin(config) {
169
169
  }
170
170
 
171
171
  if (inComponent) {
172
- // Inside nested functions (scopeStack.length >= 5), treat < as relational/generic operator
173
- // At component top-level (scopeStack.length <= 4), apply JSX detection logic
172
+ // Check if we're inside a nested function (arrow function, function expression, etc.)
173
+ // We need to distinguish between being inside a function vs just being in nested scopes
174
+ // (like for loops, if blocks, JSX elements, etc.)
175
+ const nestedFunctionContext = this.context.some((ctx) => ctx.token === 'function');
176
+
177
+ // Inside nested functions, treat < as relational/generic operator
174
178
  // BUT: if the < is followed by /, it's a closing JSX tag, not a less-than operator
175
179
  const nextChar =
176
180
  this.pos + 1 < this.input.length ? this.input.charCodeAt(this.pos + 1) : -1;
177
181
  const isClosingTag = nextChar === 47; // '/'
178
182
 
179
- if (this.scopeStack.length >= 5 && !isClosingTag) {
183
+ if (nestedFunctionContext && !isClosingTag) {
180
184
  // Inside function - treat as TypeScript generic, not JSX
181
185
  ++this.pos;
182
186
  return this.finishToken(tt.relational, '<');
@@ -109,7 +109,7 @@ const visitors = {
109
109
  const parent = context.path.at(-1);
110
110
 
111
111
  if (
112
- is_reference(node, /** @type {Node} */(parent)) &&
112
+ is_reference(node, /** @type {Node} */ (parent)) &&
113
113
  binding &&
114
114
  context.state.inside_server_block &&
115
115
  context.state.scope.server_block
@@ -144,7 +144,7 @@ const visitors = {
144
144
  }
145
145
 
146
146
  if (
147
- is_reference(node, /** @type {Node} */(parent)) &&
147
+ is_reference(node, /** @type {Node} */ (parent)) &&
148
148
  node.tracked &&
149
149
  binding?.node !== node
150
150
  ) {
@@ -155,7 +155,7 @@ const visitors = {
155
155
  }
156
156
 
157
157
  if (
158
- is_reference(node, /** @type {Node} */(parent)) &&
158
+ is_reference(node, /** @type {Node} */ (parent)) &&
159
159
  node.tracked &&
160
160
  binding?.node !== node
161
161
  ) {
@@ -191,7 +191,7 @@ const visitors = {
191
191
  error(
192
192
  `Directly accessing internal property "${propertyName}" of a tracked object is not allowed. Use \`get(${node.object.name})\` or \`@${node.object.name}\` instead.`,
193
193
  context.state.analysis.module.filename,
194
- node.property
194
+ node.property,
195
195
  );
196
196
  }
197
197
  }
@@ -205,7 +205,7 @@ const visitors = {
205
205
  `Accessing a tracked object directly is not allowed, use the \`@\` prefix to read the value inside a tracked object - for example \`@${node.object.name}${node.property.type === 'Identifier' ? `.${node.property.name}` : ''}\``,
206
206
  context.state.analysis.module.filename,
207
207
  node,
208
- )
208
+ );
209
209
  }
210
210
  }
211
211
 
@@ -259,8 +259,11 @@ const visitors = {
259
259
  const callee = declarator.init.callee;
260
260
  // Check if it's a call to `track` or `tracked`
261
261
  if (
262
- (callee.type === 'Identifier' && (callee.name === 'track' || callee.name === 'tracked')) ||
263
- (callee.type === 'MemberExpression' && callee.property.type === 'Identifier' && (callee.property.name === 'track' || callee.property.name === 'tracked'))
262
+ (callee.type === 'Identifier' &&
263
+ (callee.name === 'track' || callee.name === 'tracked')) ||
264
+ (callee.type === 'MemberExpression' &&
265
+ callee.property.type === 'Identifier' &&
266
+ (callee.property.name === 'track' || callee.property.name === 'tracked'))
264
267
  ) {
265
268
  binding.metadata = { ...binding.metadata, is_tracked_object: true };
266
269
  }
@@ -623,7 +626,6 @@ const visitors = {
623
626
  context.state.analysis.module.filename,
624
627
  node,
625
628
  );
626
-
627
629
  },
628
630
 
629
631
  Element(node, context) {
@@ -829,7 +831,7 @@ const visitors = {
829
831
  error(
830
832
  '`await` is not allowed in client-side control-flow statements',
831
833
  context.state.analysis.module.filename,
832
- node
834
+ node,
833
835
  );
834
836
  }
835
837
  }
@@ -198,7 +198,7 @@ const visitors = {
198
198
  ...node.metadata,
199
199
  original_name: node.name,
200
200
  is_capitalized: true,
201
- }
201
+ },
202
202
  };
203
203
  return b.member(capitalized_node, b.literal('#v'), true);
204
204
  }
@@ -552,7 +552,7 @@ const visitors = {
552
552
  ...pattern.metadata,
553
553
  original_name: pattern.name,
554
554
  is_capitalized: true,
555
- }
555
+ },
556
556
  };
557
557
  }
558
558
  return pattern;
@@ -560,7 +560,7 @@ const visitors = {
560
560
  return {
561
561
  ...pattern,
562
562
  elements: pattern.elements.map((element) =>
563
- element ? capitalize_pattern(element) : element
563
+ element ? capitalize_pattern(element) : element,
564
564
  ),
565
565
  };
566
566
  } else if (pattern.type === 'ObjectPattern') {
@@ -1007,7 +1007,7 @@ const visitors = {
1007
1007
  let expression = visit(class_attribute.value, { ...state, metadata });
1008
1008
 
1009
1009
  const hash_arg = scoping_hash ? b.literal(scoping_hash) : undefined;
1010
- const is_html = context.state.metadata.namespace === 'html' && node.id.name !== 'svg';
1010
+ const is_html = context.state.namespace === 'html' && node.id.name !== 'svg';
1011
1011
 
1012
1012
  if (metadata.tracking) {
1013
1013
  local_updates.push(
@@ -1896,7 +1896,10 @@ function transform_ts_child(node, context) {
1896
1896
  },
1897
1897
  end: {
1898
1898
  line: node.loc.end.line,
1899
- column: closing_tag_start + 3 + (node.metadata?.original_name?.length || type_expression.length),
1899
+ column:
1900
+ closing_tag_start +
1901
+ 3 +
1902
+ (node.metadata?.original_name?.length || type_expression.length),
1900
1903
  },
1901
1904
  };
1902
1905
  // Add metadata if this was capitalized
@@ -2330,6 +2333,22 @@ function create_tsx_with_typescript_support() {
2330
2333
 
2331
2334
  return {
2332
2335
  ...base_tsx,
2336
+ // Custom handler for ArrayPattern to ensure typeAnnotation is visited
2337
+ // esrap's TypeScript handler doesn't visit typeAnnotation for ArrayPattern (only for ObjectPattern)
2338
+ ArrayPattern(node, context) {
2339
+ context.write('[');
2340
+ for (let i = 0; i < node.elements.length; i++) {
2341
+ if (i > 0) context.write(', ');
2342
+ if (node.elements[i]) {
2343
+ context.visit(node.elements[i]);
2344
+ }
2345
+ }
2346
+ context.write(']');
2347
+ // Visit type annotation if present
2348
+ if (node.typeAnnotation) {
2349
+ context.visit(node.typeAnnotation);
2350
+ }
2351
+ },
2333
2352
  // Custom handler for FunctionDeclaration to support component->function mapping
2334
2353
  // Needed for volar mappings and intellisense on function or component keyword
2335
2354
  FunctionDeclaration(node, context) {
@@ -201,7 +201,9 @@ export function get_delegated_event(event_name, handler, state) {
201
201
  if (binding != null) {
202
202
  for (const { path } of binding.references) {
203
203
  const parent = path.at(-1);
204
- if (parent === undefined) return unhoisted;
204
+ if (parent === undefined) {
205
+ return unhoisted;
206
+ }
205
207
 
206
208
  const grandparent = path.at(-2);
207
209
 
@@ -227,7 +229,11 @@ export function get_delegated_event(event_name, handler, state) {
227
229
  ) {
228
230
  return unhoisted;
229
231
  }
230
- } else if (parent.type !== 'FunctionDeclaration' && parent.type !== 'VariableDeclarator') {
232
+ } else if (
233
+ parent.type !== 'FunctionDeclaration' &&
234
+ parent.type !== 'VariableDeclarator' &&
235
+ parent.type !== 'Attribute'
236
+ ) {
231
237
  return unhoisted;
232
238
  }
233
239
  }
@@ -262,24 +268,29 @@ export function get_delegated_event(event_name, handler, state) {
262
268
 
263
269
  const visited_references = new Set();
264
270
  const scope = target_function.metadata.scope;
265
- for (const [reference] of scope.references) {
271
+ for (const [reference, ref_nodes] of scope.references) {
266
272
  // Bail out if the arguments keyword is used or $host is referenced
267
273
  if (reference === 'arguments') return unhoisted;
268
274
 
269
275
  const binding = scope.get(reference);
270
276
  const local_binding = state.scope.get(reference);
271
277
 
278
+ if (local_binding === null || binding == null) {
279
+ return unhoisted;
280
+ }
281
+
272
282
  // If we are referencing a binding that is shadowed in another scope then bail out.
273
- if (local_binding !== null && binding !== null && local_binding.node !== binding.node) {
283
+ if (local_binding.node !== binding.node) {
274
284
  return unhoisted;
275
285
  }
286
+ const is_tracked = ref_nodes.some(({ node }) => node.tracked);
276
287
 
277
288
  if (
278
289
  binding !== null &&
279
290
  // Bail out if the the binding is a rest param
280
291
  (binding.declaration_kind === 'rest_param' || // or any normal not reactive bindings that are mutated.
281
292
  // Bail out if we reference anything from the EachBlock (for now) that mutates in non-runes mode,
282
- (binding.kind === 'normal' && binding.updated))
293
+ (binding.kind === 'normal' && !is_tracked && binding.updated))
283
294
  ) {
284
295
  return unhoisted;
285
296
  }
@@ -352,7 +363,7 @@ export function build_hoisted_params(node, context) {
352
363
  }
353
364
  } else {
354
365
  for (const param of node.params) {
355
- params.push(/** @type {Pattern} */(context.visit(param)));
366
+ params.push(/** @type {Pattern} */ (context.visit(param)));
356
367
  }
357
368
  }
358
369
 
@@ -554,7 +565,7 @@ export function is_ripple_import(callee, context) {
554
565
  * @returns {boolean}
555
566
  */
556
567
  export function is_declared_function_within_component(node, context) {
557
- const component = context.path?.find(/** @param {RippleNode} n */(n) => n.type === 'Component');
568
+ const component = context.path?.find(/** @param {RippleNode} n */ (n) => n.type === 'Component');
558
569
 
559
570
  if (node.type === 'Identifier' && component) {
560
571
  const binding = context.state.scope.get(node.name);
@@ -611,8 +622,8 @@ export function visit_assignment_expression(node, context, build_assignment) {
611
622
  assignment ??
612
623
  b.assignment(
613
624
  '=',
614
- /** @type {Pattern} */(context.visit(path.node)),
615
- /** @type {Expression} */(context.visit(value)),
625
+ /** @type {Pattern} */ (context.visit(path.node)),
626
+ /** @type {Expression} */ (context.visit(value)),
616
627
  )
617
628
  );
618
629
  });
@@ -702,8 +713,8 @@ export function build_assignment(operator, left, right, context) {
702
713
  object,
703
714
  b.assignment(
704
715
  operator,
705
- /** @type {Pattern} */(context.visit(left)),
706
- /** @type {Expression} */(context.visit(right)),
716
+ /** @type {Pattern} */ (context.visit(left)),
717
+ /** @type {Expression} */ (context.visit(right)),
707
718
  ),
708
719
  );
709
720
  }
@@ -64,7 +64,7 @@ export class TrackedDate extends Date {
64
64
  proto[method] = function (...args) {
65
65
  // @ts-ignore
66
66
  var result = date_proto[method].apply(this, args);
67
- set(this.#time, date_proto.getTime.call(this), this.#block);
67
+ set(this.#time, date_proto.getTime.call(this));
68
68
  return result;
69
69
  };
70
70
  }
@@ -14,7 +14,7 @@ export {
14
14
  set_selected,
15
15
  } from './render.js';
16
16
 
17
- export { render, render_spread, async, ref } from './blocks.js';
17
+ export { render, render_spread, async, ref, branch } from './blocks.js';
18
18
 
19
19
  export { event, delegate } from './events.js';
20
20
 
@@ -47,6 +47,8 @@ export {
47
47
  derived,
48
48
  maybe_tracked,
49
49
  tick,
50
+ proxy_tracked,
51
+ with_block,
50
52
  } from './runtime.js';
51
53
 
52
54
  export { composite } from './composite.js';
@@ -163,29 +163,29 @@ export function set_attributes(element, attributes) {
163
163
  * @param {Element} element
164
164
  * @param {string} key
165
165
  * @param {any} value
166
- */
166
+ */
167
167
  function set_attribute_helper(element, key, value) {
168
- if (key === 'class') {
169
- const is_html = element.namespaceURI === 'http://www.w3.org/1999/xhtml';
170
- set_class(/** @type {HTMLElement} */ (element), value, undefined, is_html);
171
- } else if (key === '#class') {
172
- // Special case for static class when spreading props
173
- element.classList.add(value);
174
- } else if (typeof key === 'string' && is_event_attribute(key)) {
175
- // Handle event handlers in spread props
176
- const event_name = get_attribute_event_name(key);
177
-
178
- if (is_delegated(event_name)) {
179
- // Use delegation for delegated events
180
- /** @type {any} */ (element)['__' + event_name] = value;
181
- delegate([event_name]);
182
- } else {
183
- // Use addEventListener for non-delegated events
184
- event(event_name, element, value);
185
- }
186
- } else {
187
- set_attribute(element, key, value);
188
- }
168
+ if (key === 'class') {
169
+ const is_html = element.namespaceURI === 'http://www.w3.org/1999/xhtml';
170
+ set_class(/** @type {HTMLElement} */ (element), value, undefined, is_html);
171
+ } else if (key === '#class') {
172
+ // Special case for static class when spreading props
173
+ element.classList.add(value);
174
+ } else if (typeof key === 'string' && is_event_attribute(key)) {
175
+ // Handle event handlers in spread props
176
+ const event_name = get_attribute_event_name(key);
177
+
178
+ if (is_delegated(event_name)) {
179
+ // Use delegation for delegated events
180
+ /** @type {any} */ (element)['__' + event_name] = value;
181
+ delegate([event_name]);
182
+ } else {
183
+ // Use addEventListener for non-delegated events
184
+ event(event_name, element, value);
185
+ }
186
+ } else {
187
+ set_attribute(element, key, value);
188
+ }
189
189
  }
190
190
 
191
191
  /**
@@ -312,7 +312,7 @@ export function apply_element_spread(element, fn) {
312
312
  for (const symbol of get_own_property_symbols(next)) {
313
313
  // Ensure we are not trying to write to a proxied object
314
314
  if (TRACKED_OBJECT in next) {
315
- next = {...next};
315
+ next = { ...next };
316
316
  }
317
317
  var ref_fn = next[symbol];
318
318
 
@@ -30,7 +30,7 @@ import {
30
30
  import { capture, suspend } from './try.js';
31
31
  import {
32
32
  define_property,
33
- get_descriptors,
33
+ get_descriptor,
34
34
  get_own_property_symbols,
35
35
  is_array,
36
36
  is_tracked_object,
@@ -135,6 +135,20 @@ export function run_teardown(block) {
135
135
  }
136
136
  }
137
137
 
138
+ /**
139
+ * @param {Block} block
140
+ * @param {() => void} fn
141
+ */
142
+ export function with_block(block, fn) {
143
+ var prev_block = active_block;
144
+ active_block = block;
145
+ try {
146
+ return fn();
147
+ } finally {
148
+ active_block = prev_block;
149
+ }
150
+ }
151
+
138
152
  /**
139
153
  * @param {Derived} computed
140
154
  */
@@ -372,35 +386,34 @@ export function track_split(v, l, b) {
372
386
  var out = [];
373
387
  /** @type {Record<string|symbol, any>} */
374
388
  var rest = {};
375
- /** @type {Record<PropertyKey, any | null>} */
376
- var descriptors = get_descriptors(v);
389
+ /** @type {Record<PropertyKey, 1>} */
390
+ var done = {};
391
+ var props = Reflect.ownKeys(v);
377
392
 
378
- for (let i = 0, key, t, exists = true; i < l.length; i++) {
393
+ for (let i = 0, key, t; i < l.length; i++) {
379
394
  key = l[i];
380
395
 
381
- if (is_tracked_object(v[key])) {
382
- t = v[key];
396
+ if (props.includes(key)) {
397
+ if (is_tracked_object(v[key])) {
398
+ t = v[key];
399
+ } else {
400
+ t = tracked(undefined, b);
401
+ t = define_property(t, '__v', /** @type {PropertyDescriptor} */ (get_descriptor(v, key)));
402
+ }
383
403
  } else {
384
404
  t = tracked(undefined, b);
385
- exists = !!descriptors[key];
386
- if (exists) {
387
- t = define_property(t, '__v', descriptors[key]);
388
- }
389
405
  }
390
406
 
391
407
  out[i] = t;
392
- if (exists) {
393
- descriptors[key] = null;
394
- }
408
+ done[key] = 1;
395
409
  }
396
410
 
397
- var props = Reflect.ownKeys(descriptors);
398
411
  for (let i = 0, key; i < props.length; i++) {
399
412
  key = props[i];
400
- if (descriptors[key] === null) {
413
+ if (done[key]) {
401
414
  continue;
402
415
  }
403
- define_property(rest, key, descriptors[key]);
416
+ define_property(rest, key, /** @type {PropertyDescriptor} */ (get_descriptor(v, key)));
404
417
  }
405
418
 
406
419
  out.push(tracked(rest, b));
@@ -903,34 +916,37 @@ export function flush_sync(fn) {
903
916
  */
904
917
  export function spread_props(fn, block) {
905
918
  var computed = derived(fn, block);
919
+ return proxy_tracked(computed);
920
+ }
906
921
 
922
+ /**
923
+ * @param {Tracked | Derived} tracked
924
+ * @returns {Object}
925
+ */
926
+ export function proxy_tracked(tracked) {
907
927
  return new Proxy(
908
928
  {},
909
929
  {
910
- get(target, property) {
911
- const obj = get_derived(computed);
930
+ get(_, property) {
931
+ const obj = get(tracked);
912
932
  return obj[property];
913
933
  },
914
- has(target, property) {
934
+ has(_, property) {
915
935
  if (property === TRACKED_OBJECT) {
916
936
  return true;
917
937
  }
918
- const obj = get_derived(computed);
938
+ const obj = get(tracked);
919
939
  return property in obj;
920
940
  },
921
- getOwnPropertyDescriptor(target, key) {
922
- const obj = get_derived(computed);
941
+ getOwnPropertyDescriptor(_, key) {
942
+ const obj = get(tracked);
923
943
 
924
944
  if (key in obj) {
925
- return {
926
- enumerable: true,
927
- configurable: true,
928
- value: obj[key],
929
- };
945
+ return get_descriptor(obj, key);
930
946
  }
931
947
  },
932
948
  ownKeys() {
933
- const obj = get_derived(computed);
949
+ const obj = get(tracked);
934
950
  return Reflect.ownKeys(obj);
935
951
  },
936
952
  },