Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 353x 349x 349x 349x 349x 349x 349x 345x 345x 345x 345x 345x 345x 345x 345x 345x 345x 345x 345x 345x 345x 345x 345x 345x 345x 797x 797x 743x 793x 776x 776x 776x 776x 776x 776x 100x 100x 100x 776x 776x 776x 776x 354x 776x 272x 272x 504x 504x 504x 504x 743x 345x 345x 345x 345x 345x 258x 345x 218x 218x 12x 12x 12x 12x 12x 218x 218x 220x 220x 158x 158x 158x 220x 218x 349x 4x 4x 349x 353x 353x 353x | /** @import { Expression, LabeledStatement } from 'estree' */ /** @import { AST, ReactiveStatement, SvelteNode } from '#compiler' */ /** @import { Context } from '../types' */ import * as e from '../../../errors.js'; import { extract_identifiers, object } from '../../../utils/ast.js'; import * as w from '../../../warnings.js'; /** * @param {LabeledStatement} node * @param {Context} context */ export function LabeledStatement(node, context) { if (node.label.name === '$') { const parent = /** @type {SvelteNode} */ (context.path.at(-1)); const is_reactive_statement = context.state.ast_type === 'instance' && parent.type === 'Program'; if (is_reactive_statement) { if (context.state.analysis.runes) { e.legacy_reactive_statement_invalid(node); } // Find all dependencies of this `$: {...}` statement /** @type {ReactiveStatement} */ const reactive_statement = { assignments: new Set(), dependencies: [] }; context.next({ ...context.state, reactive_statement, function_depth: context.state.scope.function_depth + 1 }); // Every referenced binding becomes a dependency, unless it's on // the left-hand side of an `=` assignment for (const [name, nodes] of context.state.scope.references) { const binding = context.state.scope.get(name); if (binding === null) continue; for (const { node, path } of nodes) { /** @type {Expression} */ let left = node; let i = path.length - 1; let parent = /** @type {Expression} */ (path.at(i)); while (parent.type === 'MemberExpression') { left = parent; parent = /** @type {Expression} */ (path.at(--i)); } if ( parent.type === 'AssignmentExpression' && parent.operator === '=' && parent.left === left ) { continue; } reactive_statement.dependencies.push(binding); break; } } context.state.reactive_statements.set(node, reactive_statement); if ( node.body.type === 'ExpressionStatement' && node.body.expression.type === 'AssignmentExpression' ) { let ids = extract_identifiers(node.body.expression.left); if (node.body.expression.left.type === 'MemberExpression') { const id = object(node.body.expression.left); if (id !== null) { ids = [id]; } } for (const id of ids) { const binding = context.state.scope.get(id.name); if (binding?.kind === 'legacy_reactive') { // TODO does this include `let double; $: double = x * 2`? binding.legacy_dependencies = Array.from(reactive_statement.dependencies); } } } } else if (!context.state.analysis.runes) { w.reactive_declaration_invalid_placement(node); } } context.next(); } |