testParensOnlyWhereTheChainGoesOn

tcmts.core.migrate.testParensOnlyWhereTheChainGoesOn · #oxpodtnt · term

import { dropUncontinuedParens, upgradeIR } from "#codebase/tcmts/core/migrate";
import assert from "node:assert/strict";

export const testParensOnlyWhereTheChainGoesOn = () => {
  const chain = [
    "PropertyAccessExpression",
    ["expression", ["L", "0"]],
    ["questionDotToken", ["QuestionDotToken"]],
    ["name", ["N", "b"]],
  ];
  const paren = ["ParenthesizedExpression", ["expression", chain]];
  // `(a?.b) ?? x`: nothing goes on from it, the parentheses go
  assert.deepEqual(
    dropUncontinuedParens([
      "BinaryExpression",
      ["left", paren],
      ["operatorToken", ["QuestionQuestionToken"]],
      ["right", ["Str", "x"]],
    ]),
    [
      "BinaryExpression",
      ["left", chain],
      ["operatorToken", ["QuestionQuestionToken"]],
      ["right", ["Str", "x"]],
    ],
  );
  // `(a?.b).c` and `(a?.b)()`: the chain goes on, they stay
  const member = ["PropertyAccessExpression", ["expression", paren], ["name", ["N", "c"]]];
  assert.deepEqual(dropUncontinuedParens(member), member);
  const call = ["CallExpression", ["expression", paren], ["arguments", ["["]]];
  assert.deepEqual(dropUncontinuedParens(call), call);
  const tagged = ["TaggedTemplateExpression", ["tag", paren], ["template", ["Str", "t"]]];
  assert.deepEqual(dropUncontinuedParens(tagged), tagged);
  // but not when it's an argument: `f((a?.b))`
  assert.deepEqual(
    dropUncontinuedParens([
      "CallExpression",
      ["expression", ["G", "f"]],
      ["arguments", ["[", paren]],
    ]),
    ["CallExpression", ["expression", ["G", "f"]], ["arguments", ["[", chain]]],
  );
  assert.deepEqual(upgradeIR(paren, 1, 2), chain);
  assert.deepEqual(upgradeIR(paren, 2, 2), paren);
  assert.throws(() => upgradeIR(paren, 2, 3), /no way from hash format v2 to v3/);
}

uses