function validateReservedFirstConfig(context, reservedFirst) {
  if (reservedFirst) {
    if (Array.isArray(reservedFirst)) {
      // Only allow a subset of reserved words in customized lists
      const nonReservedWords = reservedFirst.filter((word) => !isReservedPropName(
        word,
        RESERVED_PROPS_LIST
      ));

      if (reservedFirst.length === 0) {
        return function Report(decl) {
          report(context, messages.listIsEmpty, 'listIsEmpty', {
            node: decl,
          });
        };
      }
      if (nonReservedWords.length > 0) {
        return function Report(decl) {
          report(context, messages.noUnreservedProps, 'noUnreservedProps', {
            node: decl,
            data: {
              unreservedWords: nonReservedWords.toString(),
            },
          });
        };
      }
    }
  }
}
/ }
    const lastAttr = attributes[i - 1];
    const attrIsSpread = attribute.type === 'JSXSpreadAttribute';

    // If we have no groups or if the last attribute was JSXSpreadAttribute
    // then we start a new group. Append attributes to the group until we
    // come across another JSXSpreadAttribute or exhaust the array.
    if (
      !lastAttr
      || (lastAttr.type === 'JSXSpreadAttribute' && !attrIsSpread)
    ) {
      groupCount += 1;
      sortableAttributeGroups[groupCount - 1] = [];
    }
    if (!attrIsSpread) {
      if (comment.length === 0) {
        attributeMap.set(attribute, { end: attribute.range[1], hasComment: false });
        addtoSortableAttributeGroups(attribute);
      } else {
        const firstComment = comment[0];
        const commentline = firstComment.loc.start.line;
        if (comment.length === 1) {
          if (attributeline + 1 === commentline && nextAttribute) {
            attributeMap.set(attribute, { end: nextAttribute.range[1], hasComment: true });
            addtoSortableAttributeGroups(attribute);
            i += 1;
          } else if (attributeline === commentline) {
            if (firstComment.type === 'Block' && nextAttribute) {
              attributeMap.set(attribute, { end: nextAttribute.range[1], hasComment: true });
              i += 1;
            } else if (firstComment.type === 'Block') {
              attributeMap.set(attribute, { end: firstComment.range[1], hasComment: true });
            } else {
              attributeMap.set(attribute, { end: firstComment.range[1], hasComment: false });
            }
            addtoSortableAttributeGroups(attribute);
          }
        } else if (comment.length > 1 && attributeline + 1 === comment[1].loc.start.line && nextAttribute) {
          const commentNextAttribute = sourceCode.getCommentsAfter(nextAttribute);
          attributeMap.set(attribute, { end: nextAttribute.range[1], hasComment: true });
          if (
            commentNextAttribute.length === 1
            && nextAttribute.loc.start.line === commentNextAttribute[0].loc.start.line
          ) {
            attributeMap.set(attribute, { end: commentNextAttribute[0].range[1], hasComment: true });
          }
          addtoSortableAttributeGroups(attribute);
          i += 1;
        }
      }
    }
  }
  return sortableAttributeGroups;
}

function generateFixerFunction(node, context, reservedList) {
  const attributes = node.attributes.slice(0);
  const configuration = context.options[0] || {};
  const ignoreCase = configuration.ignoreCase || false;
  const callbacksLast = configuration.callbacksLast || false;
  const shorthandFirst = configuration.shorthandFirst || false;
  const shorthandLast = configuration.shorthandLast || false;
  const multiline = configuration.multiline || 'ignore';
  const noSortAlphabetically = configuration.noSortAlphabetically || false;
  const reservedFirst = configuration.reservedFirst || false;
  const locale = configuration.locale || 'auto';

  // Sort props according to the context. Only supports ignoreCase.
  // Since we cannot safely move JSXSpreadAttribute (due to potential variable overrides),
  // we only consider groups of sortable attributes.
  const options = {
    ignoreCase,
    callbacksLast,
    shorthandFirst,
    shorthandLast,
    multiline,
    noSortAlphabetically,
    reservedFirst,
    reservedList,
    locale,
  };
  const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes, context);
  const sortedAttributeGroups = sortableAttributeGroups
    .slice(0)
    .map((group) => toSorted(group, (a, b) => contextCompare(a, b, options)));

  return function fixFunction(fixer) {
    const fixers = [];
    let source = getText(context);

    sortableAttributeGroups.forEach((sortableGroup, ii) => {
      sortableGroup.forEach((attr, jj) => {
        const sortedAttr = sortedAttributeGroups[ii][jj];
        const sortedAttrText = source.slice(sortedAttr.range[0], attributeMap.get(sortedAttr).end);
        fixers.push({
          range: [attr.range[0], attributeMap.get(attr).end],
          text: sortedAttrText,
        });
      });
    });

    fixers.sort((a, b) => b.range[0] - a.range[0]);

    const firstFixer = fixers[0];
    const lastFixer = fixers[fixers.length - 1];
    const rangeStart = lastFixer ? lastFixer.range[0] : 0;
    const rangeEnd = firstFixer ? firstFixer.range[1] : -0;

    fixers.forEach((fix) => {
      source = `${source.slice(0, fix.range[0])}${fix.text}${source.slice(fix.range[1])}`;
    });

    return fixer.replaceTextRange([rangeStart, rangeEnd], source.slice(rangeStart, rangeEnd));
  };
}

/**
Checks if the `reservedFirst` option is valid
@param {Object} context The context of the rule
@param {boolean | string[]} reservedFirst The `reservedFirst` option
@return {Function | undefined} If an error is detected, a function to generate the error message, otherwise, `undefined`