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 346x 342x 342x 342x 342x 342x 342x 338x 338x 338x 338x 338x 338x 338x 338x 338x 338x 338x 338x 338x 338x 338x 338x 338x 338x 779x 779x 729x 775x 762x 762x 762x 762x 762x 762x 100x 100x 100x 762x 762x 762x 762x 347x 762x 265x 265x 497x 497x 497x 497x 729x 338x 338x 338x 338x 338x 254x 338x 214x 214x 12x 12x 12x 12x 12x 214x 214x 216x 216x 154x 154x 154x 216x 214x 342x 4x 4x 342x 346x 346x 346x | /** @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(); } |