ripple 0.2.125 → 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.125",
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",
@@ -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}`);
@@ -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])) {
@@ -24,6 +24,7 @@ import {
24
24
  TRY_BLOCK,
25
25
  UNINITIALIZED,
26
26
  REF_PROP,
27
+ TRACKED_OBJECT,
27
28
  } from './constants.js';
28
29
  import { capture, suspend } from './try.js';
29
30
  import {
@@ -412,7 +413,7 @@ function is_tracking_dirty(tracking) {
412
413
  var tracked = tracking.t;
413
414
 
414
415
  if ((tracked.f & DERIVED) !== 0) {
415
- update_derived(/** @type {Derived} **/ (tracked));
416
+ update_derived(/** @type {Derived} **/(tracked));
416
417
  }
417
418
 
418
419
  if (tracked.c > tracking.c) {
@@ -472,7 +473,7 @@ export function async_computed(fn, block) {
472
473
  }
473
474
 
474
475
  promise.then((v) => {
475
- if (parent && is_destroyed(/** @type {Block} */ (parent))) {
476
+ if (parent && is_destroyed(/** @type {Block} */(parent))) {
476
477
  return;
477
478
  }
478
479
  if (promise === current && t.v !== v) {
@@ -746,7 +747,7 @@ export function get(tracked) {
746
747
  }
747
748
 
748
749
  return (tracked.f & DERIVED) !== 0
749
- ? get_derived(/** @type {Derived} */ (tracked))
750
+ ? get_derived(/** @type {Derived} */(tracked))
750
751
  : get_tracked(tracked);
751
752
  }
752
753
 
@@ -872,7 +873,7 @@ export function flush_sync(fn) {
872
873
  * @returns {Object}
873
874
  */
874
875
  export function spread_props(fn, block) {
875
- let computed = derived(fn, block);
876
+ var computed = derived(fn, block);
876
877
 
877
878
  return new Proxy(
878
879
  {},
@@ -882,9 +883,23 @@ export function spread_props(fn, block) {
882
883
  return obj[property];
883
884
  },
884
885
  has(target, property) {
886
+ if (property === TRACKED_OBJECT) {
887
+ return true;
888
+ }
885
889
  const obj = get_derived(computed);
886
890
  return property in obj;
887
891
  },
892
+ getOwnPropertyDescriptor(target, key) {
893
+ const obj = get_derived(computed);
894
+
895
+ if (key in obj) {
896
+ return {
897
+ enumerable: true,
898
+ configurable: true,
899
+ value: obj[key],
900
+ };
901
+ }
902
+ },
888
903
  ownKeys() {
889
904
  const obj = get_derived(computed);
890
905
  return Reflect.ownKeys(obj);
@@ -1164,7 +1179,7 @@ export async function maybe_tracked(v) {
1164
1179
  } else {
1165
1180
  value = await async_computed(async () => {
1166
1181
  return await get_tracked(v);
1167
- }, /** @type {Block} */ (active_block));
1182
+ }, /** @type {Block} */(active_block));
1168
1183
  }
1169
1184
  } else {
1170
1185
  value = await v;
@@ -0,0 +1,10 @@
1
+ export function test(arg: string): string
2
+ export function test(arg: number): string
3
+
4
+ export function test(arg: string | number): string {
5
+ if (typeof arg === 'string') {
6
+ return arg;
7
+ }
8
+
9
+ return arg.toString();
10
+ }
@@ -0,0 +1,40 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mount, flushSync } from 'ripple';
3
+
4
+ import { test } from './function-overload-import.ripple';
5
+
6
+ describe('function overload import tests', () => {
7
+ describe('function overloads', () => {
8
+ it('test function with string argument returns the string', () => {
9
+ const result = test('hello');
10
+ expect(result).toBe('hello');
11
+ expect(typeof result).toBe('string');
12
+ });
13
+
14
+ it('test function with number argument returns string representation', () => {
15
+ const result = test(42);
16
+ expect(result).toBe('42');
17
+ expect(typeof result).toBe('string');
18
+ });
19
+
20
+ it('test function with zero returns "0"', () => {
21
+ const result = test(0);
22
+ expect(result).toBe('0');
23
+ });
24
+
25
+ it('test function with negative number returns string representation', () => {
26
+ const result = test(-100);
27
+ expect(result).toBe('-100');
28
+ });
29
+
30
+ it('test function with empty string returns empty string', () => {
31
+ const result = test('');
32
+ expect(result).toBe('');
33
+ });
34
+
35
+ it('test function with decimal number returns string representation', () => {
36
+ const result = test(3.14159);
37
+ expect(result).toBe('3.14159');
38
+ });
39
+ });
40
+ });