From 46751da51b11183f7294bd391bd82d5800ced18e Mon Sep 17 00:00:00 2001 From: Ante Budimir Date: Wed, 12 Mar 2025 06:06:40 +0200 Subject: [PATCH] =?UTF-8?q?refactor=20=E2=99=BB=EF=B8=8F:=20improve=20code?= =?UTF-8?q?=20quality=20and=20test=20coverage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix handling of missing groupOrder configuration - Refactor negative conditions to positive ones with optional chaining - Add comprehensive tests to achieve total coverage --- CHANGELOG.md | 8 +- README.md | 15 ++- package.json | 2 +- .../recipe-order-enforcer.ts | 14 +- .../style-object-processor.ts | 20 ++- .../property-order-enforcer.ts | 26 ++-- .../concentric-order/recipe-order-enforcer.ts | 14 +- .../style-object-processor.ts | 34 +++-- .../custom-order/__tests__/defaults.test.ts | 115 ++++++++++++++++ .../custom-order/property-order-enforcer.ts | 125 +++++++++--------- .../custom-order/recipe-order-enforcer.ts | 26 ++-- src/css-rules/custom-order/rule-definition.ts | 7 +- .../custom-order/style-object-processor.ts | 86 ++++++------ .../order-strategy-visitor-creator.ts | 2 +- src/index.ts | 2 +- 15 files changed, 310 insertions(+), 186 deletions(-) create mode 100644 src/css-rules/custom-order/__tests__/defaults.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index a19e464..de7bde6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.5.0] - 2025-03-12 + +- Fix handling of missing groupOrder configuration () +- Refactor negative conditions to positive ones with optional chaining +- Add comprehensive tests to achieve total coverage + ## [1.4.7] - 2025-03-10 -- Exclude test directories from published package () +- Exclude test directories from published package (5557409) ## [1.4.6] - 2025-03-10 diff --git a/README.md b/README.md index 6747f57..d60e9e2 100644 --- a/README.md +++ b/README.md @@ -139,11 +139,20 @@ export const myStyle = style({ ### vanilla-extract/custom-order -The `vanilla-extract/custom-order` rule allows you to enforce a custom ordering of CSS properties in your vanilla-extract styles. You can specify an array of property groups in the order you prefer, and the rule will ensure that properties within these groups are sorted according to their position in the concentric CSS model. All other groups that aren't included in the groups array will have their respective properties sorted after the last group in the array. You can choose to sort them either alphabetically or following the concentric CSS order (see list of concentric groups) by setting the `sortRemainingProperties` option to 'alphabetical' or 'concentric' respectively. If not set, `sortRemainingProperties` defaults to 'alphabetical'. +The `vanilla-extract/custom-order` rule enables you to enforce a custom ordering of CSS properties in your vanilla-extract styles. You can specify an array of property groups in your preferred order, and the rule will ensure that properties within these groups are sorted according to their position in the concentric CSS model. -To configure the rule, add it to your ESLint configuration file with your desired options. You can customize the `groups` array to include any number of available CSS property groups you want to enforce, but minimum of 1 is required. +Key features of this rule include: -Example usage: +1. Custom group ordering: Define your preferred order of CSS property groups. +2. Handling of unspecified groups: All groups not included in the custom array will have their properties sorted after the last specified group. +3. Flexible sorting options: You can choose to sort remaining properties either alphabetically or following the concentric CSS order by setting the `sortRemainingProperties` option to 'alphabetical' or 'concentric' respectively. + +Default behavior: + +- If not set, `sortRemainingProperties` defaults to 'alphabetical'. +- If no `groupOrder` is specified or an empty array is provided, the rule will default to sorting all properties alphabetically, and `sortRemainingProperties` will be ignored even if set. + +To configure the rule, add it to your ESLint configuration file with your desired options. You can customize the `groups` array to include any number of available CSS property groups you want to enforce, with a minimum of one group required. ```typescript // ❌ Incorrect (Unordered) diff --git a/package.json b/package.json index 911deb5..d5cd914 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@antebudimir/eslint-plugin-vanilla-extract", - "version": "1.4.7", + "version": "1.5.0", "description": "ESLint plugin for enforcing best practices in vanilla-extract CSS styles, including CSS property ordering and additional linting rules.", "author": "Ante Budimir", "license": "MIT", diff --git a/src/css-rules/alphabetical-order/recipe-order-enforcer.ts b/src/css-rules/alphabetical-order/recipe-order-enforcer.ts index 242e4f8..602d0c4 100644 --- a/src/css-rules/alphabetical-order/recipe-order-enforcer.ts +++ b/src/css-rules/alphabetical-order/recipe-order-enforcer.ts @@ -16,13 +16,11 @@ import { enforceAlphabeticalCSSOrderInStyleObject } from './style-object-process * 3. For each relevant property (e.g., 'base', 'variants'), it applies alphabetical ordering to the CSS properties. */ export const enforceAlphabeticalCSSOrderInRecipe = (node: TSESTree.CallExpression, context: Rule.RuleContext): void => { - if (!node.arguments[0] || node.arguments[0].type !== 'ObjectExpression') { - return; + if (node.arguments[0]?.type === 'ObjectExpression') { + const recipeObject = node.arguments[0]; + + processRecipeProperties(context, recipeObject, (context, object) => + processStyleNode(context, object, enforceAlphabeticalCSSOrderInStyleObject), + ); } - - const recipeObject = node.arguments[0]; - - processRecipeProperties(context, recipeObject, (context, object) => - processStyleNode(context, object, enforceAlphabeticalCSSOrderInStyleObject), - ); }; diff --git a/src/css-rules/alphabetical-order/style-object-processor.ts b/src/css-rules/alphabetical-order/style-object-processor.ts index a59e63e..280eccf 100644 --- a/src/css-rules/alphabetical-order/style-object-processor.ts +++ b/src/css-rules/alphabetical-order/style-object-processor.ts @@ -20,18 +20,16 @@ export const enforceAlphabeticalCSSOrderInStyleObject = ( ruleContext: Rule.RuleContext, styleObject: TSESTree.ObjectExpression, ): void => { - if (!styleObject || styleObject.type !== AST_NODE_TYPES.ObjectExpression) { - return; - } + if (styleObject?.type === AST_NODE_TYPES.ObjectExpression) { + if (isSelectorsObject(styleObject)) { + processNestedSelectors(ruleContext, styleObject, enforceAlphabeticalCSSOrderInStyleObject); + return; + } + + const { regularProperties } = separateProperties(styleObject.properties); + + enforceAlphabeticalCSSOrder(ruleContext, regularProperties); - if (isSelectorsObject(styleObject)) { processNestedSelectors(ruleContext, styleObject, enforceAlphabeticalCSSOrderInStyleObject); - return; } - - const { regularProperties } = separateProperties(styleObject.properties); - - enforceAlphabeticalCSSOrder(ruleContext, regularProperties); - - processNestedSelectors(ruleContext, styleObject, enforceAlphabeticalCSSOrderInStyleObject); }; diff --git a/src/css-rules/concentric-order/property-order-enforcer.ts b/src/css-rules/concentric-order/property-order-enforcer.ts index 374321d..c6a8aca 100644 --- a/src/css-rules/concentric-order/property-order-enforcer.ts +++ b/src/css-rules/concentric-order/property-order-enforcer.ts @@ -65,21 +65,19 @@ export const enforceConcentricCSSOrder = ( ruleContext: Rule.RuleContext, cssPropertyInfoList: CSSPropertyInfo[], ): void => { - if (cssPropertyInfoList.length <= 1) { - return; - } + if (cssPropertyInfoList.length > 1) { + // Create pairs of consecutive properties + const propertyPairs = cssPropertyInfoList.slice(0, -1).map((currentProperty, index) => ({ + currentProperty, + nextProperty: cssPropertyInfoList[index + 1] as CSSPropertyInfo, + })); - // Create pairs of consecutive properties - const propertyPairs = cssPropertyInfoList.slice(0, -1).map((currentProperty, index) => ({ - currentProperty, - nextProperty: cssPropertyInfoList[index + 1] as CSSPropertyInfo, - })); + const violatingPair = propertyPairs.find( + ({ currentProperty, nextProperty }) => compareProperties(currentProperty, nextProperty) > 0, + ); - const violatingPair = propertyPairs.find( - ({ currentProperty, nextProperty }) => compareProperties(currentProperty, nextProperty) > 0, - ); - - if (violatingPair) { - reportOrderingIssue(ruleContext, violatingPair.currentProperty, violatingPair.nextProperty, cssPropertyInfoList); + if (violatingPair) { + reportOrderingIssue(ruleContext, violatingPair.currentProperty, violatingPair.nextProperty, cssPropertyInfoList); + } } }; diff --git a/src/css-rules/concentric-order/recipe-order-enforcer.ts b/src/css-rules/concentric-order/recipe-order-enforcer.ts index 9d21ccd..b70df13 100644 --- a/src/css-rules/concentric-order/recipe-order-enforcer.ts +++ b/src/css-rules/concentric-order/recipe-order-enforcer.ts @@ -19,13 +19,11 @@ export const enforceConcentricCSSOrderInRecipe = ( ruleContext: Rule.RuleContext, callExpression: TSESTree.CallExpression, ): void => { - if (!callExpression.arguments[0] || callExpression.arguments[0].type !== 'ObjectExpression') { - return; + if (callExpression.arguments[0]?.type === 'ObjectExpression') { + const recipeObjectExpression = callExpression.arguments[0]; + + processRecipeProperties(ruleContext, recipeObjectExpression, (currentContext, styleObject) => + processStyleNode(currentContext, styleObject, enforceConcentricCSSOrderInStyleObject), + ); } - - const recipeObjectExpression = callExpression.arguments[0]; - - processRecipeProperties(ruleContext, recipeObjectExpression, (currentContext, styleObject) => - processStyleNode(currentContext, styleObject, enforceConcentricCSSOrderInStyleObject), - ); }; diff --git a/src/css-rules/concentric-order/style-object-processor.ts b/src/css-rules/concentric-order/style-object-processor.ts index 4b9e69b..e99654c 100644 --- a/src/css-rules/concentric-order/style-object-processor.ts +++ b/src/css-rules/concentric-order/style-object-processor.ts @@ -50,23 +50,21 @@ export const enforceConcentricCSSOrderInStyleObject = ( ruleContext: Rule.RuleContext, styleObject: TSESTree.ObjectExpression, ): void => { - if (!styleObject || styleObject.type !== AST_NODE_TYPES.ObjectExpression) { - return; + if (styleObject?.type === AST_NODE_TYPES.ObjectExpression) { + if (isSelectorsObject(styleObject)) { + styleObject.properties.forEach((property) => { + if (property.type === AST_NODE_TYPES.Property && property.value.type === AST_NODE_TYPES.ObjectExpression) { + enforceConcentricCSSOrderInStyleObject(ruleContext, property.value); + } + }); + return; + } + + const { regularProperties } = separateProperties(styleObject.properties); + const cssPropertyInfoList = buildCSSPropertyInfoList(regularProperties); + + enforceConcentricCSSOrder(ruleContext, cssPropertyInfoList); + + processNestedSelectors(ruleContext, styleObject, enforceConcentricCSSOrderInStyleObject); } - - if (isSelectorsObject(styleObject)) { - styleObject.properties.forEach((property) => { - if (property.type === AST_NODE_TYPES.Property && property.value.type === AST_NODE_TYPES.ObjectExpression) { - enforceConcentricCSSOrderInStyleObject(ruleContext, property.value); - } - }); - return; - } - - const { regularProperties } = separateProperties(styleObject.properties); - const cssPropertyInfoList = buildCSSPropertyInfoList(regularProperties); - - enforceConcentricCSSOrder(ruleContext, cssPropertyInfoList); - - processNestedSelectors(ruleContext, styleObject, enforceConcentricCSSOrderInStyleObject); }; diff --git a/src/css-rules/custom-order/__tests__/defaults.test.ts b/src/css-rules/custom-order/__tests__/defaults.test.ts new file mode 100644 index 0000000..8e60eda --- /dev/null +++ b/src/css-rules/custom-order/__tests__/defaults.test.ts @@ -0,0 +1,115 @@ +import tsParser from '@typescript-eslint/parser'; +import { run } from 'eslint-vitest-rule-tester'; +import customGroupOrderRule from '../rule-definition.js'; + +run({ + name: 'vanilla-extract/custom-order/defaults', + rule: customGroupOrderRule, + languageOptions: { + parser: tsParser, + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, + }, + valid: [ + // Test with no options provided - should use defaults + ` + import { style } from '@vanilla-extract/css'; + const myStyle = style({ + alignItems: 'center', + backgroundColor: 'red', + color: 'blue', + display: 'flex', + margin: '10px', + padding: '20px', + zIndex: 1 + }); + `, + + // Test with empty groupOrder array - should use defaults + { + code: ` + import { style } from '@vanilla-extract/css'; + const myStyle = style({ + alignItems: 'center', + backgroundColor: 'red', + color: 'blue', + display: 'flex', + margin: '10px', + padding: '20px', + zIndex: 1 + }); + `, + options: [ + { + groupOrder: [], + }, + ], + }, + ], + invalid: [ + // Test with no options provided - should use alphabetical ordering by default + { + code: ` + import { style } from '@vanilla-extract/css'; + const myStyle = style({ + zIndex: 1, + padding: '20px', + margin: '10px', + display: 'flex', + color: 'blue', + backgroundColor: 'red', + alignItems: 'center' + }); + `, + errors: [{ messageId: 'alphabeticalOrder' }], + output: ` + import { style } from '@vanilla-extract/css'; + const myStyle = style({ + alignItems: 'center', + backgroundColor: 'red', + color: 'blue', + display: 'flex', + margin: '10px', + padding: '20px', + zIndex: 1 + }); + `, + }, + + // Test with empty groupOrder array - should use alphabetical ordering by default + { + code: ` + import { style } from '@vanilla-extract/css'; + const myStyle = style({ + zIndex: 1, + padding: '20px', + margin: '10px', + display: 'flex', + color: 'blue', + backgroundColor: 'red', + alignItems: 'center' + }); + `, + options: [ + { + groupOrder: [], + }, + ], + errors: [{ messageId: 'alphabeticalOrder' }], + output: ` + import { style } from '@vanilla-extract/css'; + const myStyle = style({ + alignItems: 'center', + backgroundColor: 'red', + color: 'blue', + display: 'flex', + margin: '10px', + padding: '20px', + zIndex: 1 + }); + `, + }, + ], +}); diff --git a/src/css-rules/custom-order/property-order-enforcer.ts b/src/css-rules/custom-order/property-order-enforcer.ts index 52e636a..a5c6788 100644 --- a/src/css-rules/custom-order/property-order-enforcer.ts +++ b/src/css-rules/custom-order/property-order-enforcer.ts @@ -23,69 +23,74 @@ export const enforceCustomGroupOrder = ( userDefinedGroups: string[] = [], sortRemainingProperties?: 'alphabetical' | 'concentric', ): void => { - if (cssPropertyInfoList.length <= 1) { - return; - } + if (cssPropertyInfoList.length > 1) { + const cssPropertyPriorityMap = createCSSPropertyPriorityMap(userDefinedGroups); - const cssPropertyPriorityMap = createCSSPropertyPriorityMap(userDefinedGroups); + const compareProperties = (firstProperty: CSSPropertyInfo, secondProperty: CSSPropertyInfo) => { + const firstPropertyInfo = cssPropertyPriorityMap.get(firstProperty.name) || { + groupIndex: Infinity, + positionInGroup: Infinity, + inUserGroup: false, + }; + const secondPropertyInfo = cssPropertyPriorityMap.get(secondProperty.name) || { + groupIndex: Infinity, + positionInGroup: Infinity, + inUserGroup: false, + }; - const compareProperties = (firstProperty: CSSPropertyInfo, secondProperty: CSSPropertyInfo) => { - 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; + if (firstPropertyInfo.inUserGroup !== secondPropertyInfo.inUserGroup) { + return firstPropertyInfo.inUserGroup ? -1 : 1; } - return firstPropertyInfo.positionInGroup - secondPropertyInfo.positionInGroup; + + 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 + // Therefore, we can exclude the next line from coverage because it's unreachable in practice + /* v8 ignore next */ + const nextPropertyName = sortedProperty?.name ?? ''; + + ruleContext.report({ + node: violatingProperty.node as Rule.Node, + messageId: 'incorrectOrder', + data: { + currentProperty: violatingProperty.name, + nextProperty: nextPropertyName, + }, + fix: (fixer) => + generateFixesForCSSOrder( + fixer, + ruleContext, + cssPropertyInfoList, + compareProperties, + (propertyInfo) => propertyInfo.node as Rule.Node, + ), + }); } - - // 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) { - ruleContext.report({ - node: violatingProperty.node as Rule.Node, - messageId: 'incorrectOrder', - data: { - currentProperty: violatingProperty.name, - nextProperty: sortedPropertyList[cssPropertyInfoList.indexOf(violatingProperty)]?.name || '', - }, - fix: (fixer) => - generateFixesForCSSOrder( - fixer, - ruleContext, - cssPropertyInfoList, - compareProperties, - (propertyInfo) => propertyInfo.node as Rule.Node, - ), - }); } }; diff --git a/src/css-rules/custom-order/recipe-order-enforcer.ts b/src/css-rules/custom-order/recipe-order-enforcer.ts index c34c7c9..c627aa0 100644 --- a/src/css-rules/custom-order/recipe-order-enforcer.ts +++ b/src/css-rules/custom-order/recipe-order-enforcer.ts @@ -23,20 +23,18 @@ export const enforceUserDefinedGroupOrderInRecipe = ( userDefinedGroups: string[], sortRemainingPropertiesMethod?: 'alphabetical' | 'concentric', ): void => { - if (!callExpression.arguments[0] || callExpression.arguments[0].type !== 'ObjectExpression') { - return; - } + if (callExpression.arguments[0]?.type === 'ObjectExpression') { + const recipeObjectExpression = callExpression.arguments[0]; - const recipeObjectExpression = callExpression.arguments[0]; - - processRecipeProperties(ruleContext, recipeObjectExpression, (currentContext, styleObject) => - processStyleNode(currentContext, styleObject, (styleContext, styleObjectNode) => - enforceUserDefinedGroupOrderInStyleObject( - styleContext, - styleObjectNode, - userDefinedGroups, - sortRemainingPropertiesMethod, + processRecipeProperties(ruleContext, recipeObjectExpression, (currentContext, styleObject) => + processStyleNode(currentContext, styleObject, (styleContext, styleObjectNode) => + enforceUserDefinedGroupOrderInStyleObject( + styleContext, + styleObjectNode, + userDefinedGroups, + sortRemainingPropertiesMethod, + ), ), - ), - ); + ); + } }; diff --git a/src/css-rules/custom-order/rule-definition.ts b/src/css-rules/custom-order/rule-definition.ts index 50159ab..91e5bc8 100644 --- a/src/css-rules/custom-order/rule-definition.ts +++ b/src/css-rules/custom-order/rule-definition.ts @@ -34,10 +34,13 @@ const customGroupOrderRule: Rule.RuleModule = { }, ], messages: { - incorrectOrder: - "Property '{{nextProperty}}' should come before '{{currentProperty}}' according to custom CSS group ordering.", + // default message for ordering in case no groupOrder is provided + alphabeticalOrder: + "Property '{{nextProperty}}' should come before '{{currentProperty}}' according to alphabetical ordering.", fontFaceOrder: "Properties in fontFace should be ordered with 'src' first, followed by other properties in alphabetical order. Property '{{nextProperty}}' should come before '{{currentProperty}}'.", + incorrectOrder: + "Property '{{nextProperty}}' should come before '{{currentProperty}}' according to custom CSS group ordering.", }, }, create(ruleContext: Rule.RuleContext) { diff --git a/src/css-rules/custom-order/style-object-processor.ts b/src/css-rules/custom-order/style-object-processor.ts index 25884a4..be3998d 100644 --- a/src/css-rules/custom-order/style-object-processor.ts +++ b/src/css-rules/custom-order/style-object-processor.ts @@ -29,51 +29,49 @@ export const enforceUserDefinedGroupOrderInStyleObject = ( userDefinedGroups: string[], sortRemainingPropertiesMethod: 'alphabetical' | 'concentric' = 'concentric', ): void => { - if (!styleObject || styleObject.type !== AST_NODE_TYPES.ObjectExpression) { - return; - } + if (styleObject?.type === AST_NODE_TYPES.ObjectExpression) { + if (isSelectorsObject(styleObject)) { + styleObject.properties.forEach((property) => { + if (property.type === AST_NODE_TYPES.Property && property.value.type === AST_NODE_TYPES.ObjectExpression) { + enforceUserDefinedGroupOrderInStyleObject( + ruleContext, + property.value, + userDefinedGroups, + sortRemainingPropertiesMethod, + ); + } + }); + return; + } - if (isSelectorsObject(styleObject)) { - styleObject.properties.forEach((property) => { - if (property.type === AST_NODE_TYPES.Property && property.value.type === AST_NODE_TYPES.ObjectExpression) { - enforceUserDefinedGroupOrderInStyleObject( - ruleContext, - property.value, - userDefinedGroups, - sortRemainingPropertiesMethod, - ); - } + const cssPropertyPriorityMap = createCSSPropertyPriorityMap(userDefinedGroups); + + const { regularProperties } = separateProperties(styleObject.properties); + const cssPropertyInfoList: CSSPropertyInfo[] = regularProperties.map((property) => { + const propertyName = getPropertyName(property); + const propertyInfo = cssPropertyPriorityMap.get(propertyName); + const group = + userDefinedGroups.find((groupName) => concentricGroups[groupName]?.includes(propertyName)) || 'remaining'; + + return { + name: propertyName, + node: property, + priority: propertyInfo?.groupIndex ?? Number.MAX_SAFE_INTEGER, + positionInGroup: propertyInfo?.positionInGroup ?? Number.MAX_SAFE_INTEGER, + group, + inUserGroup: propertyInfo?.inUserGroup ?? false, + }; }); - return; + + enforceCustomGroupOrder(ruleContext, cssPropertyInfoList, userDefinedGroups, sortRemainingPropertiesMethod); + + processNestedSelectors(ruleContext, styleObject, (nestedContext, nestedNode) => + enforceUserDefinedGroupOrderInStyleObject( + nestedContext, + nestedNode, + userDefinedGroups, + sortRemainingPropertiesMethod, + ), + ); } - - const cssPropertyPriorityMap = createCSSPropertyPriorityMap(userDefinedGroups); - - const { regularProperties } = separateProperties(styleObject.properties); - const cssPropertyInfoList: CSSPropertyInfo[] = regularProperties.map((property) => { - const propertyName = getPropertyName(property); - const propertyInfo = cssPropertyPriorityMap.get(propertyName); - const group = - userDefinedGroups.find((groupName) => concentricGroups[groupName]?.includes(propertyName)) || 'remaining'; - - return { - name: propertyName, - node: property, - priority: propertyInfo?.groupIndex ?? Number.MAX_SAFE_INTEGER, - positionInGroup: propertyInfo?.positionInGroup ?? Number.MAX_SAFE_INTEGER, - group, - inUserGroup: propertyInfo?.inUserGroup ?? false, - }; - }); - - enforceCustomGroupOrder(ruleContext, cssPropertyInfoList, userDefinedGroups, sortRemainingPropertiesMethod); - - processNestedSelectors(ruleContext, styleObject, (nestedContext, nestedNode) => - enforceUserDefinedGroupOrderInStyleObject( - nestedContext, - nestedNode, - userDefinedGroups, - sortRemainingPropertiesMethod, - ), - ); }; diff --git a/src/css-rules/shared-utils/order-strategy-visitor-creator.ts b/src/css-rules/shared-utils/order-strategy-visitor-creator.ts index dc36d28..55b172c 100644 --- a/src/css-rules/shared-utils/order-strategy-visitor-creator.ts +++ b/src/css-rules/shared-utils/order-strategy-visitor-creator.ts @@ -40,7 +40,7 @@ export const createNodeVisitors = ( return enforceConcentricCSSOrderInStyleObject; case 'userDefinedGroupOrder': if (!userDefinedGroupOrder || userDefinedGroupOrder.length === 0) { - throw new Error('💥 👿 User-defined group order must be provided for userDefinedGroupOrder strategy'); + return enforceAlphabeticalCSSOrderInStyleObject; } return (ruleContext: Rule.RuleContext, node: TSESTree.Node) => enforceUserDefinedGroupOrderInStyleObject( diff --git a/src/index.ts b/src/index.ts index 87cc9e7..1ebd3fd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import customOrderRule from './css-rules/custom-order/rule-definition.js'; export const vanillaExtract = { meta: { name: '@antebudimir/eslint-plugin-vanilla-extract', - version: '1.4.7', + version: '1.5.0', }, rules: { 'alphabetical-order': alphabeticalOrderRule,