@code-pushup/eslint-plugin
Version:
Code PushUp plugin for detecting problems in source code using ESLint.📋
112 lines • 4.44 kB
JavaScript
import { objectToKeys, slugify, ui } from '@code-pushup/utils';
import { ruleToSlug } from './hash.js';
import { parseRuleId } from './parse.js';
import { expandWildcardRules } from './rules.js';
// docs on meta.type: https://eslint.org/docs/latest/extend/custom-rules#rule-structure
const typeGroups = {
problem: {
slug: 'problems',
title: 'Problems',
description: 'Code that either will cause an error or may cause confusing behavior. Developers should consider this a high priority to resolve.',
},
suggestion: {
slug: 'suggestions',
title: 'Suggestions',
description: "Something that could be done in a better way but no errors will occur if the code isn't changed.",
},
layout: {
slug: 'formatting',
title: 'Formatting',
description: 'Primarily about whitespace, semicolons, commas, and parentheses, all the parts of the program that determine how the code looks rather than how it executes.',
},
};
export function groupsFromRuleTypes(rules) {
const allTypes = objectToKeys(typeGroups);
const auditSlugsMap = rules.reduce((acc, rule) => rule.meta.type == null
? acc
: {
...acc,
[rule.meta.type]: [
...(acc[rule.meta.type] ?? []),
ruleToSlug(rule),
],
}, {});
return allTypes
.map(type => ({
...typeGroups[type],
refs: auditSlugsMap[type]?.map((slug) => ({ slug, weight: 1 })) ??
[],
}))
.filter(group => group.refs.length);
}
export function groupsFromRuleCategories(rules) {
const categoriesMap = rules.reduce((acc, rule) => {
// meta.docs.category still used by some popular plugins (e.g. import, react, functional)
const category = rule.meta.docs?.category;
if (!category) {
return acc;
}
const { plugin = '' } = parseRuleId(rule.id);
return {
...acc,
[plugin]: {
...acc[plugin],
[category]: [...(acc[plugin]?.[category] ?? []), ruleToSlug(rule)],
},
};
}, {});
const groups = Object.entries(categoriesMap).flatMap(([plugin, categories]) => Object.entries(categories).map(([category, slugs]) => ({
slug: `${slugify(plugin)}-${slugify(category)}`,
title: `${category} (${plugin})`,
refs: slugs.map(slug => ({ slug, weight: 1 })),
})));
return groups.toSorted((a, b) => a.slug.localeCompare(b.slug));
}
export function groupsFromCustomConfig(rules, groups) {
const rulesMap = createRulesMap(rules);
return groups.map(group => {
const groupRules = Array.isArray(group.rules)
? Object.fromEntries(group.rules.map(rule => [rule, 1]))
: group.rules;
const { refs, invalidRules } = resolveGroupRefs(groupRules, rulesMap);
if (invalidRules.length > 0 && Object.entries(groupRules).length > 0) {
if (refs.length === 0) {
throw new Error(`Invalid rule configuration in group ${group.slug}. All rules are invalid.`);
}
ui().logger.warning(`Some rules in group ${group.slug} are invalid: ${invalidRules.join(', ')}`);
}
return {
slug: group.slug,
title: group.title,
refs,
};
});
}
export function createRulesMap(rules) {
return rules.reduce((acc, rule) => ({
...acc,
[rule.id]: [...(acc[rule.id] || []), rule],
}), {});
}
export function resolveGroupRefs(groupRules, rulesMap) {
return Object.entries(groupRules).reduce((acc, [rule, weight]) => {
const matchedRuleIds = rule.endsWith('*')
? expandWildcardRules(rule, Object.keys(rulesMap))
: [rule];
const matchedRefs = matchedRuleIds.flatMap(ruleId => {
const matchingRules = rulesMap[ruleId] || [];
const weightPerRule = weight / matchingRules.length;
return matchingRules.map(ruleData => ({
slug: ruleToSlug(ruleData),
weight: weightPerRule,
}));
});
return {
refs: [...acc.refs, ...matchedRefs],
invalidRules: matchedRefs.length > 0
? acc.invalidRules
: [...acc.invalidRules, rule],
};
}, { refs: [], invalidRules: [] });
}
//# sourceMappingURL=groups.js.map