function fixPropTypesSort(
  context,
  fixer,
  declarations,
  ignoreCase,
  requiredFirst,
  callbacksLast,
  noSortAlphabetically,
  sortShapeProp,
  checkTypes
) {
  function sortInSource(allNodes, source) {
    const originalSource = source;
    const sourceCode = getSourceCode(context);
    for (let i = 0; i < allNodes.length; i++) {
      const node = allNodes[i];
      let commentAfter = [];
      let commentBefore = [];
      let newStart = 0;
      let newEnd = 0;
      try {
        commentBefore = sourceCode.getCommentsBefore(node);
        commentAfter = sourceCode.getCommentsAfter(node);
      } catch (e) { /**/ }

      if (commentAfter.length === 0 || commentBefore.length === 0) {
        newStart = node.range[0];
        newEnd = node.range[1];
      }

      const firstCommentBefore = commentBefore[0];
      if (commentBefore.length >= 1) {
        newStart = firstCommentBefore.range[0];
      }
      const lastCommentAfter = commentAfter[commentAfter.length - 1];
      if (commentAfter.length >= 1) {
        newEnd = lastCommentAfter.range[1];
      }
      commentnodeMap.set(node, { start: newStart, end: newEnd, hasComment: true });
    }
    const nodeGroups = allNodes.reduce((acc, curr) => {
      if (curr.type === 'ExperimentalSpreadProperty' || curr.type === 'SpreadElement') {
        acc.push([]);
      } else {
        acc[acc.length - 1].push(curr);
      }
      return acc;
    }, [[]]);

    nodeGroups.forEach((nodes) => {
      const sortedAttributes = toSorted(
        nodes,
        (a, b) => sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast, noSortAlphabetically)
      );

      const sourceCodeText = getText(context);
      let separator = '';
      source = nodes.reduceRight((acc, attr, index) => {
        const sortedAttr = sortedAttributes[index];
        const commentNode = commentnodeMap.get(sortedAttr);
        let sortedAttrText = sourceCodeText.slice(commentNode.start, commentNode.end);
        const sortedAttrTextLastChar = sortedAttrText[sortedAttrText.length - 1];
        if (!separator && [';', ','].some((allowedSep) => sortedAttrTextLastChar === allowedSep)) {
          separator = sortedAttrTextLastChar;
        }
        if (sortShapeProp && isShapeProp(sortedAttr.value)) {
          const shape = getShapeProperties(sortedAttr.value);
          if (shape) {
            const attrSource = sortInSource(
              shape,
              originalSource
            );
            sortedAttrText = attrSource.slice(sortedAttr.range[0], sortedAttr.range[1]);
          }
        }
        const sortedAttrTextVal = checkTypes && !sortedAttrText.endsWith(separator) ? `${sortedAttrText}${separator}` : sortedAttrText;
        return `${acc.slice(0, commentnodeMap.get(attr).start)}${sortedAttrTextVal}${acc.slice(commentnodeMap.get(attr).end)}`;
      }, source);
    });
    return source;
  }

  const source = sortInSource(declarations, getText(context));

  const rangeStart = commentnodeMap.get(declarations[0]).start;
  const rangeEnd = commentnodeMap.get(declarations[declarations.length - 1]).end;
  return fixer.replaceTextRange([rangeStart, rangeEnd], source.slice(rangeStart, rangeEnd));
}
Fixes sort order of prop types.

@param {Context} context the second element to compare.
@param {Fixer} fixer the first element to compare.
@param {Array} declarations The context of the two nodes.
@param {boolean=} ignoreCase whether or not to ignore case when comparing the two elements.
@param {boolean=} requiredFirst whether or not to sort required elements first.
@param {boolean=} callbacksLast whether or not to sort callbacks after everything else.
@param {boolean=} noSortAlphabetically whether or not to disable alphabetical sorting of the elements.
@param {boolean=} sortShapeProp whether or not to sort propTypes defined in PropTypes.shape.
@param {boolean=} checkTypes whether or not sorting of prop type definitions are checked.
@returns {Object|*|{range, text}} the sort order of the two elements.