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.

82 lines (81 loc) 3.79 kB
import * as path from 'path'; 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 { ThemeContractAnalyzer } from './theme-contract-analyzer.js'; import { processThemeTokensInStyleObject } from './theme-token-processor.js'; /** * Creates ESLint rule visitors for the prefer-theme-tokens rule */ export const createThemeTokenVisitors = (context, options) => { const tracker = new ReferenceTracker(); const trackingVisitor = createReferenceTrackingVisitor(tracker); // Initialize the theme contract analyzer const analyzer = new ThemeContractAnalyzer(); // Set rem base if provided if (options.remBase) { analyzer.setRemBase(options.remBase); } // Load theme contracts if provided const themeContracts = options.themeContracts || []; if (themeContracts.length > 0) { // Use getCwd() to get project root, or fallback to linted file's directory const baseDir = context.getCwd ? context.getCwd() : path.dirname(context.filename || context.getFilename()); themeContracts.forEach((contractPath) => { analyzer.loadThemeContract(contractPath, baseDir); }); } const process = (context, object) => processThemeTokensInStyleObject(context, object, options, analyzer); return { ...trackingVisitor, // Call the tracking visitor's ImportDeclaration handler ImportDeclaration(node) { if (trackingVisitor.ImportDeclaration) { trackingVisitor.ImportDeclaration(node); } }, CallExpression(node) { if (node.callee.type !== AST_NODE_TYPES.Identifier) return; const functionName = node.callee.name; if (!tracker.isTrackedFunction(functionName)) return; const originalName = tracker.getOriginalName(functionName); if (!originalName) return; switch (originalName) { case 'fontFace': if (node.arguments.length > 0 && node.arguments[0]?.type === AST_NODE_TYPES.ObjectExpression) { process(context, node.arguments[0]); } break; case 'globalFontFace': if (node.arguments.length > 1 && node.arguments[1]?.type === AST_NODE_TYPES.ObjectExpression) { process(context, node.arguments[1]); } break; case 'style': case 'styleVariants': case 'keyframes': if (node.arguments.length > 0) { processStyleNode(context, node.arguments[0], (context, object) => process(context, object)); } break; case 'globalStyle': if (node.arguments.length > 1 && node.arguments[1]?.type === AST_NODE_TYPES.ObjectExpression) { process(context, node.arguments[1]); } break; case 'recipe': case 'compoundVariant': if (node.arguments.length > 0 && node.arguments[0]?.type === AST_NODE_TYPES.ObjectExpression) { processRecipeProperties(context, node.arguments[0], (context, object) => process(context, object)); } break; default: break; } }, }; };