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 +1 -1
- package/src/compiler/index.js +1 -1
- package/src/compiler/phases/1-parse/index.js +1 -0
- package/src/compiler/phases/2-analyze/index.js +58 -32
- package/src/compiler/phases/3-transform/client/index.js +63 -33
- package/src/compiler/phases/3-transform/segments.js +36 -0
- package/src/compiler/phases/3-transform/server/index.js +50 -37
- 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/src/runtime/internal/server/index.js +20 -9
- package/src/utils/builders.js +2 -2
- package/tests/client/__snapshots__/for.test.ripple.snap +0 -80
- package/tests/client/basic/__snapshots__/basic.rendering.test.ripple.snap +0 -48
- package/tests/client/basic/basic.errors.test.ripple +16 -0
- package/tests/client/function-overload-import.ripple +10 -0
- package/tests/client/function-overload.test.ripple +40 -0
- package/tests/server/await.test.ripple +61 -0
- package/tests/server/for.test.ripple +44 -0
- package/tests/server/if.test.ripple +21 -1
- package/tests/utils/escaping.test.js +102 -0
- package/tests/utils/events.test.js +147 -0
- package/tests/utils/normalize_css_property_name.test.js +43 -0
- package/tests/utils/patterns.test.js +382 -0
- package/tests/utils/sanitize_template_string.test.js +51 -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",
|
package/src/compiler/index.js
CHANGED
|
@@ -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
|
-
|
|
425
|
-
|
|
426
|
-
|
|
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
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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
|
-
|
|
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
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
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:
|
|
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
|
-
|
|
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}`);
|
|
@@ -120,7 +120,18 @@ const visitors = {
|
|
|
120
120
|
component_fn = b.async(component_fn);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
|
|
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
|
-
|
|
553
|
-
const
|
|
562
|
+
AssignmentExpression(node, context) {
|
|
563
|
+
const left = node.left;
|
|
554
564
|
|
|
555
|
-
if (
|
|
556
|
-
|
|
557
|
-
|
|
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
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
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
|
-
'
|
|
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
|
-
|
|
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
|
-
|
|
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)));
|
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])) {
|