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 | 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2831x 2831x 2831x 2721x 2831x 110x 110x 110x 110x 110x 110x 110x 207x 207x 207x 207x 207x 207x 207x 87x 87x 87x 87x 87x 207x 110x 110x 110x 47x 47x 47x 63x 63x 63x 63x 110x 13x 13x 13x 63x 110x 53x 53x 53x 53x 53x 53x 53x 53x 53x 10x 10x 10x 2721x 2831x 2721x 2721x 2831x 1046x 2831x 2831x | /** @import { AssignmentExpression, AssignmentOperator, Expression, Node, Pattern } from 'estree' */ /** @import { Context as ClientContext } from '../client/types.js' */ /** @import { Context as ServerContext } from '../server/types.js' */ import { extract_paths, is_expression_async } from '../../../utils/ast.js'; import * as b from '../../../utils/builders.js'; /** * @template {ClientContext | ServerContext} Context * @param {AssignmentExpression} node * @param {Context} context * @param {(operator: AssignmentOperator, left: Pattern, right: Expression, context: Context) => Expression | null} build_assignment * @returns */ export function visit_assignment_expression(node, context, build_assignment) { 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 = build_assignment('=', path.node, value, context); if (assignment !== null) changed = true; return ( assignment ?? b.assignment( '=', /** @type {Pattern} */ (context.visit(path.node)), /** @type {Expression} */ (context.visit(value)) ) ); }); if (!changed) { // No change to output -> nothing to transform -> we can keep the original assignment return context.next(); } const is_standalone = /** @type {Node} */ (context.path.at(-1)).type.endsWith('Statement'); 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 const iife = b.arrow([rhs], sequence); const iife_is_async = is_expression_async(value) || assignments.some((assignment) => is_expression_async(assignment)); return iife_is_async ? b.await(b.call(b.async(iife), value)) : b.call(iife, value); } return sequence; } if (node.left.type !== 'Identifier' && node.left.type !== 'MemberExpression') { throw new Error(`Unexpected assignment type ${node.left.type}`); } return ( build_assignment(node.operator, node.left, node.right, context) ?? /** @type {Expression} */ (context.next()) ); } |