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
3 changes: 2 additions & 1 deletion rules/max-function-length.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Rule } from 'eslint';
import { MaxFunctionLengthOptions } from '../types/rule-options.js';
import { createRuleMeta, RULE_CATEGORIES } from '../utils/rule-meta.js';
import { getSourceCode } from '../utils/node-helpers.js';

const rule: Rule.RuleModule = {
meta: createRuleMeta('max-function-length', {
Expand Down Expand Up @@ -44,7 +45,7 @@ Long functions are hard to understand and test. Consider:
const skipBlankLines = options.skipBlankLines !== undefined ? options.skipBlankLines : true;
const skipComments = options.skipComments !== undefined ? options.skipComments : true;

const sourceCode = context.getSourceCode();
const sourceCode = getSourceCode(context);

function countLines(node: any): number {
const body = node.body;
Expand Down
5 changes: 3 additions & 2 deletions rules/no-complex-conditionals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { Rule } from 'eslint';
import { ComplexConditionalsOptions } from '../types/rule-options.js';
import { createRuleMeta, RULE_CATEGORIES } from '../utils/rule-meta.js';
import { getSourceCode } from '../utils/node-helpers.js';

const rule: Rule.RuleModule = {
meta: createRuleMeta('no-complex-conditionals', {
Expand Down Expand Up @@ -56,7 +57,7 @@ Refactoring suggestions:

// Suggest a name based on context
const suggestName = (node: any): string => {
const sourceCode = context.getSourceCode();
const sourceCode = getSourceCode(context);
const text = sourceCode.getText(node).toLowerCase();

if (text.includes('valid') || text.includes('invalid')) return 'isValid';
Expand All @@ -72,7 +73,7 @@ Refactoring suggestions:

// Format condition breakdown for message
const formatConditionBreakdown = (node: any): string => {
const sourceCode = context.getSourceCode();
const sourceCode = getSourceCode(context);

// Split by operators to show each part
const parts: string[] = [];
Expand Down
73 changes: 73 additions & 0 deletions tests/eslint10-compatibility.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* @fileoverview Regression tests for ESLint 10 compatibility
*
* ESLint 10 removed the deprecated `context.getSourceCode()` method, so rules
* must read the `context.sourceCode` property instead. These tests drive the
* rules with a context that only exposes the property, which is what ESLint 10
* hands to a rule.
*
* @see https://github.com/aryelu/eslint-plugin-code-complete/issues/14
*/

import { describe, it, expect } from 'vitest';
import { Rule, SourceCode } from 'eslint';
import * as parser from '@typescript-eslint/parser';

import noComplexConditionals from '../rules/no-complex-conditionals';
import maxFunctionLength from '../rules/max-function-length';

/**
* Builds a rule context that mimics ESLint 10: `sourceCode` is present and
* `getSourceCode` does not exist at all.
*/
function createEslint10Context(code: string, options: unknown[] = []) {
const { ast } = parser.parseForESLint(code, {
loc: true,
range: true,
tokens: true,
comment: true
});

const sourceCode = new SourceCode({ text: code, ast: ast as any });
const reports: any[] = [];

const context = {
options,
sourceCode,
report: (descriptor: any) => reports.push(descriptor)
} as unknown as Rule.RuleContext;

expect((context as any).getSourceCode).toBeUndefined();

return { ast, context, reports };
}

describe('ESLint 10 compatibility', () => {
it('no-complex-conditionals reports without context.getSourceCode', () => {
const { ast, context, reports } = createEslint10Context('if (a && b && c && d) {}');

const listener = noComplexConditionals.create(context);
(listener.IfStatement as any)(ast.body[0]);

expect(reports).toHaveLength(1);
expect(reports[0].messageId).toBe('complexConditional');
expect(reports[0].data.conditionBreakdown).toContain('1. a');
expect(reports[0].data.suggestedName).toBe('shouldProceed');
});

it('max-function-length reports without context.getSourceCode', () => {
const body = Array.from({ length: 5 }, (_unused, index) => ` const value${index} = ${index};`).join('\n');
const { ast, context, reports } = createEslint10Context(
`function longFunction() {\n${body}\n}`,
[{ maxLines: 3 }]
);

const listener = maxFunctionLength.create(context);
(listener.FunctionDeclaration as any)(ast.body[0]);

expect(reports).toHaveLength(1);
expect(reports[0].messageId).toBe('maxFunctionLength');
expect(reports[0].data.name).toBe('longFunction');
expect(reports[0].data.lines).toBe('5');
});
});
17 changes: 16 additions & 1 deletion utils/node-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* @author eslint-plugin-code-complete
*/

import type { Rule, SourceCode } from 'eslint';

/**
* Checks if a parameter name starts with an allowed prefix
* @param {string} name - Parameter name
Expand Down Expand Up @@ -226,4 +228,17 @@ export function countFunctionParameters(
}

return count;
}
}
/**
* Gets the SourceCode object for the current lint run.
*
* ESLint 10 removed the deprecated `context.getSourceCode()` method in favour of
* the `context.sourceCode` property, so prefer the property and only fall back to
* the method for older versions that predate it.
*
* @param {Rule.RuleContext} context - The rule context
* @returns {SourceCode} - The source code object
*/
export function getSourceCode(context: Rule.RuleContext): SourceCode {
return context.sourceCode ?? context.getSourceCode();
}
Loading