Repository: babel/babel
File: packages/babel-plugin-transform-nullish-coalescing-operator/src/index.ts (TypeScript)

Code before:

  return {
    name: "transform-nullish-coalescing-operator",
    inherits:
      USE_ESM || IS_STANDALONE || api.version[0] === "8"
        ? undefined
        : // eslint-disable-next-line no-restricted-globals
          require("@babel/plugin-syntax-nullish-coalescing-operator").default,

    visitor: {
      LogicalExpression(path) {
        const { node, scope } = path;
        if (node.operator !== "??") {
          return;
        }

        let pureMember = false;
        if (pureGetters) {
          let obj: t.Expression = node.left;
          while (t.isMemberExpression(obj)) {
            if (obj.computed) break;
            obj = obj.object;
          }
          if (t.isIdentifier(obj)) {
            pureMember = true;
          }
        }

        let ref;
        let assignment;
        // skip creating extra reference when `left` is pure
        if (
          pureMember ||
          // globalThis
          t.isIdentifier(node.left) ||
          scope.isPure(node.left)
        ) {
          ref = node.left;
          assignment = t.cloneNode(node.left);
        } else if (scope.path.isPattern()) {
          // Replace `function (a, x = a.b ?? c) {}` to `function (a, x = (() => a.b ?? c)() ){}`
          // so the temporary variable can be injected in correct scope
          path.replaceWith(template.statement.ast`(() => ${path.node})()`);
          // The injected nullish expression will be queued and eventually transformed when visited
          return;
        } else {
          ref = scope.generateUidIdentifierBasedOnNode(node.left);
          scope.push({ id: t.cloneNode(ref) });
          assignment = t.assignmentExpression("=", ref, node.left);
        }


Review comment (suggestion):
I would also put a limit of "at most `identifier.identifier`", since for cases like `some.object.with.a?.property` it's better to introduce the temp var than to emit `some.object.with.a == null ? null : some.object.with.a.property`

Code after:

  return {
    name: "transform-nullish-coalescing-operator",
    manipulateOptions: process.env.BABEL_8_BREAKING
      ? undefined
      : (_, parser) => parser.plugins.push("nullishCoalescingOperator"),

    visitor: {
      LogicalExpression(path) {
        const { node, scope } = path;
        if (node.operator !== "??") {
          return;
        }

        let ref;
        let assignment;
        // skip creating extra reference when `left` is pure
        if (
          (pureGetters &&
            scope.path.isPattern() &&
            t.isMemberExpression(node.left) &&
            !node.left.computed &&
            t.isIdentifier(node.left.object) &&
            t.isIdentifier(node.left.property)) ||
          (t.isIdentifier(node.left) &&
            (pureGetters ||
              // globalThis
              scope.hasBinding(node.left.name))) ||
          scope.isPure(node.left)
        ) {
          ref = node.left;
          assignment = t.cloneNode(node.left);
        } else if (scope.path.isPattern()) {
          // Replace `function (a, x = a.b ?? c) {}` to `function (a, x = (() => a.b ?? c)() ){}`
          // so the temporary variable can be injected in correct scope
          path.replaceWith(template.statement.ast`(() => ${path.node})()`);
          // The injected nullish expression will be queued and eventually transformed when visited
          return;
        } else {
          ref = scope.generateUidIdentifierBasedOnNode(node.left);
          scope.push({ id: t.cloneNode(ref) });
          assignment = t.assignmentExpression("=", ref, node.left);
        }

        path.replaceWith(
          t.conditionalExpression(
            // We cannot use `!= null` in spec mode because
            // `document.all == null` and `document.all` is not "nullish".
            noDocumentAll
              ? t.binaryExpression("!=", assignment, t.nullLiteral())
              : t.logicalExpression(