UNPKG

@antebudimir/eslint-plugin-vanilla-extract

Version:

Comprehensive ESLint plugin for vanilla-extract with CSS property ordering, style validation, and best practices enforcement. Supports alphabetical, concentric and custom CSS ordering, auto-fixing, and zero-runtime safety.

86 lines (85 loc) 4.52 kB
import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils'; import { processRecipeProperties } from '../shared-utils/recipe-property-processor.js'; import { ReferenceTracker, createReferenceTrackingVisitor } from '../shared-utils/reference-tracker.js'; import { processStyleNode } from '../shared-utils/style-node-processor.js'; import { processTrailingZeroInStyleObject } from './trailing-zero-processor.js'; /** * Creates ESLint rule visitors for detecting and processing trailing zeros in numeric CSS values. * Uses reference tracking to automatically detect vanilla-extract functions based on their import statements. * * @param context The ESLint rule context. * @returns An object with visitor functions for the ESLint rule. */ export const createTrailingZeroVisitors = (context) => { const tracker = new ReferenceTracker(); const trackingVisitor = createReferenceTrackingVisitor(tracker); return { // Include the reference tracking visitors ...trackingVisitor, CallExpression(node) { if (node.callee.type !== AST_NODE_TYPES.Identifier) { return; } const functionName = node.callee.name; // Check if this function is tracked as a vanilla-extract function if (!tracker.isTrackedFunction(functionName)) { return; } const originalName = tracker.getOriginalName(functionName); if (!originalName) { return; } // Handle different function types based on their original imported name switch (originalName) { case 'fontFace': if (node.arguments.length > 0 && node.arguments[0]?.type === AST_NODE_TYPES.ObjectExpression) { processTrailingZeroInStyleObject(context, node.arguments[0]); } break; case 'globalFontFace': if (node.arguments.length > 1 && node.arguments[1]?.type === AST_NODE_TYPES.ObjectExpression) { processTrailingZeroInStyleObject(context, node.arguments[1]); } break; case 'style': if (node.arguments.length > 0) { processStyleNode(context, node.arguments[0], processTrailingZeroInStyleObject); } break; case 'styleVariants': case 'keyframes': // For styleVariants and keyframes, the argument is an object where each property value is a style object if (node.arguments.length > 0 && node.arguments[0]?.type === AST_NODE_TYPES.ObjectExpression) { const variantsObject = node.arguments[0]; variantsObject.properties.forEach((property) => { if (property.type === 'Property' && property.value.type === 'ObjectExpression') { processTrailingZeroInStyleObject(context, property.value); } }); } break; case 'globalStyle': if (node.arguments.length >= 2) { processStyleNode(context, node.arguments[1], processTrailingZeroInStyleObject); } break; case 'globalKeyframes': // For globalKeyframes, the second argument is an object where each property value is a style object if (node.arguments.length >= 2 && node.arguments[1]?.type === AST_NODE_TYPES.ObjectExpression) { const keyframesObject = node.arguments[1]; keyframesObject.properties.forEach((property) => { if (property.type === 'Property' && property.value.type === 'ObjectExpression') { processTrailingZeroInStyleObject(context, property.value); } }); } break; case 'recipe': if (node.arguments.length > 0 && node.arguments[0]?.type === AST_NODE_TYPES.ObjectExpression) { processRecipeProperties(context, node.arguments[0], processTrailingZeroInStyleObject); } break; } }, }; };