116 lines
3.7 KiB
JavaScript
116 lines
3.7 KiB
JavaScript
import { UnreachableCaseError } from './unreachable-case-error.js'
|
|
import { getEslintDisabledRules } from './get-eslint-disabled-rules.js'
|
|
/**
|
|
* Determines which lines have the specified ESLint rule disabled via comments.
|
|
*
|
|
* Parses ESLint disable comments in the source code to identify lines where a
|
|
* specific rule should not be enforced. Handles all ESLint disable directives:
|
|
*
|
|
* - `eslint-disable-next-line` - Disables the rule for the next line
|
|
* - `eslint-disable-line` - Disables the rule for the current line
|
|
* - `eslint-disable` - Disables the rule from this point forward
|
|
* - `eslint-enable` - Re-enables the rule after a previous disable.
|
|
*
|
|
* The function correctly handles:
|
|
*
|
|
* - Rule-specific disables (e.g., `eslint-disable-next-line rule-name`)
|
|
* - Global disables (e.g., `eslint-disable-next-line` without specific rules)
|
|
* - Nested disable/enable pairs.
|
|
*
|
|
* @example
|
|
*
|
|
* ```ts
|
|
* // Source code with disable comments:
|
|
* // eslint-disable-next-line perfectionist/sort-imports
|
|
* import { z } from 'zod'
|
|
* import { a } from 'a'
|
|
*
|
|
* // eslint-disable perfectionist/sort-imports
|
|
* import { y } from 'y'
|
|
* import { b } from 'b'
|
|
* // eslint-enable perfectionist/sort-imports
|
|
*
|
|
* getEslintDisabledLines({
|
|
* sourceCode,
|
|
* ruleName: 'perfectionist/sort-imports',
|
|
* })
|
|
* // Returns: [2, 5, 6] (lines where the rule is disabled)
|
|
* ```
|
|
*
|
|
* @param props - Configuration object.
|
|
* @param props.sourceCode - ESLint source code object containing comments.
|
|
* @param props.ruleName - Name of the rule to check for disable directives.
|
|
* @returns Array of line numbers (1-indexed) where the rule is disabled.
|
|
*/
|
|
function getEslintDisabledLines(props) {
|
|
let { sourceCode, ruleName } = props
|
|
let returnValue = []
|
|
let lineRulePermanentlyDisabled = null
|
|
for (let comment of sourceCode.getAllComments()) {
|
|
let eslintDisabledRules = getEslintDisabledRules(comment.value)
|
|
if (!eslintDisabledRules) {
|
|
continue
|
|
}
|
|
/* v8 ignore next 3 -- @preserve Hard to test this false branch. */
|
|
if (
|
|
!(
|
|
eslintDisabledRules.rules === 'all' ||
|
|
eslintDisabledRules.rules.includes(ruleName)
|
|
)
|
|
) {
|
|
continue
|
|
}
|
|
switch (eslintDisabledRules.eslintDisableDirective) {
|
|
case 'eslint-disable-next-line':
|
|
returnValue.push(comment.loc.end.line + 1)
|
|
continue
|
|
case 'eslint-disable-line':
|
|
returnValue.push(comment.loc.start.line)
|
|
continue
|
|
case 'eslint-disable':
|
|
lineRulePermanentlyDisabled ??= comment.loc.start.line
|
|
break
|
|
case 'eslint-enable':
|
|
/* v8 ignore next -- @preserve Hard to cover in test without raising another ESLint error. */
|
|
if (!lineRulePermanentlyDisabled) {
|
|
continue
|
|
}
|
|
returnValue.push(
|
|
...createArrayFromTo(
|
|
lineRulePermanentlyDisabled + 1,
|
|
comment.loc.start.line,
|
|
),
|
|
)
|
|
lineRulePermanentlyDisabled = null
|
|
break
|
|
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
|
default:
|
|
throw new UnreachableCaseError(
|
|
eslintDisabledRules.eslintDisableDirective,
|
|
)
|
|
}
|
|
}
|
|
return returnValue
|
|
}
|
|
/**
|
|
* Creates an array of consecutive integers from start to end (inclusive).
|
|
*
|
|
* Helper function to generate an array of line numbers for ranges disabled by
|
|
* eslint-disable/eslint-enable comment pairs.
|
|
*
|
|
* @example
|
|
*
|
|
* ```ts
|
|
* createArrayFromTo(5, 8)
|
|
* // Returns: [5, 6, 7, 8]
|
|
* ```
|
|
*
|
|
* @param i - Starting number (inclusive).
|
|
* @param index - Ending number (inclusive).
|
|
* @returns Array of consecutive integers from i to index.
|
|
*/
|
|
function createArrayFromTo(i, index) {
|
|
return Array.from({ length: index - i + 1 }, (_, item) => i + item)
|
|
}
|
|
export { getEslintDisabledLines }
|