//@ts-check import lodash from 'lodash'; const { camelCase } = lodash; /** * Rule IDs that are overly verbose and that we want to combine so they report more cleanly * * i.e. so we can report `reactHooksRulesOfHooks` instead of `reactHookFooIsCalledConditionallyReactHooksMust...` */ const rulesToCombine = ['react-hooks/rules-of-hooks', 'react/no-unescaped-entities', 'no-barrel-files/no-barrel-files']; const legacyChecksToTransform = [ { messageRegex: /gfFormUsage/i, prefix: 'noGfFormUsage' }, { messageRegex: /skippingA11Y/i, prefix: 'noSkippingA11YTestsInStories' }, ]; /** * Custom formatter that outputs suppressed rule violations in a format suitable for * consuming on our CI code stats scripts * * Output in the format: * @example * betterEslint_reactHooksRulesOfHooks 123 * betterEslint_noBarrelFilesNoBarrelFiles 123 * * @type {import('eslint').ESLint.FormatterFunction} */ export default function statsReporter(results) { /** @type {Record} */ const countByMessage = {}; for (const result of results) { for (const message of result.suppressedMessages) { // eslint disable directives count as suppressions // we only want to report the case where everything is a file suppression const everySuppressionIsFile = message.suppressions.every((suppression) => suppression.kind === 'file'); if (!everySuppressionIsFile) { continue; } const key = message.ruleId && rulesToCombine.includes(message.ruleId) ? camelCase(message.ruleId) : camelCase(message.message); countByMessage[key] = (countByMessage[key] || 0) + 1; } } return Object.entries(countByMessage) .map(([key, value]) => { const prefix = legacyChecksToTransform.find((v) => v.messageRegex.test(key))?.prefix || 'betterEslint'; return `${prefix}_${key} ${value}`; }) .join('\n'); }