function getRemovalParent()

in tools/hermes-parser/js/hermes-transform/src/transform/mutations/RemoveNode.js [77:259]


function getRemovalParent(node: RemoveNodeMutation['node']): $ReadOnly<{
  type: 'array',
  parent: ESNode,
  key: string,
  targetIndex: number,
}> {
  const key = ((): string => {
    function getErrorMessage(expectedParents: $ReadOnlyArray<string>): string {
      return `Tried to remove ${node.type} from parent of type ${
        node.parent.type
      }.\nHowever ${
        node.type
      } can only be safely removed from parent of type ${expectedParents.join(
        ' | ',
      )}.`;
    }
    function assertParent(expectedParents_: string | $ReadOnlyArray<string>) {
      const expectedParents =
        typeof expectedParents_ === 'string'
          ? [expectedParents_]
          : expectedParents_;
      if (!expectedParents.includes(node.parent.type)) {
        return new InvalidRemovalError(getErrorMessage(expectedParents));
      }
    }

    switch (node.type) {
      // ClassMember
      case 'ClassProperty':
      case 'ClassPrivateProperty':
      case 'MethodDefinition':
        assertParent('ClassBody');
        return 'body';

      case 'EnumBooleanMember':
      case 'EnumDefaultedMember':
      case 'EnumNumberMember':
      case 'EnumStringMember':
        assertParent(VALID_ENUM_MEMBER_PARENTS);
        return 'members';

      // FunctionParameter
      case 'AssignmentPattern':
      case 'ArrayPattern':
      case 'ObjectPattern':
        assertParent(VALID_FUNCTION_PARAMETER_PARENTS);
        return 'params';

      case 'FunctionTypeParam':
        assertParent('FunctionTypeAnnotation');
        return 'params';

      case 'ObjectTypeCallProperty':
        assertParent('ObjectTypeAnnotation');
        return 'callProperties';

      case 'ObjectTypeIndexer':
        assertParent('ObjectTypeAnnotation');
        return 'indexers';

      case 'ObjectTypeInternalSlot':
        assertParent('ObjectTypeAnnotation');
        return 'internalSlots';

      case 'ObjectTypeProperty':
      case 'ObjectTypeSpreadProperty':
        assertParent('ObjectTypeAnnotation');
        return 'properties';

      case 'Property':
        assertParent(VALID_PROPERTY_PARENTS);
        return 'properties';

      // Identifier can be the child of a number of usecases
      case 'Identifier':
        switch (node.parent.type) {
          case 'ArrowFunctionExpression':
          case 'FunctionDeclaration':
          case 'FunctionExpression':
            return 'params';

          case 'ArrayExpression':
          case 'ArrayPattern':
            return 'elements';

          default:
            throw new InvalidRemovalError(
              getErrorMessage([
                'ArrowFunctionExpression',
                'FunctionDeclaration',
                'FunctionExpression',
                'ArrayExpression',
                'ArrayPattern',
              ]),
            );
        }

      // RestElement can be the child of a number of usecases
      case 'RestElement':
        switch (node.parent.type) {
          case 'ArrowFunctionExpression':
          case 'FunctionDeclaration':
          case 'FunctionExpression':
            return 'params';

          case 'ArrayPattern':
            return 'elements';

          case 'ObjectPattern':
            return 'properties';

          case 'CallExpression':
          case 'OptionalCallExpression':
          case 'NewExpression':
            return 'arguments';

          default:
            throw new InvalidRemovalError(
              getErrorMessage([
                'ArrowFunctionExpression',
                'FunctionDeclaration',
                'FunctionExpression',
                'ArrayPattern',
                'ObjectPattern',
                'CallExpression',
                'OptionalCallExpression',
                'NewExpression',
              ]),
            );
        }

      // SpreadElement can be the child of a number of usecases
      case 'SpreadElement':
        switch (node.parent.type) {
          case 'ArrayExpression':
            return 'elements';

          case 'ObjectExpression':
            return 'properties';

          case 'CallExpression':
          case 'OptionalCallExpression':
          case 'NewExpression':
            return 'arguments';

          default:
            throw new InvalidRemovalError(
              getErrorMessage([
                'ArrayExpression',
                'ObjectExpression',
                'CallExpression',
                'OptionalCallExpression',
                'NewExpression',
              ]),
            );
        }

      default:
        throw new InvalidRemovalError(
          `Cannot perform a remove mutation on node of type ${node.type}`,
        );
    }
  })();

  const targetIndex = (() => {
    // $FlowExpectedError[prop-missing]
    const arr = node.parent[key];
    const idx = arr.indexOf(node);
    if (idx === -1) {
      throw new InvalidRemovalError(
        `Could not find target in array of \`${parent.type}.${key}\`.`,
      );
    }
    return idx;
  })();

  return {
    type: 'array',
    parent: node.parent,
    key,
    targetIndex,
  };
}