UNPKG

@antebudimir/eslint-plugin-vanilla-extract

Version:

ESLint plugin for enforcing best practices in vanilla-extract CSS styles, including CSS property ordering and additional linting rules.

72 lines (71 loc) 4.08 kB
import { generateFixesForCSSOrder } from '../shared-utils/css-order-fixer.js'; import { createCSSPropertyPriorityMap } from '../shared-utils/css-property-priority-map.js'; /** * Enforces a custom ordering of CSS properties based on user-defined groups. * * @param ruleContext The ESLint rule context used for reporting and fixing. * @param cssPropertyInfoList An array of CSS property information objects to be ordered. * @param userDefinedGroups Array of user-defined property groups for custom ordering. * @param sortRemainingProperties Strategy for sorting properties not in user-defined groups * ('alphabetical' or 'concentric'). Defaults to 'concentric'. * * This function compares CSS properties based on their group priority and position within * those groups. Properties not part of user-defined groups are sorted according to the * specified strategy. If an ordering violation is detected, an ESLint report is generated * with a suggested fix. */ export const enforceCustomGroupOrder = (ruleContext, cssPropertyInfoList, userDefinedGroups = [], sortRemainingProperties) => { if (cssPropertyInfoList.length > 1) { const cssPropertyPriorityMap = createCSSPropertyPriorityMap(userDefinedGroups); const compareProperties = (firstProperty, secondProperty) => { const firstPropertyInfo = cssPropertyPriorityMap.get(firstProperty.name) || { groupIndex: Infinity, positionInGroup: Infinity, inUserGroup: false, }; const secondPropertyInfo = cssPropertyPriorityMap.get(secondProperty.name) || { groupIndex: Infinity, positionInGroup: Infinity, inUserGroup: false, }; if (firstPropertyInfo.inUserGroup !== secondPropertyInfo.inUserGroup) { return firstPropertyInfo.inUserGroup ? -1 : 1; } if (firstPropertyInfo.inUserGroup) { if (firstPropertyInfo.groupIndex !== secondPropertyInfo.groupIndex) { return firstPropertyInfo.groupIndex - secondPropertyInfo.groupIndex; } return firstPropertyInfo.positionInGroup - secondPropertyInfo.positionInGroup; } // For properties not in user-defined groups if (sortRemainingProperties === 'alphabetical') { return firstProperty.name.localeCompare(secondProperty.name); } else { return (firstPropertyInfo.groupIndex - secondPropertyInfo.groupIndex || firstPropertyInfo.positionInGroup - secondPropertyInfo.positionInGroup); } }; const sortedPropertyList = [...cssPropertyInfoList].sort(compareProperties); // Find the first pair that violates the new ordering const violatingProperty = cssPropertyInfoList .slice(0, -1) .find((currentProperty, index) => currentProperty.name !== sortedPropertyList[index]?.name); if (violatingProperty) { const indexInSorted = cssPropertyInfoList.indexOf(violatingProperty); const sortedProperty = sortedPropertyList[indexInSorted]; // Defensive programming - sortedProperty will always exist and have a name because sortedPropertyList is derived from cssPropertyInfoList and cssPropertyInfoList exists and is non-empty. // This fallback is theoretically unreachable in practice but included for type safety. const nextPropertyName = sortedProperty?.name ?? ''; ruleContext.report({ node: violatingProperty.node, messageId: 'incorrectOrder', data: { currentProperty: violatingProperty.name, nextProperty: nextPropertyName, }, fix: (fixer) => generateFixesForCSSOrder(fixer, ruleContext, cssPropertyInfoList, compareProperties, (propertyInfo) => propertyInfo.node), }); } } };