From 9204a51b4a43541e5e59c6c7fb3a3da1e2d49c2b Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Sat, 13 Jan 2024 06:32:18 +1300 Subject: [PATCH] fix: replace use of deprecated methods (#1453) * fix: replace usage of deprecated `Context#getScope` * chore: define new `SourceCode` methods * fix: replace usage of deprecated `context` methods * fix: replace usage of deprecated `Context#getSourceCode` * fix: replace usage of deprecated `Context#getFilename` * test: ignore coverage on `context` helpers --- src/index.ts | 28 +++++++++- src/rules/expect-expect.ts | 6 ++- src/rules/no-commented-out-tests.ts | 4 +- src/rules/no-conditional-expect.ts | 3 +- src/rules/no-confusing-set-timeout.ts | 4 +- src/rules/no-disabled-tests.ts | 3 +- src/rules/no-done-callback.ts | 10 +++- src/rules/no-if.ts | 3 +- src/rules/no-jasmine-globals.ts | 3 +- src/rules/no-large-snapshots.ts | 3 +- src/rules/no-test-return-statement.ts | 3 +- src/rules/prefer-comparison-matcher.ts | 3 +- src/rules/prefer-equality-matcher.ts | 3 +- src/rules/prefer-mock-promise-shorthand.ts | 3 +- src/rules/prefer-spy-on.ts | 4 +- src/rules/prefer-to-contain.ts | 3 +- src/rules/utils/misc.ts | 62 +++++++++++++++++++++- src/rules/utils/parseJestFnCall.ts | 8 +-- 18 files changed, 131 insertions(+), 25 deletions(-) diff --git a/src/index.ts b/src/index.ts index f56603f16..f1956d1e2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import { readdirSync } from 'fs'; import { join, parse } from 'path'; -import type { TSESLint } from '@typescript-eslint/utils'; +import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; import { name as packageName, version as packageVersion, @@ -19,6 +19,32 @@ declare module '@typescript-eslint/utils/dist/ts-eslint/Rule' { } } +declare module '@typescript-eslint/utils/dist/ts-eslint/SourceCode' { + export interface SourceCode { + /** + * Returns the scope of the given node. + * This information can be used track references to variables. + * @since 8.37.0 + */ + getScope(node: TSESTree.Node): TSESLint.Scope.Scope; + /** + * Returns an array of the ancestors of the given node, starting at + * the root of the AST and continuing through the direct parent of the current node. + * This array does not include the currently-traversed node itself. + * @since 8.38.0 + */ + getAncestors(node: TSESTree.Node): TSESTree.Node[]; + /** + * Returns a list of variables declared by the given node. + * This information can be used to track references to variables. + * @since 8.38.0 + */ + getDeclaredVariables( + node: TSESTree.Node, + ): readonly TSESLint.Scope.Variable[]; + } +} + // copied from https://github.com/babel/babel/blob/d8da63c929f2d28c401571e2a43166678c555bc4/packages/babel-helpers/src/helpers.js#L602-L606 /* istanbul ignore next */ const interopRequireDefault = (obj: any): { default: any } => diff --git a/src/rules/expect-expect.ts b/src/rules/expect-expect.ts index 4fd07ef94..4bdd815cd 100644 --- a/src/rules/expect-expect.ts +++ b/src/rules/expect-expect.ts @@ -6,6 +6,8 @@ import { AST_NODE_TYPES, type TSESTree } from '@typescript-eslint/utils'; import { createRule, + getAncestors, + getDeclaredVariables, getNodeName, getTestCallExpressionsFromDeclaredVariables, isSupportedAccessor, @@ -94,7 +96,7 @@ export default createRule< : -1; if (node.type === AST_NODE_TYPES.FunctionDeclaration) { - const declaredVariables = context.getDeclaredVariables(node); + const declaredVariables = getDeclaredVariables(context, node); const testCallExpressions = getTestCallExpressionsFromDeclaredVariables( declaredVariables, @@ -129,7 +131,7 @@ export default createRule< unchecked.push(node); } else if (matchesAssertFunctionName(name, assertFunctionNames)) { // Return early in case of nested `it` statements. - checkCallExpressionUsed(context.getAncestors()); + checkCallExpressionUsed(getAncestors(context, node)); } }, 'Program:exit'() { diff --git a/src/rules/no-commented-out-tests.ts b/src/rules/no-commented-out-tests.ts index f933506b6..1949c3fc4 100644 --- a/src/rules/no-commented-out-tests.ts +++ b/src/rules/no-commented-out-tests.ts @@ -1,5 +1,5 @@ import type { TSESTree } from '@typescript-eslint/utils'; -import { createRule } from './utils'; +import { createRule, getSourceCode } from './utils'; function hasTests(node: TSESTree.Comment) { return /^\s*[xf]?(test|it|describe)(\.\w+|\[['"]\w+['"]\])?\s*\(/mu.test( @@ -23,7 +23,7 @@ export default createRule({ }, defaultOptions: [], create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); function checkNode(node: TSESTree.Comment) { if (!hasTests(node)) { diff --git a/src/rules/no-conditional-expect.ts b/src/rules/no-conditional-expect.ts index 53c939f34..519c23523 100644 --- a/src/rules/no-conditional-expect.ts +++ b/src/rules/no-conditional-expect.ts @@ -2,6 +2,7 @@ import { AST_NODE_TYPES, type TSESTree } from '@typescript-eslint/utils'; import { type KnownCallExpression, createRule, + getDeclaredVariables, getTestCallExpressionsFromDeclaredVariables, isSupportedAccessor, isTypeOfJestFnCall, @@ -39,7 +40,7 @@ export default createRule({ return { FunctionDeclaration(node) { - const declaredVariables = context.getDeclaredVariables(node); + const declaredVariables = getDeclaredVariables(context, node); const testCallExpressions = getTestCallExpressionsFromDeclaredVariables( declaredVariables, context, diff --git a/src/rules/no-confusing-set-timeout.ts b/src/rules/no-confusing-set-timeout.ts index c7cf42589..52bb29606 100644 --- a/src/rules/no-confusing-set-timeout.ts +++ b/src/rules/no-confusing-set-timeout.ts @@ -1,6 +1,7 @@ import { type ParsedJestFnCall, createRule, + getScope, isIdentifier, parseJestFnCall, } from './utils'; @@ -38,7 +39,6 @@ export default createRule({ return { CallExpression(node) { - const scope = context.getScope(); const jestFnCall = parseJestFnCall(node, context); if (!jestFnCall) { @@ -51,7 +51,7 @@ export default createRule({ return; } - if (!['global', 'module'].includes(scope.type)) { + if (!['global', 'module'].includes(getScope(context, node).type)) { context.report({ messageId: 'globalSetTimeout', node }); } diff --git a/src/rules/no-disabled-tests.ts b/src/rules/no-disabled-tests.ts index cb8d0c047..af0c7547e 100644 --- a/src/rules/no-disabled-tests.ts +++ b/src/rules/no-disabled-tests.ts @@ -1,6 +1,7 @@ import { createRule, getAccessorValue, + getScope, parseJestFnCall, resolveScope, } from './utils'; @@ -80,7 +81,7 @@ export default createRule({ } }, 'CallExpression[callee.name="pending"]'(node) { - if (resolveScope(context.getScope(), 'pending')) { + if (resolveScope(getScope(context, node), 'pending')) { return; } diff --git a/src/rules/no-done-callback.ts b/src/rules/no-done-callback.ts index 6ad6d5900..a227987c4 100644 --- a/src/rules/no-done-callback.ts +++ b/src/rules/no-done-callback.ts @@ -3,7 +3,13 @@ import { type TSESLint, type TSESTree, } from '@typescript-eslint/utils'; -import { createRule, getNodeName, isFunction, parseJestFnCall } from './utils'; +import { + createRule, + getNodeName, + getSourceCode, + isFunction, + parseJestFnCall, +} from './utils'; const findCallbackArg = ( node: TSESTree.CallExpression, @@ -104,7 +110,7 @@ export default createRule({ fix(fixer) { const { body, params } = callback; - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const firstBodyToken = sourceCode.getFirstToken(body); const lastBodyToken = sourceCode.getLastToken(body); diff --git a/src/rules/no-if.ts b/src/rules/no-if.ts index e42f9a97d..880c44b1c 100644 --- a/src/rules/no-if.ts +++ b/src/rules/no-if.ts @@ -3,6 +3,7 @@ import { TestCaseName, createRule, getAccessorValue, + getDeclaredVariables, getNodeName, getTestCallExpressionsFromDeclaredVariables, parseJestFnCall, @@ -89,7 +90,7 @@ export default createRule({ stack.push(isTestFunctionExpression(node)); }, FunctionDeclaration(node) { - const declaredVariables = context.getDeclaredVariables(node); + const declaredVariables = getDeclaredVariables(context, node); const testCallExpressions = getTestCallExpressionsFromDeclaredVariables( declaredVariables, context, diff --git a/src/rules/no-jasmine-globals.ts b/src/rules/no-jasmine-globals.ts index adf34604a..89c51ba30 100644 --- a/src/rules/no-jasmine-globals.ts +++ b/src/rules/no-jasmine-globals.ts @@ -2,6 +2,7 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, getNodeName, + getScope, isSupportedAccessor, resolveScope, } from './utils'; @@ -46,7 +47,7 @@ export default createRule({ calleeName === 'fail' || calleeName === 'pending' ) { - if (resolveScope(context.getScope(), calleeName)) { + if (resolveScope(getScope(context, node), calleeName)) { // It's a local variable, not a jasmine global. return; } diff --git a/src/rules/no-large-snapshots.ts b/src/rules/no-large-snapshots.ts index c9764c315..0f28dcfba 100644 --- a/src/rules/no-large-snapshots.ts +++ b/src/rules/no-large-snapshots.ts @@ -7,6 +7,7 @@ import { import { createRule, getAccessorValue, + getFilename, isSupportedAccessor, parseJestFnCall, } from './utils'; @@ -46,7 +47,7 @@ const reportOnViolation = ( node.expression.left.type === AST_NODE_TYPES.MemberExpression && isSupportedAccessor(node.expression.left.property) ) { - const fileName = context.getFilename(); + const fileName = getFilename(context); const allowedSnapshotsInFile = allowedSnapshots[fileName]; if (allowedSnapshotsInFile) { diff --git a/src/rules/no-test-return-statement.ts b/src/rules/no-test-return-statement.ts index 1efe214f9..252c76e67 100644 --- a/src/rules/no-test-return-statement.ts +++ b/src/rules/no-test-return-statement.ts @@ -1,6 +1,7 @@ import { AST_NODE_TYPES, type TSESTree } from '@typescript-eslint/utils'; import { createRule, + getDeclaredVariables, getTestCallExpressionsFromDeclaredVariables, isFunction, isTypeOfJestFnCall, @@ -54,7 +55,7 @@ export default createRule({ context.report({ messageId: 'noReturnValue', node: returnStmt }); }, FunctionDeclaration(node) { - const declaredVariables = context.getDeclaredVariables(node); + const declaredVariables = getDeclaredVariables(context, node); const testCallExpressions = getTestCallExpressionsFromDeclaredVariables( declaredVariables, context, diff --git a/src/rules/prefer-comparison-matcher.ts b/src/rules/prefer-comparison-matcher.ts index 80c3735e4..17b87ac4a 100644 --- a/src/rules/prefer-comparison-matcher.ts +++ b/src/rules/prefer-comparison-matcher.ts @@ -4,6 +4,7 @@ import { createRule, getAccessorValue, getFirstMatcherArg, + getSourceCode, isBooleanLiteral, isStringNode, parseJestFnCall, @@ -116,7 +117,7 @@ export default createRule({ context.report({ fix(fixer) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); // preserve the existing modifier if it's not a negation const modifierText = diff --git a/src/rules/prefer-equality-matcher.ts b/src/rules/prefer-equality-matcher.ts index 6e4f32c4f..18ed9abb1 100644 --- a/src/rules/prefer-equality-matcher.ts +++ b/src/rules/prefer-equality-matcher.ts @@ -5,6 +5,7 @@ import { createRule, getAccessorValue, getFirstMatcherArg, + getSourceCode, isBooleanLiteral, parseJestFnCall, } from './utils'; @@ -74,7 +75,7 @@ export default createRule({ const buildFixer = (equalityMatcher: string): TSESLint.ReportFixFunction => fixer => { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); // preserve the existing modifier if it's not a negation let modifierText = diff --git a/src/rules/prefer-mock-promise-shorthand.ts b/src/rules/prefer-mock-promise-shorthand.ts index 7248604ee..7007d816e 100644 --- a/src/rules/prefer-mock-promise-shorthand.ts +++ b/src/rules/prefer-mock-promise-shorthand.ts @@ -5,6 +5,7 @@ import { createRule, getAccessorValue, getNodeName, + getSourceCode, isFunction, isSupportedAccessor, } from './utils'; @@ -70,7 +71,7 @@ export default createRule({ messageId: 'useMockShorthand', data: { replacement }, fix(fixer) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); // there shouldn't be more than one argument, but if there is don't try // fixing since we have no idea what to do with the extra arguments diff --git a/src/rules/prefer-spy-on.ts b/src/rules/prefer-spy-on.ts index 36ca2a350..ae1648bf6 100644 --- a/src/rules/prefer-spy-on.ts +++ b/src/rules/prefer-spy-on.ts @@ -3,7 +3,7 @@ import { type TSESLint, type TSESTree, } from '@typescript-eslint/utils'; -import { createRule, getNodeName } from './utils'; +import { createRule, getNodeName, getSourceCode } from './utils'; const findNodeObject = ( node: TSESTree.CallExpression | TSESTree.MemberExpression, @@ -57,7 +57,7 @@ const getAutoFixMockImplementation = ( } const [arg] = jestFnCall.arguments; - const argSource = arg && context.getSourceCode().getText(arg); + const argSource = arg && getSourceCode(context).getText(arg); return argSource ? `.mockImplementation(${argSource})` diff --git a/src/rules/prefer-to-contain.ts b/src/rules/prefer-to-contain.ts index d5e2c4dc9..2e4f7e0ca 100644 --- a/src/rules/prefer-to-contain.ts +++ b/src/rules/prefer-to-contain.ts @@ -7,6 +7,7 @@ import { createRule, getAccessorValue, getFirstMatcherArg, + getSourceCode, hasOnlyOneArgument, isBooleanLiteral, isSupportedAccessor, @@ -89,7 +90,7 @@ export default createRule({ context.report({ fix(fixer) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); // we need to negate the expectation if the current expected // value is itself negated by the "not" modifier diff --git a/src/rules/utils/misc.ts b/src/rules/utils/misc.ts index fdab32a4f..9e025fcf5 100644 --- a/src/rules/utils/misc.ts +++ b/src/rules/utils/misc.ts @@ -178,7 +178,7 @@ export const removeExtraArgumentsFixer = ( const firstArg = func.arguments[from]; const lastArg = func.arguments[func.arguments.length - 1]; - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); let tokenAfterLastParam = sourceCode.getTokenAfter(lastArg)!; if (tokenAfterLastParam.value === ',') { @@ -229,3 +229,63 @@ export const getFirstMatcherArg = ( return followTypeAssertionChain(firstArg); }; + +/* istanbul ignore next */ +export const getFilename = ( + context: TSESLint.RuleContext, +) => { + return 'filename' in context + ? (context.filename as string) + : context.getFilename(); +}; + +/* istanbul ignore next */ +export const getSourceCode = ( + context: TSESLint.RuleContext, +) => { + return 'sourceCode' in context + ? (context.sourceCode as TSESLint.SourceCode) + : context.getSourceCode(); +}; + +/* istanbul ignore next */ +export const getScope = ( + context: TSESLint.RuleContext, + node: TSESTree.Node, +) => { + const sourceCode = getSourceCode(context); + + if ('getScope' in sourceCode) { + return sourceCode.getScope(node); + } + + return context.getScope(); +}; + +/* istanbul ignore next */ +export const getAncestors = ( + context: TSESLint.RuleContext, + node: TSESTree.Node, +) => { + const sourceCode = getSourceCode(context); + + if ('getAncestors' in sourceCode) { + return sourceCode.getAncestors(node); + } + + return context.getAncestors(); +}; + +/* istanbul ignore next */ +export const getDeclaredVariables = ( + context: TSESLint.RuleContext, + node: TSESTree.Node, +) => { + const sourceCode = getSourceCode(context); + + if ('getDeclaredVariables' in sourceCode) { + return sourceCode.getDeclaredVariables(node); + } + + return context.getDeclaredVariables(node); +}; diff --git a/src/rules/utils/parseJestFnCall.ts b/src/rules/utils/parseJestFnCall.ts index 26ed1303d..63a43eda3 100644 --- a/src/rules/utils/parseJestFnCall.ts +++ b/src/rules/utils/parseJestFnCall.ts @@ -12,6 +12,7 @@ import { TestCaseName, findTopMostCallExpression, getAccessorValue, + getScope, getStringValue, isIdentifier, isStringNode, @@ -257,7 +258,7 @@ const parseJestFnCallWithReasonInner = ( return null; } - const resolved = resolveToJestFn(context, getAccessorValue(first)); + const resolved = resolveToJestFn(context, first); // we're not a jest function if (!resolved) { @@ -553,9 +554,10 @@ interface ResolvedJestFn { const resolveToJestFn = ( context: TSESLint.RuleContext, - identifier: string, + accessor: AccessorNode, ): ResolvedJestFn | null => { - const maybeImport = resolveScope(context.getScope(), identifier); + const identifier = getAccessorValue(accessor); + const maybeImport = resolveScope(getScope(context, accessor), identifier); // the identifier was found as a local variable or function declaration // meaning it's not a function from jest