ripple 0.3.7 → 0.3.9
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 +14 -0
- package/package.json +2 -2
- package/src/compiler/phases/1-parse/index.js +48 -349
- package/src/compiler/phases/2-analyze/index.js +343 -52
- package/src/compiler/phases/3-transform/client/index.js +28 -160
- package/src/compiler/phases/3-transform/segments.js +0 -7
- package/src/compiler/phases/3-transform/server/index.js +31 -154
- 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 +5 -17
- package/src/compiler/types/parse.d.ts +1 -17
- package/src/compiler/utils.js +53 -20
- 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 +3 -2
- package/src/runtime/internal/client/events.js +1 -1
- package/src/runtime/internal/client/head.js +3 -4
- package/src/runtime/internal/client/index.js +0 -1
- package/src/runtime/internal/client/runtime.js +0 -52
- package/src/runtime/internal/server/index.js +31 -55
- package/tests/client/array/array.copy-within.test.ripple +12 -12
- package/tests/client/array/array.derived.test.ripple +46 -46
- package/tests/client/array/array.iteration.test.ripple +10 -10
- package/tests/client/array/array.mutations.test.ripple +20 -20
- package/tests/client/array/array.to-methods.test.ripple +6 -6
- package/tests/client/async-suspend.test.ripple +5 -5
- package/tests/client/basic/basic.attributes.test.ripple +81 -81
- package/tests/client/basic/basic.collections.test.ripple +9 -9
- package/tests/client/basic/basic.components.test.ripple +28 -28
- package/tests/client/basic/basic.errors.test.ripple +46 -18
- package/tests/client/basic/basic.events.test.ripple +37 -37
- package/tests/client/basic/basic.get-set.test.ripple +6 -6
- package/tests/client/basic/basic.reactivity.test.ripple +58 -203
- package/tests/client/basic/basic.rendering.test.ripple +19 -19
- package/tests/client/basic/basic.utilities.test.ripple +3 -3
- package/tests/client/boundaries.test.ripple +12 -12
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.ripple.snap +5 -5
- package/tests/client/compiler/compiler.assignments.test.ripple +19 -19
- package/tests/client/compiler/compiler.basic.test.ripple +46 -27
- package/tests/client/compiler/compiler.tracked-access.test.ripple +2 -2
- package/tests/client/composite/composite.dynamic-components.test.ripple +9 -9
- package/tests/client/composite/composite.props.test.ripple +14 -16
- package/tests/client/composite/composite.reactivity.test.ripple +69 -70
- package/tests/client/composite/composite.render.test.ripple +3 -3
- package/tests/client/computed-properties.test.ripple +4 -4
- package/tests/client/date.test.ripple +42 -42
- package/tests/client/dynamic-elements.test.ripple +44 -45
- package/tests/client/events.test.ripple +70 -70
- package/tests/client/for.test.ripple +25 -25
- package/tests/client/head.test.ripple +19 -19
- package/tests/client/html.test.ripple +3 -3
- package/tests/client/input-value.test.ripple +84 -84
- package/tests/client/lazy-destructuring.test.ripple +138 -26
- package/tests/client/map.test.ripple +16 -16
- package/tests/client/media-query.test.ripple +7 -7
- package/tests/client/portal.test.ripple +11 -11
- package/tests/client/ref.test.ripple +4 -4
- package/tests/client/return.test.ripple +52 -52
- package/tests/client/set.test.ripple +6 -6
- package/tests/client/svg.test.ripple +5 -5
- package/tests/client/switch.test.ripple +44 -44
- package/tests/client/try.test.ripple +5 -5
- package/tests/client/url/url.derived.test.ripple +6 -6
- package/tests/client/url-search-params/url-search-params.derived.test.ripple +8 -8
- package/tests/client/url-search-params/url-search-params.iteration.test.ripple +10 -10
- package/tests/client/url-search-params/url-search-params.mutation.test.ripple +10 -10
- package/tests/client/url-search-params/url-search-params.retrieval.test.ripple +18 -18
- package/tests/client/url-search-params/url-search-params.serialization.test.ripple +2 -2
- package/tests/hydration/compiled/client/events.js +25 -25
- package/tests/hydration/compiled/client/for.js +70 -66
- package/tests/hydration/compiled/client/head.js +25 -25
- package/tests/hydration/compiled/client/hmr.js +2 -2
- package/tests/hydration/compiled/client/html.js +3 -3
- package/tests/hydration/compiled/client/if-children.js +24 -24
- package/tests/hydration/compiled/client/if.js +18 -18
- package/tests/hydration/compiled/client/mixed-control-flow.js +9 -9
- package/tests/hydration/compiled/client/portal.js +3 -3
- package/tests/hydration/compiled/client/reactivity.js +16 -16
- package/tests/hydration/compiled/client/return.js +40 -40
- package/tests/hydration/compiled/client/switch.js +12 -12
- package/tests/hydration/compiled/server/events.js +19 -19
- package/tests/hydration/compiled/server/for.js +41 -41
- package/tests/hydration/compiled/server/head.js +26 -26
- package/tests/hydration/compiled/server/hmr.js +2 -2
- package/tests/hydration/compiled/server/html.js +2 -2
- package/tests/hydration/compiled/server/if-children.js +16 -16
- package/tests/hydration/compiled/server/if.js +11 -11
- package/tests/hydration/compiled/server/mixed-control-flow.js +6 -6
- package/tests/hydration/compiled/server/portal.js +2 -2
- package/tests/hydration/compiled/server/reactivity.js +16 -16
- package/tests/hydration/compiled/server/return.js +25 -25
- package/tests/hydration/compiled/server/switch.js +8 -8
- package/tests/hydration/components/events.ripple +25 -25
- package/tests/hydration/components/for.ripple +66 -66
- package/tests/hydration/components/head.ripple +16 -16
- package/tests/hydration/components/hmr.ripple +2 -2
- package/tests/hydration/components/html.ripple +3 -3
- package/tests/hydration/components/if-children.ripple +24 -24
- package/tests/hydration/components/if.ripple +18 -18
- package/tests/hydration/components/mixed-control-flow.ripple +9 -9
- package/tests/hydration/components/portal.ripple +3 -3
- package/tests/hydration/components/reactivity.ripple +16 -16
- package/tests/hydration/components/return.ripple +40 -40
- package/tests/hydration/components/switch.ripple +20 -20
- package/tests/server/await.test.ripple +3 -3
- package/tests/server/basic.attributes.test.ripple +34 -34
- package/tests/server/basic.components.test.ripple +10 -10
- package/tests/server/basic.test.ripple +38 -40
- package/tests/server/compiler.test.ripple +22 -0
- package/tests/server/composite.props.test.ripple +12 -14
- package/tests/server/dynamic-elements.test.ripple +15 -15
- package/tests/server/head.test.ripple +11 -11
- package/tests/server/lazy-destructuring.test.ripple +92 -13
- package/tsconfig.typecheck.json +4 -0
- package/types/index.d.ts +0 -19
- 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,
|
|
@@ -328,6 +329,275 @@ function setup_lazy_array_transforms(pattern, source_id, state, writable) {
|
|
|
328
329
|
}
|
|
329
330
|
}
|
|
330
331
|
|
|
332
|
+
/**
|
|
333
|
+
* @param {AST.Pattern} pattern
|
|
334
|
+
* @returns {AST.TypeNode | undefined}
|
|
335
|
+
*/
|
|
336
|
+
function get_pattern_type_annotation(pattern) {
|
|
337
|
+
return pattern.typeAnnotation?.typeAnnotation;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* @param {AST.TypeNode | undefined} type_annotation
|
|
342
|
+
* @returns {AST.TypeNode | undefined}
|
|
343
|
+
*/
|
|
344
|
+
function unwrap_type_annotation(type_annotation) {
|
|
345
|
+
/** @type {AST.TypeNode | undefined} */
|
|
346
|
+
let annotation = type_annotation;
|
|
347
|
+
|
|
348
|
+
while (annotation) {
|
|
349
|
+
if (annotation.type === 'TSParenthesizedType') {
|
|
350
|
+
annotation = annotation.typeAnnotation;
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (annotation.type === 'TSOptionalType') {
|
|
354
|
+
annotation = annotation.typeAnnotation;
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return annotation;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* @param {AST.TypeNode} type_annotation
|
|
365
|
+
* @returns {AST.TypeNode}
|
|
366
|
+
*/
|
|
367
|
+
function normalize_tuple_element_type(type_annotation) {
|
|
368
|
+
/** @type {AST.TypeNode} */
|
|
369
|
+
let annotation = type_annotation;
|
|
370
|
+
|
|
371
|
+
while (true) {
|
|
372
|
+
if (annotation.type === 'TSNamedTupleMember') {
|
|
373
|
+
annotation = annotation.elementType;
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
if (annotation.type === 'TSParenthesizedType') {
|
|
377
|
+
annotation = annotation.typeAnnotation;
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
if (annotation.type === 'TSOptionalType') {
|
|
381
|
+
annotation = annotation.typeAnnotation;
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return annotation;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* @param {AST.Expression} key
|
|
392
|
+
* @returns {string | null}
|
|
393
|
+
*/
|
|
394
|
+
function get_object_pattern_key_name(key) {
|
|
395
|
+
if (key.type === 'Identifier') {
|
|
396
|
+
return key.name;
|
|
397
|
+
}
|
|
398
|
+
if (key.type === 'Literal' && (typeof key.value === 'string' || typeof key.value === 'number')) {
|
|
399
|
+
return String(key.value);
|
|
400
|
+
}
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* @param {AST.PropertyNameNonComputed} key
|
|
406
|
+
* @returns {string | null}
|
|
407
|
+
*/
|
|
408
|
+
function get_type_property_key_name(key) {
|
|
409
|
+
if (key.type === 'Identifier') {
|
|
410
|
+
return key.name;
|
|
411
|
+
}
|
|
412
|
+
if (key.type === 'Literal' && (typeof key.value === 'string' || typeof key.value === 'number')) {
|
|
413
|
+
return String(key.value);
|
|
414
|
+
}
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* @param {AST.TypeNode | undefined} type_annotation
|
|
420
|
+
* @param {AST.Property | AST.RestElement} property
|
|
421
|
+
* @returns {AST.TypeNode | undefined}
|
|
422
|
+
*/
|
|
423
|
+
function get_object_property_type_annotation(type_annotation, property) {
|
|
424
|
+
if (property.type === 'RestElement' || property.computed) {
|
|
425
|
+
return undefined;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const object_type_annotation = unwrap_type_annotation(type_annotation);
|
|
429
|
+
if (object_type_annotation?.type !== 'TSTypeLiteral') {
|
|
430
|
+
return undefined;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const key_name = get_object_pattern_key_name(property.key);
|
|
434
|
+
if (key_name === null) {
|
|
435
|
+
return undefined;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
for (const member of object_type_annotation.members) {
|
|
439
|
+
if (member.type !== 'TSPropertySignature' || member.computed) {
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
const member_key_name = get_type_property_key_name(member.key);
|
|
443
|
+
if (member_key_name === key_name) {
|
|
444
|
+
return member.typeAnnotation?.typeAnnotation;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return undefined;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* @param {AST.TypeNode | undefined} type_annotation
|
|
453
|
+
* @param {number} index
|
|
454
|
+
* @param {boolean} is_rest
|
|
455
|
+
* @returns {AST.TypeNode | undefined}
|
|
456
|
+
*/
|
|
457
|
+
function get_array_element_type_annotation(type_annotation, index, is_rest) {
|
|
458
|
+
const array_type_annotation = unwrap_type_annotation(type_annotation);
|
|
459
|
+
|
|
460
|
+
if (array_type_annotation?.type === 'TSArrayType') {
|
|
461
|
+
return array_type_annotation.elementType;
|
|
462
|
+
}
|
|
463
|
+
if (array_type_annotation?.type !== 'TSTupleType') {
|
|
464
|
+
return undefined;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (is_rest) {
|
|
468
|
+
for (let i = array_type_annotation.elementTypes.length - 1; i >= 0; i -= 1) {
|
|
469
|
+
const element_type = normalize_tuple_element_type(array_type_annotation.elementTypes[i]);
|
|
470
|
+
if (element_type.type === 'TSRestType') {
|
|
471
|
+
return element_type.typeAnnotation;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return undefined;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (index < array_type_annotation.elementTypes.length) {
|
|
478
|
+
const element_type = normalize_tuple_element_type(array_type_annotation.elementTypes[index]);
|
|
479
|
+
if (element_type.type === 'TSRestType') {
|
|
480
|
+
const rest_type_annotation = unwrap_type_annotation(element_type.typeAnnotation);
|
|
481
|
+
return rest_type_annotation?.type === 'TSArrayType'
|
|
482
|
+
? rest_type_annotation.elementType
|
|
483
|
+
: element_type.typeAnnotation;
|
|
484
|
+
}
|
|
485
|
+
return element_type;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const last_element = array_type_annotation.elementTypes.at(-1);
|
|
489
|
+
if (!last_element) {
|
|
490
|
+
return undefined;
|
|
491
|
+
}
|
|
492
|
+
const normalized_last_element = normalize_tuple_element_type(last_element);
|
|
493
|
+
if (normalized_last_element.type === 'TSRestType') {
|
|
494
|
+
const rest_type_annotation = unwrap_type_annotation(normalized_last_element.typeAnnotation);
|
|
495
|
+
return rest_type_annotation?.type === 'TSArrayType'
|
|
496
|
+
? rest_type_annotation.elementType
|
|
497
|
+
: normalized_last_element.typeAnnotation;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return undefined;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Checks if a parameter source has a Tracked<T> type annotation imported from ripple.
|
|
505
|
+
* This is used to determine if lazy array destructuring should use the track tuple fast path.
|
|
506
|
+
* @param {AST.TypeNode | undefined} type_annotation - The source type annotation
|
|
507
|
+
* @param {AnalysisContext} context - The analysis context
|
|
508
|
+
* @returns {boolean}
|
|
509
|
+
*/
|
|
510
|
+
function is_param_tracked_type(type_annotation, context) {
|
|
511
|
+
const annotation = unwrap_type_annotation(type_annotation);
|
|
512
|
+
|
|
513
|
+
if (
|
|
514
|
+
annotation?.type === 'TSTypeReference' &&
|
|
515
|
+
annotation.typeName?.type === 'Identifier' &&
|
|
516
|
+
annotation.typeName.name === 'Tracked'
|
|
517
|
+
) {
|
|
518
|
+
const binding = context.state.scope.get('Tracked');
|
|
519
|
+
|
|
520
|
+
return (
|
|
521
|
+
binding?.declaration_kind === 'import' &&
|
|
522
|
+
binding.initial !== null &&
|
|
523
|
+
binding.initial.type === 'ImportDeclaration' &&
|
|
524
|
+
binding.initial.source.type === 'Literal' &&
|
|
525
|
+
binding.initial.source.value === 'ripple'
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Sets up lazy transforms for any lazy subpatterns nested inside a function or component param.
|
|
534
|
+
* @param {AST.Pattern} pattern
|
|
535
|
+
* @param {AnalysisContext} context
|
|
536
|
+
* @param {AST.TypeNode | undefined} [type_annotation]
|
|
537
|
+
*/
|
|
538
|
+
function setup_nested_lazy_param_transforms(pattern, context, type_annotation = undefined) {
|
|
539
|
+
const pattern_type_annotation = get_pattern_type_annotation(pattern) ?? type_annotation;
|
|
540
|
+
|
|
541
|
+
switch (pattern.type) {
|
|
542
|
+
case 'AssignmentPattern':
|
|
543
|
+
setup_nested_lazy_param_transforms(pattern.left, context, pattern_type_annotation);
|
|
544
|
+
return;
|
|
545
|
+
|
|
546
|
+
case 'RestElement':
|
|
547
|
+
setup_nested_lazy_param_transforms(pattern.argument, context, pattern_type_annotation);
|
|
548
|
+
return;
|
|
549
|
+
|
|
550
|
+
case 'ObjectPattern':
|
|
551
|
+
case 'ArrayPattern': {
|
|
552
|
+
if (pattern.lazy) {
|
|
553
|
+
const param_id = b.id(context.state.scope.generate('lazy'));
|
|
554
|
+
const is_tracked_type =
|
|
555
|
+
pattern.type === 'ArrayPattern' &&
|
|
556
|
+
is_param_tracked_type(pattern_type_annotation, context);
|
|
557
|
+
|
|
558
|
+
setup_lazy_transforms(pattern, param_id, context.state, true, is_tracked_type);
|
|
559
|
+
pattern.metadata = { ...pattern.metadata, lazy_id: param_id.name };
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (pattern.type === 'ObjectPattern') {
|
|
564
|
+
for (const property of pattern.properties) {
|
|
565
|
+
const property_type_annotation = get_object_property_type_annotation(
|
|
566
|
+
pattern_type_annotation,
|
|
567
|
+
property,
|
|
568
|
+
);
|
|
569
|
+
if (property.type === 'RestElement') {
|
|
570
|
+
setup_nested_lazy_param_transforms(
|
|
571
|
+
property.argument,
|
|
572
|
+
context,
|
|
573
|
+
property_type_annotation,
|
|
574
|
+
);
|
|
575
|
+
} else {
|
|
576
|
+
setup_nested_lazy_param_transforms(property.value, context, property_type_annotation);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
} else {
|
|
580
|
+
for (let i = 0; i < pattern.elements.length; i += 1) {
|
|
581
|
+
const element = pattern.elements[i];
|
|
582
|
+
if (element !== null) {
|
|
583
|
+
setup_nested_lazy_param_transforms(
|
|
584
|
+
element,
|
|
585
|
+
context,
|
|
586
|
+
get_array_element_type_annotation(
|
|
587
|
+
pattern_type_annotation,
|
|
588
|
+
i,
|
|
589
|
+
element.type === 'RestElement',
|
|
590
|
+
),
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
331
601
|
/**
|
|
332
602
|
* @param {AST.Function} node
|
|
333
603
|
* @param {AnalysisContext} context
|
|
@@ -342,12 +612,11 @@ function visit_function(node, context) {
|
|
|
342
612
|
for (let i = 0; i < node.params.length; i++) {
|
|
343
613
|
const param_node = node.params[i];
|
|
344
614
|
const param = param_node.type === 'AssignmentPattern' ? param_node.left : param_node;
|
|
615
|
+
const param_type_annotation =
|
|
616
|
+
get_pattern_type_annotation(param) ?? param_node.typeAnnotation?.typeAnnotation;
|
|
345
617
|
|
|
346
|
-
if (
|
|
347
|
-
|
|
348
|
-
setup_lazy_transforms(param, param_id, context.state, true, false);
|
|
349
|
-
// Store the generated identifier name on the pattern for the transform phase
|
|
350
|
-
param.metadata = { ...param.metadata, lazy_id: param_id.name };
|
|
618
|
+
if (param.type === 'ObjectPattern' || param.type === 'ArrayPattern') {
|
|
619
|
+
setup_nested_lazy_param_transforms(param, context, param_type_annotation);
|
|
351
620
|
}
|
|
352
621
|
}
|
|
353
622
|
|
|
@@ -451,19 +720,14 @@ function unwrap_template_expression(expression) {
|
|
|
451
720
|
|
|
452
721
|
/**
|
|
453
722
|
* @param {AST.Expression} expression
|
|
454
|
-
* @param {AnalysisState}
|
|
723
|
+
* @param {Context<AST.Node, AnalysisState>} context
|
|
455
724
|
* @returns {boolean}
|
|
456
725
|
*/
|
|
457
|
-
function is_children_template_expression(expression,
|
|
726
|
+
function is_children_template_expression(expression, context) {
|
|
727
|
+
const component = context.path.findLast((node) => node.type === 'Component');
|
|
728
|
+
const component_scope = component ? context.state.scopes.get(component) : null;
|
|
458
729
|
const unwrapped = unwrap_template_expression(expression);
|
|
459
730
|
|
|
460
|
-
if (unwrapped.type === 'TrackedExpression') {
|
|
461
|
-
return is_children_template_expression(
|
|
462
|
-
/** @type {AST.Expression} */ (unwrapped.argument),
|
|
463
|
-
state,
|
|
464
|
-
);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
731
|
if (unwrapped.type === 'MemberExpression') {
|
|
468
732
|
let property_name = null;
|
|
469
733
|
|
|
@@ -481,13 +745,25 @@ function is_children_template_expression(expression, state) {
|
|
|
481
745
|
const target = unwrap_template_expression(/** @type {AST.Expression} */ (unwrapped.object));
|
|
482
746
|
|
|
483
747
|
if (target.type === 'Identifier') {
|
|
484
|
-
const binding = state.scope.get(target.name);
|
|
485
|
-
return binding?.declaration_kind === 'param';
|
|
748
|
+
const binding = context.state.scope.get(target.name);
|
|
749
|
+
return binding?.declaration_kind === 'param' && binding.scope === component_scope;
|
|
486
750
|
}
|
|
487
751
|
}
|
|
488
752
|
}
|
|
489
753
|
|
|
490
|
-
|
|
754
|
+
if (unwrapped.type !== 'Identifier' || unwrapped.name !== 'children') {
|
|
755
|
+
return false;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const binding = context.state.scope.get(unwrapped.name);
|
|
759
|
+
return (
|
|
760
|
+
(binding?.declaration_kind === 'param' ||
|
|
761
|
+
binding?.kind === 'prop' ||
|
|
762
|
+
binding?.kind === 'prop_fallback' ||
|
|
763
|
+
binding?.kind === 'lazy' ||
|
|
764
|
+
binding?.kind === 'lazy_fallback') &&
|
|
765
|
+
binding.scope === component_scope
|
|
766
|
+
);
|
|
491
767
|
}
|
|
492
768
|
|
|
493
769
|
/** @type {Visitors<AST.Node, AnalysisState>} */
|
|
@@ -579,7 +855,7 @@ const visitors = {
|
|
|
579
855
|
}
|
|
580
856
|
|
|
581
857
|
// Lazy bindings from track() calls (read_unwraps) are inherently reactive —
|
|
582
|
-
// propagate tracking
|
|
858
|
+
// propagate tracking so that control flow (if/for/switch)
|
|
583
859
|
// and early returns create reactive blocks
|
|
584
860
|
if (
|
|
585
861
|
!node.tracked &&
|
|
@@ -599,16 +875,6 @@ const visitors = {
|
|
|
599
875
|
MemberExpression(node, context) {
|
|
600
876
|
const parent = context.path.at(-1);
|
|
601
877
|
|
|
602
|
-
if (
|
|
603
|
-
context.state.metadata?.tracking === false &&
|
|
604
|
-
parent?.type !== 'AssignmentExpression' &&
|
|
605
|
-
(node.tracked ||
|
|
606
|
-
((node.property.type === 'Identifier' || node.property.type === 'Literal') &&
|
|
607
|
-
/** @type {AST.TrackedNode} */ (node.property).tracked))
|
|
608
|
-
) {
|
|
609
|
-
context.state.metadata.tracking = true;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
878
|
// Track #style.className or #style['className'] references
|
|
613
879
|
if (node.object.type === 'StyleIdentifier') {
|
|
614
880
|
const component = is_inside_component(context, true);
|
|
@@ -673,7 +939,7 @@ const visitors = {
|
|
|
673
939
|
|
|
674
940
|
if (propertyName && internalProperties.has(propertyName)) {
|
|
675
941
|
error(
|
|
676
|
-
`Directly accessing internal property "${propertyName}" of a tracked object is not allowed. Use
|
|
942
|
+
`Directly accessing internal property "${propertyName}" of a tracked object is not allowed. Use \`${node.object.name}.value\` or \`&[]\` lazy destructuring instead.`,
|
|
677
943
|
context.state.analysis.module.filename,
|
|
678
944
|
node.property,
|
|
679
945
|
context.state.loose ? context.state.analysis.errors : undefined,
|
|
@@ -684,6 +950,8 @@ const visitors = {
|
|
|
684
950
|
|
|
685
951
|
if (
|
|
686
952
|
binding !== null &&
|
|
953
|
+
binding.kind !== 'lazy' &&
|
|
954
|
+
binding.kind !== 'lazy_fallback' &&
|
|
687
955
|
binding.initial?.type === 'CallExpression' &&
|
|
688
956
|
is_ripple_track_call(binding.initial.callee, context)
|
|
689
957
|
) {
|
|
@@ -701,7 +969,7 @@ const visitors = {
|
|
|
701
969
|
// pass through
|
|
702
970
|
} else {
|
|
703
971
|
error(
|
|
704
|
-
`Accessing a tracked object directly is not allowed, use
|
|
972
|
+
`Accessing a tracked object directly is not allowed, use \`.value\` or \`&[]\` lazy destructuring to read the value inside a tracked object - for example \`${node.object.name}.value\``,
|
|
705
973
|
context.state.analysis.module.filename,
|
|
706
974
|
node.object,
|
|
707
975
|
context.state.loose ? context.state.analysis.errors : undefined,
|
|
@@ -726,6 +994,19 @@ const visitors = {
|
|
|
726
994
|
|
|
727
995
|
const callee = node.callee;
|
|
728
996
|
|
|
997
|
+
if (
|
|
998
|
+
!context.path.some((path_node) => path_node.type === 'TsxCompat') &&
|
|
999
|
+
is_children_template_expression(/** @type {AST.Expression} */ (callee), context)
|
|
1000
|
+
) {
|
|
1001
|
+
error(
|
|
1002
|
+
'`children` cannot be called like a regular function. Use element syntax instead, e.g. `<children />` or `<props.children />`.',
|
|
1003
|
+
context.state.analysis.module.filename,
|
|
1004
|
+
callee,
|
|
1005
|
+
context.state.loose ? context.state.analysis.errors : undefined,
|
|
1006
|
+
context.state.analysis.comments,
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
729
1010
|
if (context.state.function_depth === 0 && is_ripple_track_call(callee, context)) {
|
|
730
1011
|
error(
|
|
731
1012
|
'`track` can only be used within a reactive context, such as a component, function or class that is used or created from a component',
|
|
@@ -794,20 +1075,6 @@ const visitors = {
|
|
|
794
1075
|
declarator.id.metadata = { ...declarator.id.metadata, lazy_id: lazy_id.name };
|
|
795
1076
|
}
|
|
796
1077
|
|
|
797
|
-
const paths = extract_paths(declarator.id);
|
|
798
|
-
|
|
799
|
-
for (const path of paths) {
|
|
800
|
-
if (path.node.tracked) {
|
|
801
|
-
error(
|
|
802
|
-
'Variables cannot be reactively referenced using @',
|
|
803
|
-
state.analysis.module.filename,
|
|
804
|
-
path.node,
|
|
805
|
-
context.state.loose ? context.state.analysis.errors : undefined,
|
|
806
|
-
context.state.analysis.comments,
|
|
807
|
-
);
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
|
|
811
1078
|
visit(declarator, state);
|
|
812
1079
|
}
|
|
813
1080
|
|
|
@@ -815,6 +1082,30 @@ const visitors = {
|
|
|
815
1082
|
}
|
|
816
1083
|
},
|
|
817
1084
|
|
|
1085
|
+
ExpressionStatement(node, context) {
|
|
1086
|
+
const { state, visit } = context;
|
|
1087
|
+
|
|
1088
|
+
// Handle standalone lazy destructuring assignment: &[data] = track(0);
|
|
1089
|
+
if (
|
|
1090
|
+
node.expression.type === 'AssignmentExpression' &&
|
|
1091
|
+
node.expression.operator === '=' &&
|
|
1092
|
+
(node.expression.left.type === 'ObjectPattern' ||
|
|
1093
|
+
node.expression.left.type === 'ArrayPattern') &&
|
|
1094
|
+
node.expression.left.lazy
|
|
1095
|
+
) {
|
|
1096
|
+
const pattern = /** @type {AST.ObjectPattern | AST.ArrayPattern} */ (node.expression.left);
|
|
1097
|
+
const lazy_id = b.id(state.scope.generate('lazy'));
|
|
1098
|
+
const init = /** @type {AST.Expression} */ (node.expression.right);
|
|
1099
|
+
const init_is_track =
|
|
1100
|
+
init?.type === 'CallExpression' && is_ripple_track_call(init.callee, context) === 'track';
|
|
1101
|
+
setup_lazy_transforms(pattern, lazy_id, state, true, !!init_is_track);
|
|
1102
|
+
// Store the generated identifier name on the pattern for the transform phase
|
|
1103
|
+
pattern.metadata = { ...pattern.metadata, lazy_id: lazy_id.name };
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
context.next();
|
|
1107
|
+
},
|
|
1108
|
+
|
|
818
1109
|
StyleIdentifier(node, context) {
|
|
819
1110
|
const component = is_inside_component(context, true);
|
|
820
1111
|
const parent = context.path.at(-1);
|
|
@@ -868,9 +1159,13 @@ const visitors = {
|
|
|
868
1159
|
if (node.params.length > 0) {
|
|
869
1160
|
const props = node.params[0];
|
|
870
1161
|
|
|
871
|
-
if (
|
|
1162
|
+
if (props.type === 'ObjectPattern' || props.type === 'ArrayPattern') {
|
|
872
1163
|
// Lazy destructuring: &{...} or &[...] — set up lazy transforms
|
|
873
|
-
|
|
1164
|
+
if (props.lazy) {
|
|
1165
|
+
setup_lazy_transforms(props, b.id('__props'), context.state, true, false);
|
|
1166
|
+
} else {
|
|
1167
|
+
setup_nested_lazy_param_transforms(props, context, get_pattern_type_annotation(props));
|
|
1168
|
+
}
|
|
874
1169
|
} else if (props.type === 'AssignmentPattern') {
|
|
875
1170
|
error(
|
|
876
1171
|
'Props are always an object, use destructured props with default values instead',
|
|
@@ -1418,6 +1713,7 @@ const visitors = {
|
|
|
1418
1713
|
|
|
1419
1714
|
const { state, visit, path } = context;
|
|
1420
1715
|
const is_dom_element = is_element_dom_element(node);
|
|
1716
|
+
/** @type {Set<AST.Identifier>} */
|
|
1421
1717
|
const attribute_names = new Set();
|
|
1422
1718
|
|
|
1423
1719
|
mark_control_flow_has_template(path);
|
|
@@ -1673,12 +1969,7 @@ const visitors = {
|
|
|
1673
1969
|
Text(node, context) {
|
|
1674
1970
|
mark_control_flow_has_template(context.path);
|
|
1675
1971
|
|
|
1676
|
-
if (
|
|
1677
|
-
is_children_template_expression(
|
|
1678
|
-
/** @type {AST.Expression} */ (node.expression),
|
|
1679
|
-
context.state,
|
|
1680
|
-
)
|
|
1681
|
-
) {
|
|
1972
|
+
if (is_children_template_expression(/** @type {AST.Expression} */ (node.expression), context)) {
|
|
1682
1973
|
error(
|
|
1683
1974
|
'`children` cannot be rendered using text interpolation. Use `<children />` instead.',
|
|
1684
1975
|
context.state.analysis.module.filename,
|