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 +1 -1
- package/src/compiler/phases/3-transform/client/index.js +63 -33
- package/src/compiler/phases/3-transform/segments.js +36 -0
- package/src/compiler/utils.js +8 -8
- package/src/runtime/internal/client/render.js +5 -1
- package/src/runtime/internal/client/runtime.js +20 -5
- package/tests/client/function-overload-import.ripple +10 -0
- package/tests/client/function-overload.test.ripple +40 -0
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.
|
|
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
|
-
|
|
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
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
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
|
-
|
|
1043
|
-
/** @type {Expression} */
|
|
1044
|
-
/** @type {Expression} */
|
|
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
|
-
|
|
1062
|
-
/** @type {Expression} */
|
|
1063
|
-
|
|
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
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
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
|
-
|
|
1340
|
-
|
|
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} */
|
|
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} */
|
|
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}`);
|
package/src/compiler/utils.js
CHANGED
|
@@ -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} */
|
|
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 */
|
|
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} */
|
|
615
|
-
/** @type {Expression} */
|
|
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} */
|
|
706
|
-
/** @type {Expression} */
|
|
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} **/
|
|
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} */
|
|
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} */
|
|
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
|
-
|
|
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} */
|
|
1182
|
+
}, /** @type {Block} */(active_block));
|
|
1168
1183
|
}
|
|
1169
1184
|
} else {
|
|
1170
1185
|
value = await v;
|
|
@@ -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
|
+
});
|