ripple 0.2.124 → 0.2.126

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.124",
6
+ "version": "0.2.126",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index-client.js",
9
9
  "main": "src/runtime/index-client.js",
@@ -24,7 +24,7 @@ export function parse(source) {
24
24
  */
25
25
  export function compile(source, filename, options = {}) {
26
26
  const ast = parse_module(source);
27
- const analysis = analyze(ast, filename);
27
+ const analysis = analyze(ast, filename, options);
28
28
  const result = options.mode === 'server'
29
29
  ? transform_server(filename, source, analysis)
30
30
  : transform_client(filename, source, analysis, false);
@@ -1392,6 +1392,7 @@ function RipplePlugin(config) {
1392
1392
  this.next();
1393
1393
  this.enterScope(0);
1394
1394
  node.id = this.parseIdent();
1395
+ this.declareName(node.id.name, 'var', node.id.start);
1395
1396
  this.parseFunctionParams(node);
1396
1397
  this.eat(tt.braceL);
1397
1398
  node.body = [];
@@ -111,7 +111,8 @@ const visitors = {
111
111
  if (
112
112
  is_reference(node, /** @type {Node} */ (parent)) &&
113
113
  binding &&
114
- context.state.inside_server_block
114
+ context.state.inside_server_block &&
115
+ context.state.scope.server_block
115
116
  ) {
116
117
  let current_scope = binding.scope;
117
118
 
@@ -349,13 +350,14 @@ const visitors = {
349
350
  node.metadata = {
350
351
  ...node.metadata,
351
352
  has_template: false,
353
+ has_await: false,
352
354
  };
353
355
 
354
356
  context.visit(switch_case, context.state);
355
357
 
356
- if (!node.metadata.has_template) {
358
+ if (!node.metadata.has_template && !node.metadata.has_await) {
357
359
  error(
358
- 'Component switch statements must contain a template in each of their cases. Move the switch statement into an effect if it does not render anything.',
360
+ 'Component switch statements must contain a template or an await expression in each of their cases. Move the switch statement into an effect if it does not render anything.',
359
361
  context.state.analysis.module.filename,
360
362
  node,
361
363
  );
@@ -416,15 +418,16 @@ const visitors = {
416
418
  node.metadata = {
417
419
  ...node.metadata,
418
420
  has_template: false,
421
+ has_await: false,
419
422
  };
420
423
  context.next();
421
424
 
422
- if (!node.metadata.has_template) {
425
+ if (!node.metadata.has_template && !node.metadata.has_await) {
423
426
  error(
424
- 'Component for...of loops must contain a template in their body. Move the for loop into an effect if it does not render anything.',
425
- context.state.analysis.module.filename,
426
- node,
427
- );
427
+ 'Component for...of loops must contain a template or an await expression in their body. Move the for loop into an effect if it does not render anything.',
428
+ context.state.analysis.module.filename,
429
+ node,
430
+ );
428
431
  }
429
432
  },
430
433
 
@@ -437,9 +440,14 @@ const visitors = {
437
440
 
438
441
  if (declaration && declaration.type === 'FunctionDeclaration') {
439
442
  server_block.metadata.exports.push(declaration.id.name);
443
+ } else if (declaration && declaration.type === 'Component') {
444
+ // Handle exported components in server blocks
445
+ if (server_block) {
446
+ server_block.metadata.exports.push(declaration.id.name);
447
+ }
440
448
  } else {
441
449
  // TODO
442
- throw new Error('Not implemented');
450
+ throw new Error('Not implemented: Exported declaration type not supported in server blocks.');
443
451
  }
444
452
 
445
453
  return context.next();
@@ -462,35 +470,39 @@ const visitors = {
462
470
  node.metadata = {
463
471
  ...node.metadata,
464
472
  has_template: false,
473
+ has_await: false,
465
474
  };
466
475
 
467
476
  context.visit(node.consequent, context.state);
468
477
 
469
- if (!node.metadata.has_template) {
478
+ if (!node.metadata.has_template && !node.metadata.has_await) {
470
479
  error(
471
- 'Component if statements must contain a template in their "then" body. Move the if statement into an effect if it does not render anything.',
472
- context.state.analysis.module.filename,
473
- node,
474
- );
480
+ 'Component if statements must contain a template or an await expression in their "then" body. Move the if statement into an effect if it does not render anything.',
481
+ context.state.analysis.module.filename,
482
+ node,
483
+ );
475
484
  }
476
485
 
477
486
  if (node.alternate) {
478
- node.metadata = {
479
- ...node.metadata,
480
- has_template: false,
481
- };
487
+ node.metadata.has_template = false;
488
+ node.metadata.has_await = false;
482
489
  context.visit(node.alternate, context.state);
483
490
 
484
- if (!node.metadata.has_template) {
491
+ if (!node.metadata.has_template && !node.metadata.has_await) {
485
492
  error(
486
- 'Component if statements must contain a template in their "else" body. Move the if statement into an effect if it does not render anything.',
487
- context.state.analysis.module.filename,
488
- node,
489
- );
493
+ 'Component if statements must contain a template or an await expression in their "else" body. Move the if statement into an effect if it does not render anything.',
494
+ context.state.analysis.module.filename,
495
+ node,
496
+ );
490
497
  }
491
498
  }
492
499
  },
493
-
500
+ /**
501
+ *
502
+ * @param {any} node
503
+ * @param {any} context
504
+ * @returns
505
+ */
494
506
  TryStatement(node, context) {
495
507
  if (!is_inside_component(context)) {
496
508
  return context.next();
@@ -744,7 +756,12 @@ const visitors = {
744
756
  mark_control_flow_has_template(context.path);
745
757
  context.next();
746
758
  },
747
-
759
+
760
+ /**
761
+ *
762
+ * @param {any} node
763
+ * @param {any} context
764
+ */
748
765
  AwaitExpression(node, context) {
749
766
  if (is_inside_component(context)) {
750
767
  if (context.state.metadata?.await === false) {
@@ -754,18 +771,27 @@ const visitors = {
754
771
  const parent_block = get_parent_block_node(context);
755
772
 
756
773
  if (parent_block !== null && parent_block.type !== 'Component') {
757
- error(
758
- '`await` expressions can only currently be used at the top-level of a component body. Support for using them in control flow statements will be added in the future.',
759
- context.state.analysis.module.filename,
760
- node,
761
- );
774
+ if (context.state.inside_server_block === false) {
775
+ error(
776
+ '`await` is not allowed in client-side control-flow statements',
777
+ context.state.analysis.module.filename,
778
+ node
779
+ );
780
+ }
762
781
  }
763
782
 
783
+ if (parent_block) {
784
+ if (!parent_block.metadata) {
785
+ parent_block.metadata = {};
786
+ }
787
+ parent_block.metadata.has_await = true;
788
+ }
789
+
764
790
  context.next();
765
791
  },
766
792
  };
767
793
 
768
- export function analyze(ast, filename) {
794
+ export function analyze(ast, filename, options = {}) {
769
795
  const scope_root = new ScopeRoot();
770
796
 
771
797
  const { scope, scopes } = create_scopes(ast, scope_root, null);
@@ -785,7 +811,7 @@ export function analyze(ast, filename) {
785
811
  scopes,
786
812
  analysis,
787
813
  inside_head: false,
788
- inside_server_block: false,
814
+ inside_server_block: options.mode === 'server',
789
815
  },
790
816
  visitors,
791
817
  );
@@ -48,6 +48,12 @@ function add_ripple_internal_import(context) {
48
48
  }
49
49
 
50
50
  function visit_function(node, context) {
51
+ // Function overload signatures don't have a body - they're TypeScript-only
52
+ // Remove them when compiling to JavaScript
53
+ if (!context.state.to_ts && !node.body) {
54
+ return b.empty;
55
+ }
56
+
51
57
  if (context.state.to_ts) {
52
58
  return context.next(context.state);
53
59
  }
@@ -545,10 +551,9 @@ const visitors = {
545
551
 
546
552
  const handle_static_attr = (name, value) => {
547
553
  const attr_value = b.literal(
548
- ` ${name}${
549
- is_boolean_attribute(name) && value === true
550
- ? ''
551
- : `="${value === true ? '' : escape_html(value, true)}"`
554
+ ` ${name}${is_boolean_attribute(name) && value === true
555
+ ? ''
556
+ : `="${value === true ? '' : escape_html(value, true)}"`
552
557
  }`,
553
558
  );
554
559
 
@@ -880,16 +885,16 @@ const visitors = {
880
885
  }
881
886
  }
882
887
 
883
- if (node.metadata.scoped && state.component.css) {
884
- const hasClassAttr = node.attributes.some(attr =>
885
- attr.type === 'Attribute' && attr.name.type === 'Identifier' && attr.name.name === 'class'
886
- );
887
- if (!hasClassAttr) {
888
- const name = is_spreading ? '#class' : 'class';
889
- const value = state.component.css.hash;
890
- props.push(b.prop('init', b.key(name), b.literal(value)));
888
+ if (node.metadata.scoped && state.component.css) {
889
+ const hasClassAttr = node.attributes.some(attr =>
890
+ attr.type === 'Attribute' && attr.name.type === 'Identifier' && attr.name.name === 'class'
891
+ );
892
+ if (!hasClassAttr) {
893
+ const name = is_spreading ? '#class' : 'class';
894
+ const value = state.component.css.hash;
895
+ props.push(b.prop('init', b.key(name), b.literal(value)));
896
+ }
891
897
  }
892
- }
893
898
 
894
899
  const children_filtered = [];
895
900
 
@@ -1039,10 +1044,10 @@ const visitors = {
1039
1044
  operator === '='
1040
1045
  ? context.visit(right)
1041
1046
  : b.binary(
1042
- operator === '+=' ? '+' : operator === '-=' ? '-' : operator === '*=' ? '*' : '/',
1043
- /** @type {Expression} */ (context.visit(left)),
1044
- /** @type {Expression} */ (context.visit(right)),
1045
- ),
1047
+ operator === '+=' ? '+' : operator === '-=' ? '-' : operator === '*=' ? '*' : '/',
1048
+ /** @type {Expression} */(context.visit(left)),
1049
+ /** @type {Expression} */(context.visit(right)),
1050
+ ),
1046
1051
  b.id('__block'),
1047
1052
  );
1048
1053
  }
@@ -1058,12 +1063,12 @@ const visitors = {
1058
1063
  operator === '='
1059
1064
  ? context.visit(right)
1060
1065
  : b.binary(
1061
- operator === '+=' ? '+' : operator === '-=' ? '-' : operator === '*=' ? '*' : '/',
1062
- /** @type {Expression} */ (
1063
- context.visit(left, { ...context.state, metadata: { tracking: false } })
1064
- ),
1065
- /** @type {Expression} */ (context.visit(right)),
1066
+ operator === '+=' ? '+' : operator === '-=' ? '-' : operator === '*=' ? '*' : '/',
1067
+ /** @type {Expression} */(
1068
+ context.visit(left, { ...context.state, metadata: { tracking: false } })
1066
1069
  ),
1070
+ /** @type {Expression} */(context.visit(right)),
1071
+ ),
1067
1072
  b.id('__block'),
1068
1073
  );
1069
1074
  }
@@ -1270,12 +1275,12 @@ const visitors = {
1270
1275
  b.stmt(b.call(b.id('__render'), b.id(consequent_id))),
1271
1276
  alternate_id
1272
1277
  ? b.stmt(
1273
- b.call(
1274
- b.id('__render'),
1275
- b.id(alternate_id),
1276
- node.alternate ? b.literal(false) : undefined,
1277
- ),
1278
- )
1278
+ b.call(
1279
+ b.id('__render'),
1280
+ b.id(alternate_id),
1281
+ node.alternate ? b.literal(false) : undefined,
1282
+ ),
1283
+ )
1279
1284
  : undefined,
1280
1285
  ),
1281
1286
  ]),
@@ -1307,6 +1312,22 @@ const visitors = {
1307
1312
  return b.empty;
1308
1313
  }
1309
1314
 
1315
+ // Remove TSDeclareFunction nodes (function overload signatures) in JavaScript mode
1316
+ if (!context.state.to_ts && node.declaration?.type === 'TSDeclareFunction') {
1317
+ return b.empty;
1318
+ }
1319
+
1320
+ return context.next();
1321
+ },
1322
+
1323
+ TSDeclareFunction(node, context) {
1324
+ // TSDeclareFunction nodes are TypeScript overload signatures - remove in JavaScript mode
1325
+ if (!context.state.to_ts) {
1326
+ return b.empty;
1327
+ }
1328
+
1329
+ // In TypeScript mode, keep as TSDeclareFunction - esrap will print it with 'declare'
1330
+ // We'll remove the 'declare' keyword in post-processing
1310
1331
  return context.next();
1311
1332
  },
1312
1333
 
@@ -1336,9 +1357,9 @@ const visitors = {
1336
1357
  node.handler === null
1337
1358
  ? b.literal(null)
1338
1359
  : b.arrow(
1339
- [b.id('__anchor'), ...(node.handler.param ? [node.handler.param] : [])],
1340
- b.block(transform_body(node.handler.body.body, context)),
1341
- ),
1360
+ [b.id('__anchor'), ...(node.handler.param ? [node.handler.param] : [])],
1361
+ b.block(transform_body(node.handler.body.body, context)),
1362
+ ),
1342
1363
  node.pending === null
1343
1364
  ? undefined
1344
1365
  : b.arrow([b.id('__anchor')], b.block(transform_body(node.pending.body, context))),
@@ -1464,7 +1485,7 @@ function join_template(items) {
1464
1485
  }
1465
1486
 
1466
1487
  for (const quasi of template.quasis) {
1467
- quasi.value.raw = sanitize_template_string(/** @type {string} */ (quasi.value.cooked));
1488
+ quasi.value.raw = sanitize_template_string(/** @type {string} */(quasi.value.cooked));
1468
1489
  }
1469
1490
 
1470
1491
  quasi.tail = true;
@@ -1947,7 +1968,7 @@ export function transform_client(filename, source, analysis, to_ts) {
1947
1968
  };
1948
1969
 
1949
1970
  const program = /** @type {Program} */ (
1950
- walk(/** @type {Node} */ (analysis.ast), { ...state, namespace: 'html' }, visitors)
1971
+ walk(/** @type {Node} */(analysis.ast), { ...state, namespace: 'html' }, visitors)
1951
1972
  );
1952
1973
 
1953
1974
  for (const hoisted of state.hoisted) {
@@ -1971,6 +1992,15 @@ export function transform_client(filename, source, analysis, to_ts) {
1971
1992
  sourceMapSource: path.basename(filename),
1972
1993
  });
1973
1994
 
1995
+ // Post-process TypeScript output to remove 'declare' from function overload signatures
1996
+ // Function overload signatures in regular .ts files should not have 'declare' keyword
1997
+ if (to_ts) {
1998
+ // Remove 'export declare function' -> 'export function' (for overloads only, not implementations)
1999
+ // Match: export declare function name(...): type;
2000
+ // Don't match: export declare function name(...): type { (has body)
2001
+ js.code = js.code.replace(/^(export\s+)declare\s+(function\s+\w+[^{\n]*;)$/gm, '$1$2');
2002
+ }
2003
+
1974
2004
  const css = render_stylesheets(state.stylesheets);
1975
2005
 
1976
2006
  return {
@@ -1111,6 +1111,42 @@ export function convert_source_map_to_mappings(ast, source, generated_code, sour
1111
1111
  } else if (node.type === 'TSAnyKeyword' || node.type === 'TSUnknownKeyword' || node.type === 'TSNumberKeyword' || node.type === 'TSObjectKeyword' || node.type === 'TSBooleanKeyword' || node.type === 'TSBigIntKeyword' || node.type === 'TSStringKeyword' || node.type === 'TSSymbolKeyword' || node.type === 'TSVoidKeyword' || node.type === 'TSUndefinedKeyword' || node.type === 'TSNullKeyword' || node.type === 'TSNeverKeyword' || node.type === 'TSThisType' || node.type === 'TSIntrinsicKeyword') {
1112
1112
  // Primitive type keywords - leaf nodes, no children
1113
1113
  return;
1114
+ } else if (node.type === 'TSDeclareFunction') {
1115
+ // TypeScript declare function: declare function foo(): void;
1116
+ // Visit in source order: id, typeParameters, params, returnType
1117
+ if (node.id) {
1118
+ visit(node.id);
1119
+ }
1120
+ if (node.typeParameters) {
1121
+ visit(node.typeParameters);
1122
+ }
1123
+ if (node.params) {
1124
+ for (const param of node.params) {
1125
+ visit(param);
1126
+ }
1127
+ }
1128
+ if (node.returnType) {
1129
+ visit(node.returnType);
1130
+ }
1131
+ return;
1132
+ } else if (node.type === 'TSExportAssignment') {
1133
+ // TypeScript export assignment: export = foo;
1134
+ if (node.expression) {
1135
+ visit(node.expression);
1136
+ }
1137
+ return;
1138
+ } else if (node.type === 'TSNamespaceExportDeclaration') {
1139
+ // TypeScript namespace export: export as namespace foo;
1140
+ if (node.id) {
1141
+ visit(node.id);
1142
+ }
1143
+ return;
1144
+ } else if (node.type === 'TSExternalModuleReference') {
1145
+ // TypeScript external module reference: import foo = require('bar');
1146
+ if (node.expression) {
1147
+ visit(node.expression);
1148
+ }
1149
+ return;
1114
1150
  }
1115
1151
 
1116
1152
  throw new Error(`Unhandled AST node type in mapping walker: ${node.type}`);
@@ -120,7 +120,18 @@ const visitors = {
120
120
  component_fn = b.async(component_fn);
121
121
  }
122
122
 
123
- return component_fn;
123
+ const declaration = b.function_declaration(node.id, component_fn.params, component_fn.body, component_fn.async);
124
+
125
+ if (metadata.await) {
126
+ const parent = context.path.at(-1);
127
+ if (parent.type === 'Program' || parent.type === 'BlockStatement') {
128
+ const body = parent.body;
129
+ const index = body.indexOf(node);
130
+ body.splice(index + 1, 0, b.stmt(b.assignment('=', b.member(node.id, b.id('async')), b.true)));
131
+ }
132
+ }
133
+
134
+ return declaration;
124
135
  },
125
136
 
126
137
  CallExpression(node, context) {
@@ -270,11 +281,10 @@ const visitors = {
270
281
  let class_attribute = null;
271
282
 
272
283
  const handle_static_attr = (name, value) => {
273
- const attr_str = ` ${name}${
274
- is_boolean_attribute(name) && value === true
284
+ const attr_str = ` ${name}${is_boolean_attribute(name) && value === true
275
285
  ? ''
276
286
  : `="${value === true ? '' : escape_html(value, true)}"`
277
- }`;
287
+ }`;
278
288
 
279
289
  if (is_spreading) {
280
290
  // For spread attributes, store just the actual value, not the full attribute string
@@ -549,15 +559,22 @@ const visitors = {
549
559
  context.state.init.push(b.if(context.visit(node.test), consequent, alternate));
550
560
  },
551
561
 
552
- Identifier(node, context) {
553
- const parent = /** @type {Node} */ (context.path.at(-1));
562
+ AssignmentExpression(node, context) {
563
+ const left = node.left;
554
564
 
555
- if (is_reference(node, parent) && node.tracked) {
556
- add_ripple_internal_import(context);
557
- return b.call('_$_.get', build_getter(node, context));
565
+ if (left.type === 'Identifier' && left.tracked) {
566
+ return b.call(
567
+ 'set',
568
+ context.visit(left, { ...context.state, metadata: { tracking: false } }),
569
+ context.visit(node.right)
570
+ );
558
571
  }
572
+
573
+ return context.next();
559
574
  },
560
575
 
576
+
577
+
561
578
  ServerIdentifier(node, context) {
562
579
  return b.id('_$_server_$_');
563
580
  },
@@ -605,22 +622,22 @@ const visitors = {
605
622
  const try_statements =
606
623
  node.handler !== null
607
624
  ? [
608
- b.try(
609
- b.block(body),
610
- b.catch_clause(
611
- node.handler.param || b.id('error'),
612
- b.block(
613
- transform_body(node.handler.body.body, {
614
- ...context,
615
- state: {
616
- ...context.state,
617
- scope: context.state.scopes.get(node.handler.body),
618
- },
619
- }),
620
- ),
625
+ b.try(
626
+ b.block(body),
627
+ b.catch_clause(
628
+ node.handler.param || b.id('error'),
629
+ b.block(
630
+ transform_body(node.handler.body.body, {
631
+ ...context,
632
+ state: {
633
+ ...context.state,
634
+ scope: context.state.scopes.get(node.handler.body),
635
+ },
636
+ }),
621
637
  ),
622
638
  ),
623
- ]
639
+ ),
640
+ ]
624
641
  : body;
625
642
 
626
643
  context.state.init.push(
@@ -647,6 +664,8 @@ const visitors = {
647
664
  },
648
665
 
649
666
  AwaitExpression(node, context) {
667
+ context.state.scope.server_block = true
668
+ context.inside_server_block = true
650
669
  if (context.state.to_ts) {
651
670
  return context.next();
652
671
  }
@@ -672,10 +691,8 @@ const visitors = {
672
691
  const parent = context.path.at(-1);
673
692
 
674
693
  if (node.tracked || (node.property.type === 'Identifier' && node.property.tracked)) {
675
- add_ripple_internal_import(context);
676
-
677
694
  return b.call(
678
- '_$_.get',
695
+ 'get',
679
696
  b.member(
680
697
  context.visit(node.object),
681
698
  node.computed ? context.visit(node.property) : node.property,
@@ -690,13 +707,15 @@ const visitors = {
690
707
 
691
708
  Text(node, { visit, state }) {
692
709
  const metadata = { await: false };
693
- const expression = visit(node.expression, { ...state, metadata });
710
+ let expression = visit(node.expression, { ...state, metadata });
711
+
712
+ if (expression.type === 'Identifier' && expression.tracked) {
713
+ expression = b.call('get', expression);
714
+ }
694
715
 
695
716
  if (expression.type === 'Literal') {
696
717
  state.init.push(
697
- b.stmt(
698
- b.call(b.member(b.id('__output'), b.id('push')), b.literal(escape(expression.value))),
699
- ),
718
+ b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(escape(expression.value)))),
700
719
  );
701
720
  } else {
702
721
  state.init.push(
@@ -791,13 +810,7 @@ export function transform_server(filename, source, analysis) {
791
810
  }
792
811
 
793
812
  // Add async property to component functions
794
- for (const metadata of state.component_metadata) {
795
- if (metadata.async) {
796
- program.body.push(
797
- b.stmt(b.assignment('=', b.member(b.id(metadata.id), b.id('async')), b.true)),
798
- );
799
- }
800
- }
813
+
801
814
 
802
815
  for (const import_node of state.imports) {
803
816
  program.body.unshift(b.stmt(b.id(import_node)));
@@ -1,4 +1,4 @@
1
- /** @import { Identifier, Pattern, Super, FunctionExpression, FunctionDeclaration, ArrowFunctionExpression, MemberExpression, AssignmentExpression, Expression, Node, AssignmentOperator } from 'estree' */
1
+ /** @import { Identifier, Pattern, Super, FunctionExpression, FunctionDeclaration, ArrowFunctionExpression, MemberExpression, AssignmentExpression, Expression, Node, AssignmentOperator, CallExpression } from 'estree' */
2
2
  /** @import { Component, Element, Attribute, SpreadAttribute, ScopeInterface, Binding, RippleNode, CompilerState, TransformContext, DelegatedEventResult, TextNode } from '#compiler' */
3
3
  import { build_assignment_value, extract_paths } from '../utils/ast.js';
4
4
  import * as b from '../utils/builders.js';
@@ -352,7 +352,7 @@ export function build_hoisted_params(node, context) {
352
352
  }
353
353
  } else {
354
354
  for (const param of node.params) {
355
- params.push(/** @type {Pattern} */ (context.visit(param)));
355
+ params.push(/** @type {Pattern} */(context.visit(param)));
356
356
  }
357
357
  }
358
358
 
@@ -554,7 +554,7 @@ export function is_ripple_import(callee, context) {
554
554
  * @returns {boolean}
555
555
  */
556
556
  export function is_declared_function_within_component(node, context) {
557
- const component = context.path?.find(/** @param {RippleNode} n */ (n) => n.type === 'Component');
557
+ const component = context.path?.find(/** @param {RippleNode} n */(n) => n.type === 'Component');
558
558
 
559
559
  if (node.type === 'Identifier' && component) {
560
560
  const binding = context.state.scope.get(node.name);
@@ -611,8 +611,8 @@ export function visit_assignment_expression(node, context, build_assignment) {
611
611
  assignment ??
612
612
  b.assignment(
613
613
  '=',
614
- /** @type {Pattern} */ (context.visit(path.node)),
615
- /** @type {Expression} */ (context.visit(value)),
614
+ /** @type {Pattern} */(context.visit(path.node)),
615
+ /** @type {Expression} */(context.visit(value)),
616
616
  )
617
617
  );
618
618
  });
@@ -702,8 +702,8 @@ export function build_assignment(operator, left, right, context) {
702
702
  object,
703
703
  b.assignment(
704
704
  operator,
705
- /** @type {Pattern} */ (context.visit(left)),
706
- /** @type {Expression} */ (context.visit(right)),
705
+ /** @type {Pattern} */(context.visit(left)),
706
+ /** @type {Expression} */(context.visit(right)),
707
707
  ),
708
708
  );
709
709
  }
@@ -830,7 +830,7 @@ function normalize_child(node, normalized, context) {
830
830
  */
831
831
  export function get_parent_block_node(context) {
832
832
  const path = context.path;
833
-
833
+
834
834
  for (let i = path.length - 1; i >= 0; i -= 1) {
835
835
  const context_node = path[i];
836
836
  if (
@@ -1,7 +1,7 @@
1
1
  /** @import { Block } from '#client' */
2
2
 
3
3
  import { destroy_block, ref } from './blocks.js';
4
- import { REF_PROP } from './constants.js';
4
+ import { REF_PROP, TRACKED, TRACKED_OBJECT } from './constants.js';
5
5
  import {
6
6
  get_descriptors,
7
7
  get_own_property_symbols,
@@ -310,6 +310,10 @@ export function apply_element_spread(element, fn) {
310
310
  }
311
311
 
312
312
  for (const symbol of get_own_property_symbols(next)) {
313
+ // Ensure we are not trying to write to a proxied object
314
+ if (TRACKED_OBJECT in next) {
315
+ next = {...next};
316
+ }
313
317
  var ref_fn = next[symbol];
314
318
 
315
319
  if (symbol.description === REF_PROP && (!prev || ref_fn !== prev[symbol])) {