UNPKG

@ospm/eslint-plugin-react-signals-hooks

Version:

ESLint plugin for React Signals hooks - enforces best practices, performance optimizations, and integration patterns for @preact/signals-react usage in React projects

124 lines 4.76 kB
/** biome-ignore-all assist/source/organizeImports: off */ /** biome-ignore-all lint/correctness/noUnusedVariables: off */ import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils'; import { PerformanceOperations } from './utils/performance-constants.js'; import { endPhase, startPhase, recordMetric, startTracking, trackOperation, createPerformanceTracker, DEFAULT_PERFORMANCE_BUDGET, } from './utils/performance.js'; import { getRuleDocUrl } from './utils/urls.js'; // eslint-disable-next-line @typescript-eslint/no-unused-vars function getSeverity(messageId, options) { if (!options?.severity) { return 'error'; } switch (messageId) { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition case 'exampleMessageId': { return options.severity.exampleMessageId ?? 'error'; } default: { return 'error'; } } } const ruleName = 'rule-name'; export const rule = ESLintUtils.RuleCreator((name) => { return getRuleDocUrl(name); })({ name: ruleName, meta: { type: 'suggestion', // or 'problem' or 'layout' docs: { description: 'Brief description of what the rule does', url: getRuleDocUrl(ruleName), }, fixable: 'code', hasSuggestions: false, schema: [ { type: 'object', properties: { performance: { type: 'object', additionalProperties: false, properties: { maxNodes: { type: 'number', minimum: 1 }, maxTime: { type: 'number', minimum: 1 }, }, }, severity: { type: 'object', properties: { exampleMessageId: { type: 'string', enum: ['error', 'warn', 'off'], }, }, additionalProperties: false, }, suffix: { type: 'string', minLength: 1 }, }, additionalProperties: false, }, ], messages: { exampleMessageId: 'Your error message here', }, }, defaultOptions: [ { performance: DEFAULT_PERFORMANCE_BUDGET, }, ], create(context, [option]) { const perfKey = `${ruleName}:${context.filename}:${Date.now()}`; startPhase(perfKey, 'ruleInit'); const perf = createPerformanceTracker(perfKey, option?.performance); if (option?.performance?.enableMetrics === true) { startTracking(context, perfKey, option.performance, ruleName); } if (option?.performance?.enableMetrics === true && option.performance.logMetrics === true) { console.info(`${ruleName}: Initializing rule for file: ${context.filename}`); console.info(`${ruleName}: Rule configuration:`, option); } recordMetric(perfKey, 'config', { performance: { enableMetrics: option?.performance?.enableMetrics, logMetrics: option?.performance?.logMetrics, }, }); trackOperation(perfKey, PerformanceOperations.ruleInit); endPhase(perfKey, 'ruleInit'); let nodeCount = 0; function shouldContinue() { nodeCount++; if (typeof option?.performance?.maxNodes === 'number' && nodeCount > option.performance.maxNodes) { trackOperation(perfKey, PerformanceOperations.nodeBudgetExceeded); return false; } return true; } startPhase(perfKey, 'ruleExecution'); return { '*': (node) => { if (!shouldContinue()) { endPhase(perfKey, 'recordMetrics'); return; } perf.trackNode(node); trackOperation(perfKey, PerformanceOperations.nodeProcessing); }, // Example for specific node types // CallExpression: (node) => { // trackOperation(perf, perfKey, 'CallExpression', () => { // // Your validation logic here // }); // }, [`${AST_NODE_TYPES.Program}:exit`]() { startPhase(perfKey, 'programExit'); perf['Program:exit'](); endPhase(perfKey, 'programExit'); }, }; }, }); //# sourceMappingURL=template.js.map