From 7ea9f48e2c71313a25c0c053fe986013967dffb9 Mon Sep 17 00:00:00 2001 From: itaymendel Date: Tue, 6 Oct 2026 15:02:25 +0100 Subject: [PATCH] refactor!: simplify public API around ComplexityOptions and the combined visitor --- CHANGELOG.md | 2 ++ README.md | 28 ++++++++++++---------------- src/cognitive/handlers.ts | 2 +- src/index.ts | 14 ++------------ src/rules/complexity.ts | 14 +++++--------- src/rules/shared.ts | 4 ++-- src/types.ts | 10 ++++------ tests/utils/test-helpers.ts | 2 +- 8 files changed, 29 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d1e679..98e0719 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Replace the exported `MaxCyclomaticOptions` and `MaxCognitiveOptions` types with `ComplexityOptions`. Use `cyclomatic` and `cognitive` instead of the obsolete `max` field. +- **BREAKING:** Remove public exports for `createCognitiveVisitor`, `createCyclomaticVisitor`, `createComplexityVisitor`, `VisitorContext`, and `FunctionScope`. Use `createCombinedComplexityVisitor` for AST analysis or `/standalone` for source strings. Individual visitor migrations change loop labels and cognitive recursion point locations and ordering; see the README migration guide. - Unify cognitive complexity handlers and route fixture tests through the lint rule's visitor. ### Fixed diff --git a/README.md b/README.md index f3ab724..7dc64d4 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,8 @@ function processData(items, mode, config) { } ``` +TypeScript users can import `ComplexityOptions` from `oxlint-plugin-complexity` to type the rule options. + ### Cyclomatic Complexity Counts decision points in code. [Learn more](https://en.wikipedia.org/wiki/Cyclomatic_complexity) @@ -218,24 +220,18 @@ for (const file of result.files) { } ``` -## Migration from v0.x +## Migration to v4 (for library usage) -Replace the removed `max-cyclomatic` / `max-cognitive` rules with the combined `complexity` rule: +- Replace `MaxCyclomaticOptions` / `MaxCognitiveOptions` with `ComplexityOptions`, using `cyclomatic` + and `cognitive` instead of `max`. +- Replace `createCognitiveVisitor` / `createCyclomaticVisitor` with + `createCombinedComplexityVisitor(context, callback)`. Results contain `cyclomatic`, `cognitive`, + `cyclomaticPoints`, and `cognitivePoints`; both metrics now require an oxlint `Context`. +- `createComplexityVisitor`, `VisitorContext`, and `FunctionScope` are now internal. -```diff -// .oxlintrc.json -{ - "jsPlugins": ["oxlint-plugin-complexity"], - "rules": { -- "complexity/max-cyclomatic": ["error", { "max": 20 }], -- "complexity/max-cognitive": ["error", { "max": 15 }] -+ "complexity/complexity": ["error", { -+ "cyclomatic": 20, -+ "cognitive": 15 -+ }] - } -} -``` +When migrating individual visitors, loop labels become `for-in`, `for-of`, and `do-while`. +Cognitive recursion points now identify the first recursive call in traversal order, rather than the +whole function on exit. The `/standalone` and `/diff` APIs are unchanged by this cleanup. ## Attribution diff --git a/src/cognitive/handlers.ts b/src/cognitive/handlers.ts index c4bf1f3..4124308 100644 --- a/src/cognitive/handlers.ts +++ b/src/cognitive/handlers.ts @@ -22,7 +22,7 @@ export interface CognitiveFunctionScope extends FunctionScope { hasRecursiveCall: boolean; } -// Preserve each public visitor's existing diagnostic presentation, not separate scoring rules. +// Preserve the internal extraction visitor's and combined visitor's existing point formats. const POINT_FORMATS = { standalone: { forIn: 'for...in', diff --git a/src/index.ts b/src/index.ts index acb2c03..d33d180 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,22 +7,12 @@ export type { Rule, Context, Visitor, - FunctionScope, ComplexityPoint, ComplexityResult, - MaxCyclomaticOptions, - MaxCognitiveOptions, + ComplexityOptions, } from './types.js'; -// Re-export visitor factory for advanced usage -export { createComplexityVisitor } from './visitor.js'; -export type { VisitorContext } from './visitor.js'; - -// Re-export calculators for programmatic use -export { createCyclomaticVisitor } from './cyclomatic.js'; -export { createCognitiveVisitor } from './cognitive/visitor.js'; - -// Re-export combined visitor for advanced usage +// Supported AST API for calculating both metrics in one traversal export { createCombinedComplexityVisitor } from './combined-visitor.js'; export type { CombinedComplexityResult } from './combined-visitor.js'; diff --git a/src/rules/complexity.ts b/src/rules/complexity.ts index f5f649c..791e095 100644 --- a/src/rules/complexity.ts +++ b/src/rules/complexity.ts @@ -1,9 +1,9 @@ -import { defineRule } from '@oxlint/plugins'; +import { defineRule, type RuleOptionsSchema } from '@oxlint/plugins'; import type { Rule, Context, FunctionNode, - MaxCognitiveOptions, + ComplexityOptions, VisitorWithHooks, ESTreeNode, } from '../types.js'; @@ -23,11 +23,7 @@ const DEFAULT_CYCLOMATIC = 20; const DEFAULT_COGNITIVE = 15; const DEFAULT_MIN_LINES = 10; -interface CombinedComplexityOptions extends Omit { - cyclomatic?: number; - cognitive?: number; - minLines?: number; -} +type OptionSchema = Extract[number]; /** * Enforce maximum cyclomatic and cognitive complexity (RECOMMENDED). @@ -68,7 +64,7 @@ export const complexity: Rule = defineRule({ description: 'Minimum lines to analyze (default: 10, 0 = analyze all)', }, ...EXTRACTION_SCHEMA_PROPERTIES, - }, + } satisfies Record, additionalProperties: false, }, ], @@ -138,7 +134,7 @@ export const complexity: Rule = defineRule({ return { before() { - const options = (context.options[0] ?? {}) as CombinedComplexityOptions; + const options = (context.options[0] ?? {}) as ComplexityOptions; maxCyclomatic = options.cyclomatic ?? DEFAULT_CYCLOMATIC; maxCognitive = options.cognitive ?? DEFAULT_COGNITIVE; minLines = options.minLines ?? DEFAULT_MIN_LINES; diff --git a/src/rules/shared.ts b/src/rules/shared.ts index 7e5682d..8215d7c 100644 --- a/src/rules/shared.ts +++ b/src/rules/shared.ts @@ -1,4 +1,4 @@ -import type { Context, ComplexityPoint, ESTreeNode, MaxCognitiveOptions } from '../types.js'; +import type { Context, ComplexityPoint, ESTreeNode, ComplexityOptions } from '../types.js'; import { type BreakdownOptions } from '../utils.js'; import { analyzeExtractionOpportunities, @@ -49,7 +49,7 @@ export const EXTRACTION_SCHEMA_PROPERTIES = { }, } as const; -type ExtractionSchemaOptions = Omit; +type ExtractionSchemaOptions = Pick; export interface ParsedExtractionOptions { enableExtraction: boolean; diff --git a/src/types.ts b/src/types.ts index a35389e..971fdb6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -45,12 +45,10 @@ export interface FunctionScope { points: ComplexityPoint[]; } -export interface MaxCyclomaticOptions { - max?: number; -} - -export interface MaxCognitiveOptions { - max?: number; +export interface ComplexityOptions { + cyclomatic?: number; + cognitive?: number; + minLines?: number; enableExtraction?: boolean; extractionMultiplier?: number; minExtractionPercentage?: number; diff --git a/tests/utils/test-helpers.ts b/tests/utils/test-helpers.ts index e4f1be2..ddac630 100644 --- a/tests/utils/test-helpers.ts +++ b/tests/utils/test-helpers.ts @@ -119,7 +119,7 @@ export function parseAndPrepareAst( } /** - * Exercise the separate public visitor factories for compatibility checks. + * Exercise the internal single-metric visitor factories for compatibility checks. */ function calculateStandaloneComplexityWithVisitor( code: string,