Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Migrate plugin imports from `oxlint/plugins` to standalone `@oxlint/plugins` package (fixes compatibility with oxlint >= 1.45.0)
- **BREAKING:** `enableExtraction` now defaults to `true` — extraction suggestions are on by default (set `enableExtraction: false` to opt out)

### Added

- Performance optimization: `minLines` option to skip complexity analysis for small functions. Default: 10 lines.
- Export `createCombinedComplexityVisitor` and `CombinedComplexityResult` from public API

### Removed

- **BREAKING:** Drop deprecated `complexity/max-cyclomatic` and `complexity/max-cognitive` rules — use `complexity/complexity` instead

## [1.0.0-rc.1] - 2026-02-08

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ function processData(items, mode, config) {
"minLines": 10, // Default: 10 (skip functions <10 lines like getters; 0 = analyze all; counts comments/blanks)

// Extraction suggestions (optional)
"enableExtraction": true, // Default: false
"enableExtraction": true, // Default: true
"extractionMultiplier": 1.5, // Default: 1.5 (triggers at 1.5× cognitive threshold)
"minExtractionPercentage": 30, // Default: 30 (min % of total complexity to suggest)

Expand Down Expand Up @@ -122,7 +122,7 @@ Detects common complexity patterns and provides actionable tips:

### Extraction Suggestions

When `enableExtraction: true`, analyzes variable flow to identify extractable code blocks:
Analyzes variable flow to identify extractable code blocks (enabled by default, disable with `enableExtraction: false`):

**Example output:**

Expand Down Expand Up @@ -159,9 +159,9 @@ Always review suggestions before applying, even when marked "high confidence".

---

## Migration from v0.x to v1.0
## Migration from v0.x

**v1.0:** Combined rule for better performance. Separate rules deprecated:
Replace the removed `max-cyclomatic` / `max-cognitive` rules with the combined `complexity` rule:

```diff
// .oxlintrc.json
Expand Down
12 changes: 5 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { definePlugin } from '@oxlint/plugins';
import { maxCyclomatic } from './rules/max-cyclomatic.js';
import { maxCognitive } from './rules/max-cognitive.js';
import { complexity } from './rules/complexity.js';

// Re-export types for library users
Expand All @@ -24,6 +22,10 @@ export type { VisitorContext } from './visitor.js';
export { createCyclomaticVisitor } from './cyclomatic.js';
export { createCognitiveVisitor } from './cognitive/visitor.js';

// Re-export combined visitor for advanced usage
export { createCombinedComplexityVisitor } from './combined-visitor.js';
export type { CombinedComplexityResult } from './combined-visitor.js';

// Re-export utilities
export { getFunctionName, createComplexityPoint, summarizeComplexity } from './utils.js';

Expand All @@ -50,18 +52,14 @@ export {
* Provides cyclomatic and cognitive complexity rules for oxlint.
*
* Rules:
* - complexity/complexity: RECOMMENDED - Enforce both metrics in one pass (17% faster)
* - complexity/max-cyclomatic: DEPRECATED - Use complexity instead
* - complexity/max-cognitive: DEPRECATED - Use complexity instead
* - complexity/complexity: Enforce both metrics in one pass
*/
const plugin = definePlugin({
meta: {
name: 'complexity',
},
rules: {
complexity,
'max-cyclomatic': maxCyclomatic,
'max-cognitive': maxCognitive,
},
});

Expand Down
91 changes: 0 additions & 91 deletions src/rules/max-cognitive.ts

This file was deleted.

19 changes: 0 additions & 19 deletions src/rules/max-cyclomatic.ts

This file was deleted.

119 changes: 4 additions & 115 deletions src/rules/shared.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,5 @@
import { defineRule } from '@oxlint/plugins';
import type {
Rule,
Context,
Visitor,
FunctionNode,
ComplexityPoint,
ComplexityResult,
VisitorWithHooks,
ESTreeNode,
MaxCognitiveOptions,
} from '../types.js';
import {
getFunctionName,
summarizeComplexity,
formatBreakdown,
type BreakdownOptions,
} from '../utils.js';
import type { Context, ComplexityPoint, ESTreeNode, MaxCognitiveOptions } from '../types.js';
import { type BreakdownOptions } from '../utils.js';
import {
analyzeExtractionOpportunities,
shouldAnalyzeExtraction,
Expand All @@ -24,108 +8,13 @@ import {
} from '../extraction/index.js';
import { getVariablesForFunction } from '../extraction/variable-tracker.js';

/**
* Configuration for creating a simple complexity rule
* (single metric, no extraction analysis).
*/
export interface ComplexityRuleConfig {
metricName: string;
defaultMax: number;
schemaMinimum: number;
description: string;
url: string;

/** Factory to create the visitor */
createVisitor: (
onComplexityCalculated: (result: ComplexityResult, node: ESTreeNode) => void
) => Visitor;

/** Called once before the first file is processed */
before?: () => void;

normalizeCategory?: (category: string) => string;
}

export function createComplexityRule(config: ComplexityRuleConfig): Rule {
return defineRule({
meta: {
type: 'suggestion',
docs: {
description: config.description,
recommended: false,
url: config.url,
},
schema: [
{
type: 'object',
properties: {
max: {
type: 'integer',
minimum: config.schemaMinimum,
},
},
additionalProperties: false,
},
],
},

createOnce(context: Context) {
let maxComplexity = config.defaultMax;

return {
before() {
const options = (context.options[0] ?? {}) as { max?: number };
maxComplexity = options.max ?? config.defaultMax;
config.before?.();
},

...config.createVisitor((result, node) => {
if (result.total > maxComplexity) {
const funcNode = node as FunctionNode;
const name = getFunctionName(funcNode, funcNode.parent);
const summary = summarizeComplexity(result.points, config.normalizeCategory);
const breakdown = formatBreakdown(result.points);

context.report({
node,
message: `Function '${name}' has ${config.metricName} of ${result.total}. Maximum allowed is ${maxComplexity}.${summary}${breakdown}`,
});
}
}),
} as VisitorWithHooks;
},
});
}

export function normalizeCognitiveCategory(category: string): string {
if (category.startsWith('logical operator')) return 'logical operators';
if (category.startsWith('nested ')) return 'nested functions';
if (category.startsWith('break to') || category.startsWith('continue to')) return 'labeled jumps';
return category;
}

const MIGRATION_URL = 'https://github.com/itaymendel/oxlint-plugin-complexity#migration-to-v1';
const warnedDeprecations = new Set<string>();

export function warnDeprecated(ruleName: string): void {
if (warnedDeprecations.has(ruleName)) return;
warnedDeprecations.add(ruleName);

// eslint-disable-next-line no-console -- Intentional deprecation warning
console.warn(`
DEPRECATION WARNING: complexity/${ruleName}

Use "complexity/complexity" instead for better performance:

"complexity/complexity": ["warn", {
"cyclomatic": 20,
"cognitive": 15
}]

See: ${MIGRATION_URL}
`);
}

/** JSON Schema properties shared by rules that support extraction analysis and breakdown tips. */
export const EXTRACTION_SCHEMA_PROPERTIES = {
extractionMultiplier: {
Expand All @@ -141,7 +30,7 @@ export const EXTRACTION_SCHEMA_PROPERTIES = {
},
enableExtraction: {
type: 'boolean',
description: 'Enable smart extraction suggestions for complex functions (default: false)',
description: 'Enable smart extraction suggestions for complex functions (default: true)',
},
nestingTipThreshold: {
type: 'integer',
Expand Down Expand Up @@ -171,7 +60,7 @@ export interface ParsedExtractionOptions {

export function parseExtractionOptions(options: ExtractionSchemaOptions): ParsedExtractionOptions {
return {
enableExtraction: options.enableExtraction ?? false,
enableExtraction: options.enableExtraction ?? true,
extractionOptions: {
minComplexityMultiplier: options.extractionMultiplier,
minComplexityPercentage: options.minExtractionPercentage,
Expand Down