dropUncontinuedParens
Format 1 to 2: parentheses around an optional chain are kept only where the chain goes on after them. Format 1
kept all of them, so a formatter adding redundant ones ((a?.b())) changed a hash. Format 1 dropped every other
parenthesis already, so each one left in its IR is around an optional chain.
import type { IR } from "#codebase/tcmts/core/hash"; import { CONTINUES_CHAIN } from "#codebase/tcmts/core/migrate"; export function dropUncontinuedParens(ir: IR): IR { const visit = (node: IR, continued: boolean): IR => { if (!Array.isArray(node)) return node; if (node[0] === "ParenthesizedExpression" && !continued) { const inner = ( node.find((c) => Array.isArray(c) && c[0] === "expression") as IR[] | undefined )?.[1]; if (inner !== undefined) return visit(inner, false); } const tag = node[0]; return node.map((c) => { // the slot a node goes on from: its `expression` (a tagged template's `tag`) const from = tag === "TaggedTemplateExpression" ? "tag" : "expression"; if (Array.isArray(c) && c[0] === from && typeof tag === "string" && CONTINUES_CHAIN.has(tag)) return [from, visit(c[1] as IR, true)]; return visit(c, false); }); }; return visit(ir, false); }