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 350x 346x 346x 346x 346x 346x 346x 342x 342x 342x 342x 342x 342x 342x 342x 342x 342x 342x 342x 342x 342x 342x 342x 342x 342x 790x 790x 738x 786x 771x 771x 771x 771x 771x 771x 100x 100x 100x 771x 771x 771x 771x 351x 771x 269x 269x 502x 502x 502x 502x 738x 342x 342x 342x 342x 342x 255x 342x 215x 215x 12x 12x 12x 12x 12x 215x 215x 217x 217x 155x 155x 155x 217x 215x 346x 4x 4x 346x 350x 350x 350x | /** @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(); } |