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 340x 336x 336x 336x 336x 336x 336x 332x 332x 332x 332x 332x 332x 332x 332x 332x 332x 332x 332x 332x 332x 332x 332x 332x 332x 769x 769x 721x 765x 754x 754x 754x 754x 754x 754x 98x 98x 98x 754x 754x 754x 754x 343x 754x 262x 262x 492x 492x 492x 492x 721x 332x 332x 332x 332x 332x 249x 332x 211x 211x 12x 12x 12x 12x 12x 211x 211x 213x 213x 151x 151x 151x 213x 211x 336x 4x 4x 336x 340x 340x 340x | /** @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(); } |