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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1089x 1089x 1089x 1089x 1089x 1089x 1031x 1089x 58x 58x 58x 58x 58x 58x 58x 108x 108x 108x 108x 108x 108x 58x 58x 58x 47x 47x 47x 11x 11x 11x 29x 3x 3x 3x 11x 29x 7x 7x 7x 4x 4x 4x 1031x 1089x 1089x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1139x 1139x 1139x 1139x 333x 333x 333x 1139x 1139x 1093x 1093x 46x 46x 46x 1113x 46x 1113x 38x 38x 38x 5x 5x 5x 5x 5x 5x 5x 38x 38x 38x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 2x 2x 2x 2x 1090x 1090x 1090x | /** @import { AssignmentExpression, AssignmentOperator, BinaryOperator, Expression, Node, Pattern } from 'estree' */ /** @import { SvelteNode } from '#compiler' */ /** @import { Context, ServerTransformState } from '../types.js' */ import * as b from '../../../../utils/builders.js'; import { extract_paths } from '../../../../utils/ast.js'; import { serialize_get_binding } from './shared/utils.js'; /** * @param {AssignmentExpression} node * @param {Context} context */ export function AssignmentExpression(node, context) { const parent = /** @type {Node} */ (context.path.at(-1)); const is_standalone = parent.type.endsWith('Statement'); if ( node.left.type === 'ArrayPattern' || node.left.type === 'ObjectPattern' || node.left.type === 'RestElement' ) { const value = /** @type {Expression} */ (context.visit(node.right)); const should_cache = value.type !== 'Identifier'; const rhs = should_cache ? b.id('$$value') : value; let changed = false; const assignments = extract_paths(node.left).map((path) => { const value = path.expression?.(rhs); let assignment = serialize_assignment('=', path.node, value, context); if (assignment !== null) changed = true; return assignment ?? b.assignment('=', path.node, value); }); if (!changed) { // No change to output -> nothing to transform -> we can keep the original assignment return context.next(); } const sequence = b.sequence(assignments); if (!is_standalone) { // this is part of an expression, we need the sequence to end with the value sequence.expressions.push(rhs); } if (should_cache) { // the right hand side is a complex expression, wrap in an IIFE to cache it return b.call(b.arrow([rhs], sequence), value); } return sequence; } return serialize_assignment(node.operator, node.left, node.right, context) || context.next(); } /** * Only returns an expression if this is not a `$store` assignment, as others can be kept as-is * @param {AssignmentOperator} operator * @param {Pattern} left * @param {Expression} right * @param {import('zimmerframe').Context<SvelteNode, ServerTransformState>} context * @returns {Expression | null} */ function serialize_assignment(operator, left, right, context) { let object = left; while (object.type === 'MemberExpression') { // @ts-expect-error object = object.object; } if (object.type !== 'Identifier' || !is_store_name(object.name)) { return null; } const name = object.name.slice(1); if (!context.state.scope.get(name)) { return null; } if (object === left) { let value = /** @type {Expression} */ (context.visit(right)); if (operator !== '=') { // turn `x += 1` into `x = x + 1` value = b.binary( /** @type {BinaryOperator} */ (operator.slice(0, -1)), serialize_get_binding(left, context.state), value ); } return b.call('$.store_set', b.id(name), value); } return b.call( '$.store_mutate', b.assignment('??=', b.id('$$store_subs'), b.object([])), b.literal(object.name), b.id(name), b.assignment( operator, /** @type {Pattern} */ (context.visit(left)), /** @type {Expression} */ (context.visit(right)) ) ); } /** * @param {string} name */ function is_store_name(name) { return name[0] === '$' && /[A-Za-z_]/.test(name[1]); } |