ripple 0.3.8 → 0.3.10
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/CHANGELOG.md +19 -0
- package/package.json +2 -2
- package/src/compiler/phases/1-parse/index.js +38 -172
- package/src/compiler/phases/2-analyze/index.js +308 -115
- package/src/compiler/phases/2-analyze/prune.js +13 -5
- package/src/compiler/phases/3-transform/client/index.js +197 -213
- package/src/compiler/phases/3-transform/segments.js +0 -7
- package/src/compiler/phases/3-transform/server/index.js +77 -170
- package/src/compiler/types/acorn.d.ts +1 -1
- package/src/compiler/types/estree.d.ts +1 -1
- package/src/compiler/types/import.d.ts +0 -2
- package/src/compiler/types/index.d.ts +14 -18
- package/src/compiler/types/parse.d.ts +3 -9
- package/src/compiler/utils.js +154 -21
- package/src/runtime/element.js +39 -0
- package/src/runtime/index-client.js +2 -13
- package/src/runtime/index-server.js +2 -2
- package/src/runtime/internal/client/bindings.js +3 -1
- package/src/runtime/internal/client/composite.js +11 -6
- package/src/runtime/internal/client/events.js +1 -1
- package/src/runtime/internal/client/expression.js +218 -0
- package/src/runtime/internal/client/head.js +3 -4
- package/src/runtime/internal/client/index.js +4 -1
- package/src/runtime/internal/client/portal.js +12 -6
- package/src/runtime/internal/client/runtime.js +0 -52
- package/src/runtime/internal/server/index.js +57 -56
- package/tests/client/basic/basic.components.test.ripple +85 -87
- package/tests/client/basic/basic.errors.test.ripple +28 -4
- package/tests/client/basic/basic.reactivity.test.ripple +10 -155
- package/tests/client/basic/basic.rendering.test.ripple +23 -8
- package/tests/client/capture-error.js +12 -0
- package/tests/client/compiler/compiler.basic.test.ripple +107 -18
- package/tests/client/composite/composite.props.test.ripple +5 -9
- package/tests/client/composite/composite.reactivity.test.ripple +35 -36
- package/tests/client/composite/composite.render.test.ripple +45 -13
- package/tests/client/css/global-additional-cases.test.ripple +3 -3
- package/tests/client/dynamic-elements.test.ripple +3 -4
- package/tests/client/lazy-destructuring.test.ripple +69 -12
- package/tests/client/svg.test.ripple +4 -4
- package/tests/hydration/basic.test.js +23 -0
- package/tests/hydration/compiled/client/basic.js +118 -66
- package/tests/hydration/compiled/client/composite.js +90 -37
- package/tests/hydration/compiled/client/events.js +18 -18
- package/tests/hydration/compiled/client/for.js +62 -62
- package/tests/hydration/compiled/client/head.js +10 -10
- package/tests/hydration/compiled/client/hmr.js +13 -10
- package/tests/hydration/compiled/client/html.js +274 -236
- package/tests/hydration/compiled/client/if-children.js +41 -35
- package/tests/hydration/compiled/client/if.js +2 -2
- package/tests/hydration/compiled/client/mixed-control-flow.js +12 -12
- package/tests/hydration/compiled/client/nested-control-flow.js +46 -46
- package/tests/hydration/compiled/client/portal.js +8 -8
- package/tests/hydration/compiled/client/reactivity.js +14 -14
- package/tests/hydration/compiled/client/return.js +2 -2
- package/tests/hydration/compiled/client/try.js +4 -4
- package/tests/hydration/compiled/server/basic.js +64 -31
- package/tests/hydration/compiled/server/composite.js +62 -29
- package/tests/hydration/compiled/server/hmr.js +24 -37
- package/tests/hydration/compiled/server/html.js +472 -611
- package/tests/hydration/compiled/server/if-children.js +77 -103
- package/tests/hydration/compiled/server/portal.js +8 -8
- package/tests/hydration/components/basic.ripple +15 -5
- package/tests/hydration/components/composite.ripple +13 -1
- package/tests/hydration/components/hmr.ripple +1 -3
- package/tests/hydration/components/html.ripple +13 -35
- package/tests/hydration/components/if-children.ripple +4 -8
- package/tests/hydration/composite.test.js +11 -0
- package/tests/server/basic.attributes.test.ripple +50 -0
- package/tests/server/basic.components.test.ripple +22 -28
- package/tests/server/basic.test.ripple +12 -0
- package/tests/server/compiler.test.ripple +43 -4
- package/tests/server/composite.props.test.ripple +5 -9
- package/tests/server/dynamic-elements.test.ripple +3 -4
- package/tests/server/lazy-destructuring.test.ripple +68 -12
- package/tests/server/style-identifier.test.ripple +2 -4
- package/tsconfig.typecheck.json +4 -0
- package/types/index.d.ts +9 -21
- package/tests/client/__snapshots__/tracked-expression.test.ripple.snap +0 -34
- package/tests/client/tracked-expression.test.ripple +0 -26
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
AnalysisResult,
|
|
5
5
|
AnalysisState,
|
|
6
6
|
AnalysisContext,
|
|
7
|
+
Context,
|
|
7
8
|
ScopeInterface,
|
|
8
9
|
Visitors,
|
|
9
10
|
TopScopedClasses,
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
is_inside_component,
|
|
26
27
|
is_ripple_track_call,
|
|
27
28
|
is_void_element,
|
|
29
|
+
is_children_template_expression as is_children_template_expression_in_scope,
|
|
28
30
|
normalize_children,
|
|
29
31
|
is_binding_function,
|
|
30
32
|
is_inside_try_block,
|
|
@@ -329,14 +331,185 @@ function setup_lazy_array_transforms(pattern, source_id, state, writable) {
|
|
|
329
331
|
}
|
|
330
332
|
|
|
331
333
|
/**
|
|
332
|
-
*
|
|
334
|
+
* @param {AST.Pattern} pattern
|
|
335
|
+
* @returns {AST.TypeNode | undefined}
|
|
336
|
+
*/
|
|
337
|
+
function get_pattern_type_annotation(pattern) {
|
|
338
|
+
return pattern.typeAnnotation?.typeAnnotation;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* @param {AST.TypeNode | undefined} type_annotation
|
|
343
|
+
* @returns {AST.TypeNode | undefined}
|
|
344
|
+
*/
|
|
345
|
+
function unwrap_type_annotation(type_annotation) {
|
|
346
|
+
/** @type {AST.TypeNode | undefined} */
|
|
347
|
+
let annotation = type_annotation;
|
|
348
|
+
|
|
349
|
+
while (annotation) {
|
|
350
|
+
if (annotation.type === 'TSParenthesizedType') {
|
|
351
|
+
annotation = annotation.typeAnnotation;
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
if (annotation.type === 'TSOptionalType') {
|
|
355
|
+
annotation = annotation.typeAnnotation;
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return annotation;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* @param {AST.TypeNode} type_annotation
|
|
366
|
+
* @returns {AST.TypeNode}
|
|
367
|
+
*/
|
|
368
|
+
function normalize_tuple_element_type(type_annotation) {
|
|
369
|
+
/** @type {AST.TypeNode} */
|
|
370
|
+
let annotation = type_annotation;
|
|
371
|
+
|
|
372
|
+
while (true) {
|
|
373
|
+
if (annotation.type === 'TSNamedTupleMember') {
|
|
374
|
+
annotation = annotation.elementType;
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (annotation.type === 'TSParenthesizedType') {
|
|
378
|
+
annotation = annotation.typeAnnotation;
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
if (annotation.type === 'TSOptionalType') {
|
|
382
|
+
annotation = annotation.typeAnnotation;
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return annotation;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* @param {AST.Expression} key
|
|
393
|
+
* @returns {string | null}
|
|
394
|
+
*/
|
|
395
|
+
function get_object_pattern_key_name(key) {
|
|
396
|
+
if (key.type === 'Identifier') {
|
|
397
|
+
return key.name;
|
|
398
|
+
}
|
|
399
|
+
if (key.type === 'Literal' && (typeof key.value === 'string' || typeof key.value === 'number')) {
|
|
400
|
+
return String(key.value);
|
|
401
|
+
}
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* @param {AST.PropertyNameNonComputed} key
|
|
407
|
+
* @returns {string | null}
|
|
408
|
+
*/
|
|
409
|
+
function get_type_property_key_name(key) {
|
|
410
|
+
if (key.type === 'Identifier') {
|
|
411
|
+
return key.name;
|
|
412
|
+
}
|
|
413
|
+
if (key.type === 'Literal' && (typeof key.value === 'string' || typeof key.value === 'number')) {
|
|
414
|
+
return String(key.value);
|
|
415
|
+
}
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* @param {AST.TypeNode | undefined} type_annotation
|
|
421
|
+
* @param {AST.Property | AST.RestElement} property
|
|
422
|
+
* @returns {AST.TypeNode | undefined}
|
|
423
|
+
*/
|
|
424
|
+
function get_object_property_type_annotation(type_annotation, property) {
|
|
425
|
+
if (property.type === 'RestElement' || property.computed) {
|
|
426
|
+
return undefined;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const object_type_annotation = unwrap_type_annotation(type_annotation);
|
|
430
|
+
if (object_type_annotation?.type !== 'TSTypeLiteral') {
|
|
431
|
+
return undefined;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const key_name = get_object_pattern_key_name(property.key);
|
|
435
|
+
if (key_name === null) {
|
|
436
|
+
return undefined;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
for (const member of object_type_annotation.members) {
|
|
440
|
+
if (member.type !== 'TSPropertySignature' || member.computed) {
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
const member_key_name = get_type_property_key_name(member.key);
|
|
444
|
+
if (member_key_name === key_name) {
|
|
445
|
+
return member.typeAnnotation?.typeAnnotation;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return undefined;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* @param {AST.TypeNode | undefined} type_annotation
|
|
454
|
+
* @param {number} index
|
|
455
|
+
* @param {boolean} is_rest
|
|
456
|
+
* @returns {AST.TypeNode | undefined}
|
|
457
|
+
*/
|
|
458
|
+
function get_array_element_type_annotation(type_annotation, index, is_rest) {
|
|
459
|
+
const array_type_annotation = unwrap_type_annotation(type_annotation);
|
|
460
|
+
|
|
461
|
+
if (array_type_annotation?.type === 'TSArrayType') {
|
|
462
|
+
return array_type_annotation.elementType;
|
|
463
|
+
}
|
|
464
|
+
if (array_type_annotation?.type !== 'TSTupleType') {
|
|
465
|
+
return undefined;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (is_rest) {
|
|
469
|
+
for (let i = array_type_annotation.elementTypes.length - 1; i >= 0; i -= 1) {
|
|
470
|
+
const element_type = normalize_tuple_element_type(array_type_annotation.elementTypes[i]);
|
|
471
|
+
if (element_type.type === 'TSRestType') {
|
|
472
|
+
return element_type.typeAnnotation;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return undefined;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (index < array_type_annotation.elementTypes.length) {
|
|
479
|
+
const element_type = normalize_tuple_element_type(array_type_annotation.elementTypes[index]);
|
|
480
|
+
if (element_type.type === 'TSRestType') {
|
|
481
|
+
const rest_type_annotation = unwrap_type_annotation(element_type.typeAnnotation);
|
|
482
|
+
return rest_type_annotation?.type === 'TSArrayType'
|
|
483
|
+
? rest_type_annotation.elementType
|
|
484
|
+
: element_type.typeAnnotation;
|
|
485
|
+
}
|
|
486
|
+
return element_type;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const last_element = array_type_annotation.elementTypes.at(-1);
|
|
490
|
+
if (!last_element) {
|
|
491
|
+
return undefined;
|
|
492
|
+
}
|
|
493
|
+
const normalized_last_element = normalize_tuple_element_type(last_element);
|
|
494
|
+
if (normalized_last_element.type === 'TSRestType') {
|
|
495
|
+
const rest_type_annotation = unwrap_type_annotation(normalized_last_element.typeAnnotation);
|
|
496
|
+
return rest_type_annotation?.type === 'TSArrayType'
|
|
497
|
+
? rest_type_annotation.elementType
|
|
498
|
+
: normalized_last_element.typeAnnotation;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return undefined;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Checks if a parameter source has a Tracked<T> type annotation imported from ripple.
|
|
333
506
|
* This is used to determine if lazy array destructuring should use the track tuple fast path.
|
|
334
|
-
* @param {AST.
|
|
507
|
+
* @param {AST.TypeNode | undefined} type_annotation - The source type annotation
|
|
335
508
|
* @param {AnalysisContext} context - The analysis context
|
|
336
509
|
* @returns {boolean}
|
|
337
510
|
*/
|
|
338
|
-
function is_param_tracked_type(
|
|
339
|
-
const annotation =
|
|
511
|
+
function is_param_tracked_type(type_annotation, context) {
|
|
512
|
+
const annotation = unwrap_type_annotation(type_annotation);
|
|
340
513
|
|
|
341
514
|
if (
|
|
342
515
|
annotation?.type === 'TSTypeReference' &&
|
|
@@ -357,6 +530,75 @@ function is_param_tracked_type(param, context) {
|
|
|
357
530
|
return false;
|
|
358
531
|
}
|
|
359
532
|
|
|
533
|
+
/**
|
|
534
|
+
* Sets up lazy transforms for any lazy subpatterns nested inside a function or component param.
|
|
535
|
+
* @param {AST.Pattern} pattern
|
|
536
|
+
* @param {AnalysisContext} context
|
|
537
|
+
* @param {AST.TypeNode | undefined} [type_annotation]
|
|
538
|
+
*/
|
|
539
|
+
function setup_nested_lazy_param_transforms(pattern, context, type_annotation = undefined) {
|
|
540
|
+
const pattern_type_annotation = get_pattern_type_annotation(pattern) ?? type_annotation;
|
|
541
|
+
|
|
542
|
+
switch (pattern.type) {
|
|
543
|
+
case 'AssignmentPattern':
|
|
544
|
+
setup_nested_lazy_param_transforms(pattern.left, context, pattern_type_annotation);
|
|
545
|
+
return;
|
|
546
|
+
|
|
547
|
+
case 'RestElement':
|
|
548
|
+
setup_nested_lazy_param_transforms(pattern.argument, context, pattern_type_annotation);
|
|
549
|
+
return;
|
|
550
|
+
|
|
551
|
+
case 'ObjectPattern':
|
|
552
|
+
case 'ArrayPattern': {
|
|
553
|
+
if (pattern.lazy) {
|
|
554
|
+
const param_id = b.id(context.state.scope.generate('lazy'));
|
|
555
|
+
const is_tracked_type =
|
|
556
|
+
pattern.type === 'ArrayPattern' &&
|
|
557
|
+
is_param_tracked_type(pattern_type_annotation, context);
|
|
558
|
+
|
|
559
|
+
setup_lazy_transforms(pattern, param_id, context.state, true, is_tracked_type);
|
|
560
|
+
pattern.metadata = { ...pattern.metadata, lazy_id: param_id.name };
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if (pattern.type === 'ObjectPattern') {
|
|
565
|
+
for (const property of pattern.properties) {
|
|
566
|
+
const property_type_annotation = get_object_property_type_annotation(
|
|
567
|
+
pattern_type_annotation,
|
|
568
|
+
property,
|
|
569
|
+
);
|
|
570
|
+
if (property.type === 'RestElement') {
|
|
571
|
+
setup_nested_lazy_param_transforms(
|
|
572
|
+
property.argument,
|
|
573
|
+
context,
|
|
574
|
+
property_type_annotation,
|
|
575
|
+
);
|
|
576
|
+
} else {
|
|
577
|
+
setup_nested_lazy_param_transforms(property.value, context, property_type_annotation);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
} else {
|
|
581
|
+
for (let i = 0; i < pattern.elements.length; i += 1) {
|
|
582
|
+
const element = pattern.elements[i];
|
|
583
|
+
if (element !== null) {
|
|
584
|
+
setup_nested_lazy_param_transforms(
|
|
585
|
+
element,
|
|
586
|
+
context,
|
|
587
|
+
get_array_element_type_annotation(
|
|
588
|
+
pattern_type_annotation,
|
|
589
|
+
i,
|
|
590
|
+
element.type === 'RestElement',
|
|
591
|
+
),
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
360
602
|
/**
|
|
361
603
|
* @param {AST.Function} node
|
|
362
604
|
* @param {AnalysisContext} context
|
|
@@ -371,16 +613,11 @@ function visit_function(node, context) {
|
|
|
371
613
|
for (let i = 0; i < node.params.length; i++) {
|
|
372
614
|
const param_node = node.params[i];
|
|
373
615
|
const param = param_node.type === 'AssignmentPattern' ? param_node.left : param_node;
|
|
616
|
+
const param_type_annotation =
|
|
617
|
+
get_pattern_type_annotation(param) ?? param_node.typeAnnotation?.typeAnnotation;
|
|
374
618
|
|
|
375
|
-
if (
|
|
376
|
-
|
|
377
|
-
// For ArrayPattern params with a Tracked<T> type annotation from ripple,
|
|
378
|
-
// use the track tuple fast path (get/set instead of source[0]/source[1])
|
|
379
|
-
const is_tracked_type =
|
|
380
|
-
param.type === 'ArrayPattern' && is_param_tracked_type(param, context);
|
|
381
|
-
setup_lazy_transforms(param, param_id, context.state, true, is_tracked_type);
|
|
382
|
-
// Store the generated identifier name on the pattern for the transform phase
|
|
383
|
-
param.metadata = { ...param.metadata, lazy_id: param_id.name };
|
|
619
|
+
if (param.type === 'ObjectPattern' || param.type === 'ArrayPattern') {
|
|
620
|
+
setup_nested_lazy_param_transforms(param, context, param_type_annotation);
|
|
384
621
|
}
|
|
385
622
|
}
|
|
386
623
|
|
|
@@ -453,74 +690,13 @@ function error_return_keyword(node, context, message) {
|
|
|
453
690
|
|
|
454
691
|
/**
|
|
455
692
|
* @param {AST.Expression} expression
|
|
456
|
-
* @
|
|
457
|
-
*/
|
|
458
|
-
function unwrap_template_expression(expression) {
|
|
459
|
-
/** @type {AST.Expression} */
|
|
460
|
-
let node = expression;
|
|
461
|
-
|
|
462
|
-
while (true) {
|
|
463
|
-
if (
|
|
464
|
-
node.type === 'ParenthesizedExpression' ||
|
|
465
|
-
node.type === 'TSAsExpression' ||
|
|
466
|
-
node.type === 'TSSatisfiesExpression' ||
|
|
467
|
-
node.type === 'TSNonNullExpression' ||
|
|
468
|
-
node.type === 'TSInstantiationExpression'
|
|
469
|
-
) {
|
|
470
|
-
node = /** @type {AST.Expression} */ (node.expression);
|
|
471
|
-
continue;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
if (node.type === 'ChainExpression') {
|
|
475
|
-
node = /** @type {AST.Expression} */ (node.expression);
|
|
476
|
-
continue;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
break;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
return node;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* @param {AST.Expression} expression
|
|
487
|
-
* @param {AnalysisState} state
|
|
693
|
+
* @param {Context<AST.Node, AnalysisState>} context
|
|
488
694
|
* @returns {boolean}
|
|
489
695
|
*/
|
|
490
|
-
function is_children_template_expression(expression,
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
return is_children_template_expression(
|
|
495
|
-
/** @type {AST.Expression} */ (unwrapped.argument),
|
|
496
|
-
state,
|
|
497
|
-
);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
if (unwrapped.type === 'MemberExpression') {
|
|
501
|
-
let property_name = null;
|
|
502
|
-
|
|
503
|
-
if (!unwrapped.computed && unwrapped.property.type === 'Identifier') {
|
|
504
|
-
property_name = unwrapped.property.name;
|
|
505
|
-
} else if (
|
|
506
|
-
unwrapped.computed &&
|
|
507
|
-
unwrapped.property.type === 'Literal' &&
|
|
508
|
-
typeof unwrapped.property.value === 'string'
|
|
509
|
-
) {
|
|
510
|
-
property_name = unwrapped.property.value;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
if (property_name === 'children') {
|
|
514
|
-
const target = unwrap_template_expression(/** @type {AST.Expression} */ (unwrapped.object));
|
|
515
|
-
|
|
516
|
-
if (target.type === 'Identifier') {
|
|
517
|
-
const binding = state.scope.get(target.name);
|
|
518
|
-
return binding?.declaration_kind === 'param';
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
return unwrapped.type === 'Identifier' && unwrapped.name === 'children';
|
|
696
|
+
function is_children_template_expression(expression, context) {
|
|
697
|
+
const component = context.path.findLast((node) => node.type === 'Component');
|
|
698
|
+
const component_scope = component ? context.state.scopes.get(component) : null;
|
|
699
|
+
return is_children_template_expression_in_scope(expression, context.state.scope, component_scope);
|
|
524
700
|
}
|
|
525
701
|
|
|
526
702
|
/** @type {Visitors<AST.Node, AnalysisState>} */
|
|
@@ -632,16 +808,6 @@ const visitors = {
|
|
|
632
808
|
MemberExpression(node, context) {
|
|
633
809
|
const parent = context.path.at(-1);
|
|
634
810
|
|
|
635
|
-
if (
|
|
636
|
-
context.state.metadata?.tracking === false &&
|
|
637
|
-
parent?.type !== 'AssignmentExpression' &&
|
|
638
|
-
(node.tracked ||
|
|
639
|
-
((node.property.type === 'Identifier' || node.property.type === 'Literal') &&
|
|
640
|
-
/** @type {AST.TrackedNode} */ (node.property).tracked))
|
|
641
|
-
) {
|
|
642
|
-
context.state.metadata.tracking = true;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
811
|
// Track #style.className or #style['className'] references
|
|
646
812
|
if (node.object.type === 'StyleIdentifier') {
|
|
647
813
|
const component = is_inside_component(context, true);
|
|
@@ -761,6 +927,19 @@ const visitors = {
|
|
|
761
927
|
|
|
762
928
|
const callee = node.callee;
|
|
763
929
|
|
|
930
|
+
if (
|
|
931
|
+
!context.path.some((path_node) => path_node.type === 'TsxCompat') &&
|
|
932
|
+
is_children_template_expression(/** @type {AST.Expression} */ (callee), context)
|
|
933
|
+
) {
|
|
934
|
+
error(
|
|
935
|
+
'`children` cannot be called like a regular function. Render it with `{children}` or `{props.children}` instead.',
|
|
936
|
+
context.state.analysis.module.filename,
|
|
937
|
+
callee,
|
|
938
|
+
context.state.loose ? context.state.analysis.errors : undefined,
|
|
939
|
+
context.state.analysis.comments,
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
|
|
764
943
|
if (context.state.function_depth === 0 && is_ripple_track_call(callee, context)) {
|
|
765
944
|
error(
|
|
766
945
|
'`track` can only be used within a reactive context, such as a component, function or class that is used or created from a component',
|
|
@@ -913,9 +1092,13 @@ const visitors = {
|
|
|
913
1092
|
if (node.params.length > 0) {
|
|
914
1093
|
const props = node.params[0];
|
|
915
1094
|
|
|
916
|
-
if (
|
|
1095
|
+
if (props.type === 'ObjectPattern' || props.type === 'ArrayPattern') {
|
|
917
1096
|
// Lazy destructuring: &{...} or &[...] — set up lazy transforms
|
|
918
|
-
|
|
1097
|
+
if (props.lazy) {
|
|
1098
|
+
setup_lazy_transforms(props, b.id('__props'), context.state, true, false);
|
|
1099
|
+
} else {
|
|
1100
|
+
setup_nested_lazy_param_transforms(props, context, get_pattern_type_annotation(props));
|
|
1101
|
+
}
|
|
919
1102
|
} else if (props.type === 'AssignmentPattern') {
|
|
920
1103
|
error(
|
|
921
1104
|
'Props are always an object, use destructured props with default values instead',
|
|
@@ -1463,10 +1646,24 @@ const visitors = {
|
|
|
1463
1646
|
|
|
1464
1647
|
const { state, visit, path } = context;
|
|
1465
1648
|
const is_dom_element = is_element_dom_element(node);
|
|
1649
|
+
/** @type {Set<AST.Identifier>} */
|
|
1466
1650
|
const attribute_names = new Set();
|
|
1467
1651
|
|
|
1468
1652
|
mark_control_flow_has_template(path);
|
|
1469
1653
|
|
|
1654
|
+
if (
|
|
1655
|
+
!is_dom_element &&
|
|
1656
|
+
is_children_template_expression(/** @type {AST.Expression} */ (node.id), context)
|
|
1657
|
+
) {
|
|
1658
|
+
error(
|
|
1659
|
+
'`children` cannot be rendered as a component. Render it with `{children}` or `{props.children}` instead.',
|
|
1660
|
+
state.analysis.module.filename,
|
|
1661
|
+
node.id,
|
|
1662
|
+
context.state.loose ? context.state.analysis.errors : undefined,
|
|
1663
|
+
context.state.analysis.comments,
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1470
1667
|
validate_nesting(node, context);
|
|
1471
1668
|
|
|
1472
1669
|
// Store capitalized name for dynamic components/elements
|
|
@@ -1520,7 +1717,10 @@ const visitors = {
|
|
|
1520
1717
|
if (/** @type {AST.Identifier} */ (node.id).name === 'title') {
|
|
1521
1718
|
const children = normalize_children(node.children, context);
|
|
1522
1719
|
|
|
1523
|
-
if (
|
|
1720
|
+
if (
|
|
1721
|
+
children.length !== 1 ||
|
|
1722
|
+
(children[0].type !== 'RippleExpression' && children[0].type !== 'Text')
|
|
1723
|
+
) {
|
|
1524
1724
|
// TODO: could transform children as something, e.g. Text Node, and avoid a fatal error
|
|
1525
1725
|
error(
|
|
1526
1726
|
'<title> must have only contain text nodes',
|
|
@@ -1665,30 +1865,22 @@ const visitors = {
|
|
|
1665
1865
|
}
|
|
1666
1866
|
/** @type {(AST.Node | AST.Expression)[]} */
|
|
1667
1867
|
let implicit_children = [];
|
|
1668
|
-
/** @type {AST.Identifier[]} */
|
|
1669
|
-
let explicit_children = [];
|
|
1670
1868
|
|
|
1671
1869
|
for (const child of node.children) {
|
|
1672
1870
|
if (child.type === 'Component') {
|
|
1673
|
-
if (child.id?.name === 'children') {
|
|
1674
|
-
explicit_children.push(child.id);
|
|
1675
|
-
}
|
|
1676
|
-
} else if (child.type !== 'EmptyStatement') {
|
|
1677
|
-
implicit_children.push(
|
|
1678
|
-
child.type === 'Text' || child.type === 'Html' ? child.expression : child,
|
|
1679
|
-
);
|
|
1680
|
-
}
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
|
-
if (implicit_children.length > 0 && explicit_children.length > 0) {
|
|
1684
|
-
for (const item of [...explicit_children, ...implicit_children]) {
|
|
1685
1871
|
error(
|
|
1686
|
-
'
|
|
1872
|
+
'Component declarations cannot be used inside composite component children. Pass them as explicit props on the template element instead.',
|
|
1687
1873
|
state.analysis.module.filename,
|
|
1688
|
-
|
|
1874
|
+
child.id || child,
|
|
1689
1875
|
context.state.loose ? context.state.analysis.errors : undefined,
|
|
1690
1876
|
context.state.analysis.comments,
|
|
1691
1877
|
);
|
|
1878
|
+
} else if (child.type !== 'EmptyStatement') {
|
|
1879
|
+
implicit_children.push(
|
|
1880
|
+
child.type === 'RippleExpression' || child.type === 'Text' || child.type === 'Html'
|
|
1881
|
+
? child.expression
|
|
1882
|
+
: child,
|
|
1883
|
+
);
|
|
1692
1884
|
}
|
|
1693
1885
|
}
|
|
1694
1886
|
}
|
|
@@ -1715,17 +1907,18 @@ const visitors = {
|
|
|
1715
1907
|
};
|
|
1716
1908
|
},
|
|
1717
1909
|
|
|
1910
|
+
RippleExpression(node, context) {
|
|
1911
|
+
mark_control_flow_has_template(context.path);
|
|
1912
|
+
|
|
1913
|
+
context.next();
|
|
1914
|
+
},
|
|
1915
|
+
|
|
1718
1916
|
Text(node, context) {
|
|
1719
1917
|
mark_control_flow_has_template(context.path);
|
|
1720
1918
|
|
|
1721
|
-
if (
|
|
1722
|
-
is_children_template_expression(
|
|
1723
|
-
/** @type {AST.Expression} */ (node.expression),
|
|
1724
|
-
context.state,
|
|
1725
|
-
)
|
|
1726
|
-
) {
|
|
1919
|
+
if (is_children_template_expression(/** @type {AST.Expression} */ (node.expression), context)) {
|
|
1727
1920
|
error(
|
|
1728
|
-
'`children` cannot be rendered using text interpolation. Use
|
|
1921
|
+
'`children` cannot be rendered using explicit text interpolation. Use `{children}` or `{props.children}` instead.',
|
|
1729
1922
|
context.state.analysis.module.filename,
|
|
1730
1923
|
node.expression,
|
|
1731
1924
|
context.state.loose ? context.state.analysis.errors : undefined,
|
|
@@ -306,12 +306,20 @@ function get_descendant_elements(node, adjacent_only) {
|
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
-
// For template nodes and
|
|
309
|
+
// For template nodes and interpolation expressions
|
|
310
310
|
if (
|
|
311
|
-
|
|
312
|
-
|
|
311
|
+
(current_node.type === 'RippleExpression' ||
|
|
312
|
+
current_node.type === 'Text' ||
|
|
313
|
+
current_node.type === 'Html') &&
|
|
314
|
+
/** @type {AST.RippleExpression | AST.Html | AST.TextNode} */ (current_node).expression &&
|
|
315
|
+
typeof (
|
|
316
|
+
/** @type {AST.RippleExpression | AST.Html | AST.TextNode} */ (current_node).expression
|
|
317
|
+
) === 'object'
|
|
313
318
|
) {
|
|
314
|
-
visit(
|
|
319
|
+
visit(
|
|
320
|
+
/** @type {AST.RippleExpression | AST.Html | AST.TextNode} */ (current_node).expression,
|
|
321
|
+
depth + 1,
|
|
322
|
+
);
|
|
315
323
|
}
|
|
316
324
|
}
|
|
317
325
|
|
|
@@ -409,7 +417,7 @@ function get_possible_element_siblings(node, direction, adjacent_only) {
|
|
|
409
417
|
// Stop at non-whitespace text nodes for adjacent selectors
|
|
410
418
|
else if (
|
|
411
419
|
adjacent_only &&
|
|
412
|
-
sibling.type === 'Text' &&
|
|
420
|
+
(sibling.type === 'RippleExpression' || sibling.type === 'Text') &&
|
|
413
421
|
sibling.expression.type === 'Literal' &&
|
|
414
422
|
typeof sibling.expression.value === 'string' &&
|
|
415
423
|
sibling.expression.value.trim()
|