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 339x 335x 335x 335x 335x 335x 335x 331x 331x 331x 331x 331x 331x 331x 331x 331x 331x 331x 331x 331x 331x 331x 331x 331x 331x 768x 768x 720x 764x 753x 753x 753x 753x 753x 753x 98x 98x 98x 753x 753x 753x 753x 342x 753x 261x 261x 492x 492x 492x 492x 720x 331x 331x 331x 331x 331x 248x 331x 210x 210x 12x 12x 12x 12x 12x 210x 210x 212x 212x 151x 151x 151x 212x 210x 335x 4x 4x 335x 339x 339x 339x | /** @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(); } |