mirror of
https://github.com/antebudimir/eslint-plugin-vanilla-extract.git
synced 2025-12-31 17:03:32 +00:00
refactor ♻️: improve code quality and test coverage
- Fix handling of missing groupOrder configuration - Refactor negative conditions to positive ones with optional chaining - Add comprehensive tests to achieve total coverage
This commit is contained in:
parent
5557409368
commit
46751da51b
15 changed files with 310 additions and 186 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
15
README.md
15
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)
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
115
src/css-rules/custom-order/__tests__/defaults.test.ts
Normal file
115
src/css-rules/custom-order/__tests__/defaults.test.ts
Normal file
|
|
@ -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
|
||||
});
|
||||
`,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
@ -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,
|
||||
),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue