diff --git a/.eslintrc.json b/.eslintrc.json index 12616a7d2..799a4a33f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -39,6 +39,7 @@ "@typescript-eslint/no-unsafe-member-access": "off", "@typescript-eslint/no-unsafe-return": "off", "import/no-relative-parent-imports": "error", + "functional/prefer-readonly-type": "off", "node/no-unsupported-features/es-builtins": "off", "node/no-unsupported-features/es-syntax": "off", "promise/prefer-await-to-callbacks": "off", @@ -57,7 +58,37 @@ { "files": ["./src/**/*"], "extends": ["plugin:eslint-plugin/recommended"], - "rules": {} + "rules": { + "functional/no-expression-statement": "error" + } + }, + // Rule util file. + { + "files": ["./src/util/rule.ts"], + "rules": { + "@typescript-eslint/prefer-readonly-parameter-types": "warn" + } + }, + // Typeguard files. + { + "files": ["./src/util/typeguard.ts"], + "rules": { + "jsdoc/require-jsdoc": "off" + } + }, + // Test files. + { + "files": ["./tests/**/*"], + "rules": { + "jsdoc/require-jsdoc": "off" + } + }, + // CZ Adapter files. + { + "files": ["./cz-adapter/**/*"], + "rules": { + "jsdoc/require-jsdoc": "off" + } }, // FIXME: This override is defined in the upsteam; it shouldn't need to be redefined here. Why? { diff --git a/cz-adapter/engine.ts b/cz-adapter/engine.ts index 68f6f111e..e6f7c52f5 100644 --- a/cz-adapter/engine.ts +++ b/cz-adapter/engine.ts @@ -5,16 +5,16 @@ import { rules } from "~/rules"; import type { Options } from "./options"; -type Answers = { - readonly type: string; - readonly scope?: string; - readonly scopeRules?: string; - readonly subject: string; - readonly body?: string; - readonly isBreaking: boolean; - readonly isIssueAffected: boolean; - readonly issues?: string; -}; +type Answers = Readonly<{ + type: string; + scope?: string; + scopeRules?: string; + subject: string; + body?: string; + isBreaking: boolean; + isIssueAffected: boolean; + issues?: string; +}>; type CZ = any; diff --git a/cz-adapter/options.ts b/cz-adapter/options.ts index 1059d45fe..d788c6e49 100644 --- a/cz-adapter/options.ts +++ b/cz-adapter/options.ts @@ -1,4 +1,5 @@ import { types as conventionalCommitTypes } from "conventional-commit-types"; +import type { ReadonlyDeep } from "type-fest"; // Override the descriptions of some of the types. const types = { @@ -48,13 +49,13 @@ const types = { }, }; -const defaults: { - readonly defaultType: string | undefined; - readonly defaultScope: string | undefined; - readonly defaultSubject: string | undefined; - readonly defaultBody: string | undefined; - readonly defaultIssues: string | undefined; -} = { +const defaults: Readonly<{ + defaultType: string | undefined; + defaultScope: string | undefined; + defaultSubject: string | undefined; + defaultBody: string | undefined; + defaultIssues: string | undefined; +}> = { defaultType: process.env.CZ_TYPE, defaultScope: process.env.CZ_SCOPE, defaultSubject: process.env.CZ_SUBJECT, @@ -71,6 +72,6 @@ const options = { maxLineWidth: 100, }; -export type Options = typeof options; +export type Options = ReadonlyDeep; export default options; diff --git a/docs/rules/functional-parameters.md b/docs/rules/functional-parameters.md index e4def09e5..52c521407 100644 --- a/docs/rules/functional-parameters.md +++ b/docs/rules/functional-parameters.md @@ -112,7 +112,7 @@ Any function that take takes multiple parameter can be rewritten as a higher-ord Example: - + ```js // This function diff --git a/package.json b/package.json index 180138dd2..4b97a96a7 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "verify": "yarn build && yarn lint && yarn build-tests && yarn test-compiled && rimraf build" }, "dependencies": { - "@typescript-eslint/experimental-utils": "^5.0.0", + "@typescript-eslint/utils": "^5.10.0", "deepmerge-ts": "^2.0.1", "escape-string-regexp": "^4.0.0" }, @@ -71,7 +71,7 @@ "@commitlint/config-conventional": "^14.1.0", "@google/semantic-release-replace-plugin": "^1.1.0", "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@rebeccastevens/eslint-config": "^1.1.5", + "@rebeccastevens/eslint-config": "^1.2.1", "@rollup/plugin-commonjs": "^21.0.0", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.0.5", @@ -87,8 +87,8 @@ "@types/estree": "^0.0.50", "@types/node": "16.11.0", "@types/rollup-plugin-auto-external": "^2.0.2", - "@typescript-eslint/eslint-plugin": "^5.0.0", - "@typescript-eslint/parser": "^5.0.0", + "@typescript-eslint/eslint-plugin": "^5.10.0", + "@typescript-eslint/parser": "^5.10.0", "ava": "^3.15.0", "babel-eslint": "^10.1.0", "chalk": "^4.1.2", @@ -131,6 +131,7 @@ "tsconfig-paths": "^3.10.1", "tslib": "^2.0.3", "tsutils": "^3.21.0", + "type-fest": "^2.9.0", "typescript": "^4.4.4", "word-wrap": "^1.2.3" }, diff --git a/src/common/ignore-options.ts b/src/common/ignore-options.ts index 227091f30..2759d7205 100644 --- a/src/common/ignore-options.ts +++ b/src/common/ignore-options.ts @@ -1,8 +1,9 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { TSESLint, TSESTree } from "@typescript-eslint/utils"; import escapeRegExp from "escape-string-regexp"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; -import type { BaseOptions, RuleContext } from "~/util/rule"; +import type { BaseOptions } from "~/util/rule"; import { getKeyOfValueInObjectExpression, inClass, @@ -30,96 +31,106 @@ import { isVariableDeclaration, } from "~/util/typeguard"; +/** + * The option to allow local mutations. + */ export type AllowLocalMutationOption = { readonly allowLocalMutation: boolean; }; -export const allowLocalMutationOptionSchema: JSONSchema4 = { - type: "object", - properties: { - allowLocalMutation: { - type: "boolean", - }, +/** + * The schema for the option to allow local mutations. + */ +export const allowLocalMutationOptionSchema: JSONSchema4["properties"] = { + allowLocalMutation: { + type: "boolean", }, - additionalProperties: false, }; +/** + * The option to ignore patterns. + */ export type IgnorePatternOption = { readonly ignorePattern?: ReadonlyArray | string; }; -export const ignorePatternOptionSchema: JSONSchema4 = { - type: "object", - properties: { - ignorePattern: { - type: ["string", "array"], - items: { - type: "string", - }, +/** + * The schema for the option to ignore patterns. + */ +export const ignorePatternOptionSchema: JSONSchema4["properties"] = { + ignorePattern: { + type: ["string", "array"], + items: { + type: "string", }, }, - additionalProperties: false, }; +/** + * The option to ignore accessor patterns. + */ export type IgnoreAccessorPatternOption = { readonly ignoreAccessorPattern?: ReadonlyArray | string; }; -export const ignoreAccessorPatternOptionSchema: JSONSchema4 = { - type: "object", - properties: { - ignoreAccessorPattern: { - type: ["string", "array"], - items: { - type: "string", - }, +/** + * The schema for the option to ignore accessor patterns. + */ +export const ignoreAccessorPatternOptionSchema: JSONSchema4["properties"] = { + ignoreAccessorPattern: { + type: ["string", "array"], + items: { + type: "string", }, }, - additionalProperties: false, }; +/** + * The option to ignore classes. + */ export type IgnoreClassOption = { readonly ignoreClass: boolean | "fieldsOnly"; }; -export const ignoreClassOptionSchema: JSONSchema4 = { - type: "object", - properties: { - ignoreClass: { - oneOf: [ - { - type: "boolean", - }, - { - type: "string", - enum: ["fieldsOnly"], - }, - ], - }, +/** + * The schema for the option to ignore classes. + */ +export const ignoreClassOptionSchema: JSONSchema4["properties"] = { + ignoreClass: { + oneOf: [ + { + type: "boolean", + }, + { + type: "string", + enum: ["fieldsOnly"], + }, + ], }, - additionalProperties: false, }; +/** + * The option to ignore interfaces. + */ export type IgnoreInterfaceOption = { readonly ignoreInterface: boolean; }; -export const ignoreInterfaceOptionSchema: JSONSchema4 = { - type: "object", - properties: { - ignoreInterface: { - type: "boolean", - }, +/** + * The schema for the option to ignore interfaces. + */ +export const ignoreInterfaceOptionSchema: JSONSchema4["properties"] = { + ignoreInterface: { + type: "boolean", }, - additionalProperties: false, }; /** * Get the identifier text of the given node. */ function getNodeIdentifierText( - node: TSESTree.Node | null | undefined, - context: RuleContext + node: ReadonlyDeep | null | undefined, + context: ReadonlyDeep> ): string | undefined { if (!isDefined(node)) { return undefined; @@ -143,7 +154,7 @@ function getNodeIdentifierText( : isUnaryExpression(node) ? getNodeIdentifierText(node.argument, context) : isExpressionStatement(node) - ? context.getSourceCode().getText(node) + ? context.getSourceCode().getText(node as TSESTree.Node) : isTSArrayType(node) || isTSIndexSignature(node) || isTSTupleType(node) || @@ -169,8 +180,8 @@ function getNodeIdentifierText( * Get all the identifier texts of the given node. */ function getNodeIdentifierTexts( - node: TSESTree.Node, - context: RuleContext + node: ReadonlyDeep, + context: ReadonlyDeep> ): ReadonlyArray { return ( isVariableDeclaration(node) @@ -270,11 +281,11 @@ function shouldIgnoreViaAccessorPattern( * - AllowLocalMutationOption. */ export function shouldIgnoreLocalMutation( - node: TSESTree.Node, - context: RuleContext, - options: Partial + node: ReadonlyDeep, + context: ReadonlyDeep>, + { allowLocalMutation }: Partial ): boolean { - return options.allowLocalMutation === true && inFunctionBody(node); + return allowLocalMutation === true && inFunctionBody(node); } /** @@ -283,13 +294,13 @@ export function shouldIgnoreLocalMutation( * - IgnoreClassOption. */ export function shouldIgnoreClass( - node: TSESTree.Node, - context: RuleContext, - options: Partial + node: ReadonlyDeep, + context: ReadonlyDeep>, + { ignoreClass }: Partial ): boolean { return ( - (options.ignoreClass === true && inClass(node)) || - (options.ignoreClass === "fieldsOnly" && + (ignoreClass === true && inClass(node)) || + (ignoreClass === "fieldsOnly" && (isPropertyDefinition(node) || (isAssignmentExpression(node) && inClass(node) && @@ -304,11 +315,11 @@ export function shouldIgnoreClass( * - IgnoreInterfaceOption. */ export function shouldIgnoreInterface( - node: TSESTree.Node, - context: RuleContext, - options: Partial + node: ReadonlyDeep, + context: ReadonlyDeep>, + { ignoreInterface }: Partial ): boolean { - return options.ignoreInterface === true && inInterface(node); + return ignoreInterface === true && inInterface(node); } /** @@ -318,9 +329,12 @@ export function shouldIgnoreInterface( * - IgnorePatternOption. */ export function shouldIgnorePattern( - node: TSESTree.Node, - context: RuleContext, - options: Partial + node: ReadonlyDeep, + context: ReadonlyDeep>, + { + ignorePattern, + ignoreAccessorPattern, + }: Partial ): boolean { const texts = getNodeIdentifierTexts(node, context); @@ -330,14 +344,12 @@ export function shouldIgnorePattern( return ( // Ignore if ignorePattern is set and a pattern matches. - (options.ignorePattern !== undefined && - texts.every((text) => - shouldIgnoreViaPattern(text, options.ignorePattern!) - )) || + (ignorePattern !== undefined && + texts.every((text) => shouldIgnoreViaPattern(text, ignorePattern))) || // Ignore if ignoreAccessorPattern is set and an accessor pattern matches. - (options.ignoreAccessorPattern !== undefined && + (ignoreAccessorPattern !== undefined && texts.every((text) => - shouldIgnoreViaAccessorPattern(text, options.ignoreAccessorPattern!) + shouldIgnoreViaAccessorPattern(text, ignoreAccessorPattern) )) ); } diff --git a/src/configs/off.ts b/src/configs/off.ts index 7fa6a594d..bf3bda5a1 100644 --- a/src/configs/off.ts +++ b/src/configs/off.ts @@ -2,6 +2,9 @@ import type { Linter } from "eslint"; import all from "./all"; +/** + * Turn the given rules off. + */ function turnRulesOff(rules: ReadonlyArray): Linter.Config["rules"] { return rules === undefined ? undefined diff --git a/src/index.ts b/src/index.ts index 9fcf255bb..7a5541946 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,9 +13,12 @@ import off from "~/configs/off"; import stylistic from "~/configs/stylistic"; import { rules } from "~/rules"; +/** + * The config type object ESLint is expecting. + */ type EslintPluginConfig = { - readonly rules: Record; - readonly configs: Record; + rules: Record; + configs: Record; }; const config: EslintPluginConfig = { diff --git a/src/rules/functional-parameters.ts b/src/rules/functional-parameters.ts index da5aa9e52..b21f53a7b 100644 --- a/src/rules/functional-parameters.ts +++ b/src/rules/functional-parameters.ts @@ -1,40 +1,53 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; import { deepmerge } from "deepmerge-ts"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; import type { IgnorePatternOption } from "~/common/ignore-options"; import { shouldIgnorePattern, ignorePatternOptionSchema, } from "~/common/ignore-options"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule } from "~/util/rule"; import { isIIFE, isPropertyAccess, isPropertyName } from "~/util/tree"; import { isRestElement } from "~/util/typeguard"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "functional-parameters" as const; +/** + * The parameter count options this rule can take. + */ type ParameterCountOptions = "atLeastOne" | "exactlyOne"; -// The options this rule can take. -type Options = IgnorePatternOption & { - readonly allowRestParameter: boolean; - readonly allowArgumentsKeyword: boolean; - readonly enforceParameterCount: - | ParameterCountOptions - | false - | { - readonly count: ParameterCountOptions; - readonly ignoreIIFE: boolean; - }; -}; +/** + * The options this rule can take. + */ +type Options = readonly [ + IgnorePatternOption & + Readonly<{ + allowRestParameter: boolean; + allowArgumentsKeyword: boolean; + enforceParameterCount: + | ParameterCountOptions + | false + | Readonly<{ + count: ParameterCountOptions; + ignoreIIFE: boolean; + }>; + }> +]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = [ - deepmerge(ignorePatternOptionSchema, { + { type: "object", - properties: { + properties: deepmerge(ignorePatternOptionSchema, { allowRestParameter: { type: "boolean", }, @@ -66,22 +79,28 @@ const schema: JSONSchema4 = [ }, ], }, - }, + }), additionalProperties: false, - }), + }, ]; -// The default options for the rule. -const defaultOptions: Options = { - allowRestParameter: false, - allowArgumentsKeyword: false, - enforceParameterCount: { - count: "atLeastOne", - ignoreIIFE: true, +/** + * The default options for the rule. + */ +const defaultOptions: Options = [ + { + allowRestParameter: false, + allowArgumentsKeyword: false, + enforceParameterCount: { + count: "atLeastOne", + ignoreIIFE: true, + }, }, -}; +]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { restParam: "Unexpected rest parameter. Use a regular parameter of type array instead.", @@ -91,8 +110,10 @@ const errorMessages = { paramCountExactlyOne: "Functions must have exactly one parameter.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: "Enforce functional parameters.", @@ -106,11 +127,11 @@ const meta: RuleMetaData = { * Get the rest parameter violations. */ function getRestParamViolations( - allowRestParameter: Options["allowRestParameter"], + [{ allowRestParameter }]: Options, node: - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep ): RuleResult["descriptors"] { return !allowRestParameter && node.params.length > 0 && @@ -128,11 +149,11 @@ function getRestParamViolations( * Get the parameter count violations. */ function getParamCountViolations( - enforceParameterCount: Options["enforceParameterCount"], + [{ enforceParameterCount }]: Options, node: - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep ): RuleResult["descriptors"] { if ( enforceParameterCount === false || @@ -177,13 +198,17 @@ function getParamCountViolations( */ function checkFunction( node: - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression, - context: RuleContext, + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { - if (shouldIgnorePattern(node, context, options)) { + const [optionsObject] = options; + + if (shouldIgnorePattern(node, context, optionsObject)) { return { context, descriptors: [], @@ -193,8 +218,8 @@ function checkFunction( return { context, descriptors: [ - ...getRestParamViolations(options.allowRestParameter, node), - ...getParamCountViolations(options.enforceParameterCount, node), + ...getRestParamViolations(options, node), + ...getParamCountViolations(options, node), ], }; } @@ -203,21 +228,27 @@ function checkFunction( * Check if the given identifier is for the "arguments" keyword. */ function checkIdentifier( - node: TSESTree.Identifier, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { - if (shouldIgnorePattern(node, context, options)) { + const [optionsObject] = options; + + if (shouldIgnorePattern(node, context, optionsObject)) { return { context, descriptors: [], }; } + const { allowArgumentsKeyword } = optionsObject; + return { context, descriptors: - !options.allowArgumentsKeyword && + !allowArgumentsKeyword && node.name === "arguments" && !isPropertyName(node) && !isPropertyAccess(node) diff --git a/src/rules/immutable-data.ts b/src/rules/immutable-data.ts index 4096544e9..afe5a2ecf 100644 --- a/src/rules/immutable-data.ts +++ b/src/rules/immutable-data.ts @@ -1,6 +1,7 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; import { deepmerge } from "deepmerge-ts"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; import type { IgnoreAccessorPatternOption, @@ -16,7 +17,7 @@ import { } from "~/common/ignore-options"; import { isExpected } from "~/util/misc"; import { createRule, getTypeOfNode } from "~/util/rule"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { inConstructor } from "~/util/tree"; import { isArrayConstructorType, @@ -29,31 +30,40 @@ import { isObjectConstructorType, } from "~/util/typeguard"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "immutable-data" as const; -// The options this rule can take. -type Options = IgnoreAccessorPatternOption & - IgnoreClassOption & - IgnorePatternOption & { - readonly ignoreImmediateMutation: boolean; - readonly assumeTypes: - | boolean - | { - readonly forArrays: boolean; - readonly forObjects: boolean; - }; - }; +/** + * The options this rule can take. + */ +type Options = readonly [ + IgnoreAccessorPatternOption & + IgnoreClassOption & + IgnorePatternOption & + Readonly<{ + ignoreImmediateMutation: boolean; + assumeTypes: + | boolean + | Readonly<{ + forArrays: boolean; + forObjects: boolean; + }>; + }> +]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = [ - deepmerge( - ignorePatternOptionSchema, - ignoreAccessorPatternOptionSchema, - ignoreClassOptionSchema, - { - type: "object", - properties: { + { + type: "object", + properties: deepmerge( + ignorePatternOptionSchema, + ignoreAccessorPatternOptionSchema, + ignoreClassOptionSchema, + { ignoreImmediateMutation: { type: "boolean", }, @@ -76,31 +86,39 @@ const schema: JSONSchema4 = [ }, ], }, - }, - additionalProperties: false, - } - ), + } + ), + additionalProperties: false, + }, ]; -// The default options for the rule. -const defaultOptions: Options = { - ignoreClass: false, - ignoreImmediateMutation: true, - assumeTypes: { - forArrays: true, - forObjects: true, +/** + * The default options for the rule. + */ +const defaultOptions: Options = [ + { + ignoreClass: false, + ignoreImmediateMutation: true, + assumeTypes: { + forArrays: true, + forObjects: true, + }, }, -}; +]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { generic: "Modifying an existing object/array is not allowed.", object: "Modifying properties of existing object not allowed.", array: "Modifying an array is not allowed.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: "Enforce treating data as immutable.", @@ -165,14 +183,18 @@ const objectConstructorMutatorFunctions = new Set([ * Check if the given assignment expression violates this rule. */ function checkAssignmentExpression( - node: TSESTree.AssignmentExpression, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [optionsObject] = options; + if ( !isMemberExpression(node.left) || - shouldIgnoreClass(node, context, options) || - shouldIgnorePattern(node, context, options) + shouldIgnoreClass(node, context, optionsObject) || + shouldIgnorePattern(node, context, optionsObject) ) { return { context, @@ -192,14 +214,18 @@ function checkAssignmentExpression( * Check if the given node violates this rule. */ function checkUnaryExpression( - node: TSESTree.UnaryExpression, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [optionsObject] = options; + if ( !isMemberExpression(node.argument) || - shouldIgnoreClass(node, context, options) || - shouldIgnorePattern(node, context, options) + shouldIgnoreClass(node, context, optionsObject) || + shouldIgnorePattern(node, context, optionsObject) ) { return { context, @@ -218,14 +244,18 @@ function checkUnaryExpression( * Check if the given node violates this rule. */ function checkUpdateExpression( - node: TSESTree.UpdateExpression, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [optionsObject] = options; + if ( !isMemberExpression(node.argument) || - shouldIgnoreClass(node.argument, context, options) || - shouldIgnorePattern(node.argument, context, options) + shouldIgnoreClass(node.argument, context, optionsObject) || + shouldIgnorePattern(node.argument, context, optionsObject) ) { return { context, @@ -247,8 +277,10 @@ function checkUpdateExpression( * a mutator method call. */ function isInChainCallAndFollowsNew( - node: TSESTree.MemberExpression, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, assumeArrayTypes: boolean ): boolean { return ( @@ -285,16 +317,20 @@ function isInChainCallAndFollowsNew( * Check if the given node violates this rule. */ function checkCallExpression( - node: TSESTree.CallExpression, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [optionsObject] = options; + // Not potential object mutation? if ( !isMemberExpression(node.callee) || !isIdentifier(node.callee.property) || - shouldIgnoreClass(node.callee.object, context, options) || - shouldIgnorePattern(node.callee.object, context, options) + shouldIgnoreClass(node.callee.object, context, optionsObject) || + shouldIgnorePattern(node.callee.object, context, optionsObject) ) { return { context, @@ -302,14 +338,16 @@ function checkCallExpression( }; } + const { assumeTypes, ignoreImmediateMutation } = optionsObject; + const assumeTypesForArrays = - options.assumeTypes === true || - (options.assumeTypes !== false && options.assumeTypes.forArrays === true); + assumeTypes === true || + (assumeTypes !== false && assumeTypes.forArrays === true); // Array mutation? if ( arrayMutatorMethods.has(node.callee.property.name) && - (!options.ignoreImmediateMutation || + (!ignoreImmediateMutation || !isInChainCallAndFollowsNew( node.callee, context, @@ -328,8 +366,8 @@ function checkCallExpression( } const assumeTypesForObjects = - options.assumeTypes === true || - (options.assumeTypes !== false && options.assumeTypes.forObjects === true); + assumeTypes === true || + (assumeTypes !== false && assumeTypes.forObjects === true); // Non-array object mutation (ex. Object.assign on identifier)? if ( @@ -337,8 +375,8 @@ function checkCallExpression( node.arguments.length >= 2 && (isIdentifier(node.arguments[0]) || isMemberExpression(node.arguments[0])) && - !shouldIgnoreClass(node.arguments[0], context, options) && - !shouldIgnorePattern(node.arguments[0], context, options) && + !shouldIgnoreClass(node.arguments[0], context, optionsObject) && + !shouldIgnorePattern(node.arguments[0], context, optionsObject) && isObjectConstructorType( getTypeOfNode(node.callee.object, context), assumeTypesForObjects, diff --git a/src/rules/no-class.ts b/src/rules/no-class.ts index 4b81a96c9..d32ce40d0 100644 --- a/src/rules/no-class.ts +++ b/src/rules/no-class.ts @@ -1,28 +1,41 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule } from "~/util/rule"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "no-class" as const; -// The options this rule can take. -type Options = {}; +/** + * The options this rule can take. + */ +type Options = readonly [{}]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = []; -// The default options for the rule. -const defaultOptions: Options = {}; +/** + * The default options for the rule. + */ +const defaultOptions: Options = [{}]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { generic: "Unexpected class, use functions not classes.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: "Disallow classes.", @@ -36,8 +49,12 @@ const meta: RuleMetaData = { * Check if the given class node violates this rule. */ function checkClass( - node: TSESTree.ClassDeclaration | TSESTree.ClassExpression, - context: RuleContext + node: + | ReadonlyDeep + | ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + > ): RuleResult { // All class nodes violate this rule. return { context, descriptors: [{ node, messageId: "generic" }] }; diff --git a/src/rules/no-conditional-statement.ts b/src/rules/no-conditional-statement.ts index 5842e8dc5..ff6382539 100644 --- a/src/rules/no-conditional-statement.ts +++ b/src/rules/no-conditional-statement.ts @@ -1,9 +1,10 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; import type { Type } from "typescript"; import tsutils from "~/conditional-imports/tsutils"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule, getTypeOfNode } from "~/util/rule"; import { isBlockStatement, @@ -17,15 +18,23 @@ import { isThrowStatement, } from "~/util/typeguard"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "no-conditional-statement" as const; -// The options this rule can take. -type Options = { - readonly allowReturningBranches: boolean | "ifExhaustive"; -}; +/** + * The options this rule can take. + */ +type Options = readonly [ + Readonly<{ + allowReturningBranches: boolean | "ifExhaustive"; + }> +]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = [ { type: "object", @@ -46,10 +55,14 @@ const schema: JSONSchema4 = [ }, ]; -// The default options for the rule. -const defaultOptions: Options = { allowReturningBranches: false }; +/** + * The default options for the rule. + */ +const defaultOptions: Options = [{ allowReturningBranches: false }]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { incompleteBranch: "Incomplete branch, every branch in a conditional statement must contain a return statement.", @@ -63,8 +76,10 @@ const errorMessages = { "Unexpected switch, use a conditional expression (ternary operator) instead.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: "Disallow conditional statements.", @@ -81,7 +96,7 @@ const meta: RuleMetaData = { * @returns A violation rule result. */ function incompleteBranchViolation( - node: TSESTree.Node + node: ReadonlyDeep ): RuleResult["descriptors"] { return [{ node, messageId: "incompleteBranch" }]; } @@ -90,9 +105,11 @@ function incompleteBranchViolation( * Get a function that tests if the given statement is never returning. */ function getIsNeverExpressions( - context: RuleContext + context: ReadonlyDeep< + TSESLint.RuleContext + > ) { - return (statement: TSESTree.Statement) => { + return (statement: ReadonlyDeep) => { if (isExpressionStatement(statement)) { const expressionStatementType = getTypeOfNode( statement.expression, @@ -108,11 +125,8 @@ function getIsNeverExpressions( /** * Is the given statement, when inside an if statement, a returning branch? - * - * @param statement - * @returns */ -function isIfReturningBranch(statement: TSESTree.Statement) { +function isIfReturningBranch(statement: ReadonlyDeep) { return ( // Another instance of this rule will check nested if statements. isIfStatement(statement) || @@ -128,8 +142,10 @@ function isIfReturningBranch(statement: TSESTree.Statement) { * are allowed. */ function getIfBranchViolations( - node: TSESTree.IfStatement, - context: RuleContext + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + > ): RuleResult["descriptors"] { const branches = [node.consequent, node.alternate]; const violations = branches.filter>( @@ -172,11 +188,8 @@ function getIfBranchViolations( /** * Is the given statement, when inside a switch statement, a returning branch? - * - * @param statement - * @returns */ -function isSwitchReturningBranch(statement: TSESTree.Statement) { +function isSwitchReturningBranch(statement: ReadonlyDeep) { return ( // Another instance of this rule will check nested switch statements. isSwitchStatement(statement) || @@ -190,8 +203,10 @@ function isSwitchReturningBranch(statement: TSESTree.Statement) { * statements are allowed. */ function getSwitchViolations( - node: TSESTree.SwitchStatement, - context: RuleContext + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + > ): RuleResult["descriptors"] { const isNeverExpressions = getIsNeverExpressions(context); @@ -228,7 +243,9 @@ function getSwitchViolations( /** * Does the given if statement violate this rule if it must be exhaustive. */ -function isExhaustiveIfViolation(node: TSESTree.IfStatement): boolean { +function isExhaustiveIfViolation( + node: ReadonlyDeep +): boolean { return node.alternate === null; } @@ -236,8 +253,10 @@ function isExhaustiveIfViolation(node: TSESTree.IfStatement): boolean { * Does the given typed switch statement violate this rule if it must be exhaustive. */ function isExhaustiveTypeSwitchViolation( - node: TSESTree.SwitchStatement, - context: RuleContext + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + > ): boolean { if (tsutils === undefined) { return true; @@ -261,8 +280,10 @@ function isExhaustiveTypeSwitchViolation( * Does the given switch statement violate this rule if it must be exhaustive. */ function isExhaustiveSwitchViolation( - node: TSESTree.SwitchStatement, - context: RuleContext + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + > ): boolean { return ( // No cases defined. @@ -276,16 +297,20 @@ function isExhaustiveSwitchViolation( * Check if the given IfStatement violates this rule. */ function checkIfStatement( - node: TSESTree.IfStatement, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [{ allowReturningBranches }] = options; + return { context, descriptors: - options.allowReturningBranches === false + allowReturningBranches === false ? [{ node, messageId: "unexpectedIf" }] - : options.allowReturningBranches === "ifExhaustive" + : allowReturningBranches === "ifExhaustive" ? isExhaustiveIfViolation(node) ? [{ node, messageId: "incompleteIf" }] : getIfBranchViolations(node, context) @@ -297,16 +322,20 @@ function checkIfStatement( * Check if the given SwitchStatement violates this rule. */ function checkSwitchStatement( - node: TSESTree.SwitchStatement, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [{ allowReturningBranches }] = options; + return { context, descriptors: - options.allowReturningBranches === false + allowReturningBranches === false ? [{ node, messageId: "unexpectedSwitch" }] - : options.allowReturningBranches === "ifExhaustive" + : allowReturningBranches === "ifExhaustive" ? isExhaustiveSwitchViolation(node, context) ? [{ node, messageId: "incompleteSwitch" }] : getSwitchViolations(node, context) diff --git a/src/rules/no-expression-statement.ts b/src/rules/no-expression-statement.ts index 6710f4080..3c9ea32b5 100644 --- a/src/rules/no-expression-statement.ts +++ b/src/rules/no-expression-statement.ts @@ -1,6 +1,7 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; import { deepmerge } from "deepmerge-ts"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; import type { IgnorePatternOption } from "~/common/ignore-options"; import { @@ -8,43 +9,60 @@ import { ignorePatternOptionSchema, } from "~/common/ignore-options"; import { isDirectivePrologue } from "~/util/misc"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule, getTypeOfNode } from "~/util/rule"; import { isVoidType } from "~/util/typeguard"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "no-expression-statement" as const; -// The options this rule can take. -type Options = IgnorePatternOption & { - readonly ignoreVoid: boolean; -}; +/** + * The options this rule can take. + */ +type Options = readonly [ + IgnorePatternOption & + Readonly<{ + ignoreVoid: boolean; + }> +]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = [ - deepmerge(ignorePatternOptionSchema, { + { type: "object", - properties: { + properties: deepmerge(ignorePatternOptionSchema, { ignoreVoid: { type: "boolean", }, - }, + }), additionalProperties: false, - }), + }, ]; -// The default options for the rule. -const defaultOptions: Options = { - ignoreVoid: false, -}; +/** + * The default options for the rule. + */ +const defaultOptions: Options = [ + { + ignoreVoid: false, + }, +]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { generic: "Using expressions to cause side-effects not allowed.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: "Disallow expression statements.", @@ -58,11 +76,15 @@ const meta: RuleMetaData = { * Check if the given ExpressionStatement violates this rule. */ function checkExpressionStatement( - node: TSESTree.ExpressionStatement, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { - if (shouldIgnorePattern(node, context, options)) { + const [optionsObject] = options; + + if (shouldIgnorePattern(node, context, optionsObject)) { return { context, descriptors: [], @@ -77,7 +99,9 @@ function checkExpressionStatement( }; } - if (options.ignoreVoid === true) { + const { ignoreVoid } = optionsObject; + + if (ignoreVoid === true) { const type = getTypeOfNode(node.expression, context); return { diff --git a/src/rules/no-let.ts b/src/rules/no-let.ts index be6f02a51..fe4c9c832 100644 --- a/src/rules/no-let.ts +++ b/src/rules/no-let.ts @@ -1,6 +1,7 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; import { deepmerge } from "deepmerge-ts"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; import type { AllowLocalMutationOption, @@ -12,45 +13,66 @@ import { allowLocalMutationOptionSchema, ignorePatternOptionSchema, } from "~/common/ignore-options"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule } from "~/util/rule"; import { inForLoopInitializer } from "~/util/tree"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "no-let" as const; -// The options this rule can take. -type Options = AllowLocalMutationOption & - IgnorePatternOption & { - readonly allowInForLoopInit: boolean; - }; +/** + * The options this rule can take. + */ +type Options = readonly [ + AllowLocalMutationOption & + IgnorePatternOption & + Readonly<{ + allowInForLoopInit: boolean; + }> +]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = [ - deepmerge(allowLocalMutationOptionSchema, ignorePatternOptionSchema, { + { type: "object", - properties: { - allowInForLoopInit: { - type: "boolean", - }, - }, + properties: deepmerge( + allowLocalMutationOptionSchema, + ignorePatternOptionSchema, + { + allowInForLoopInit: { + type: "boolean", + }, + } + ), additionalProperties: false, - }), + }, ]; -// The default options for the rule. -const defaultOptions: Options = { - allowInForLoopInit: false, - allowLocalMutation: false, -}; +/** + * The default options for the rule. + */ +const defaultOptions: Options = [ + { + allowInForLoopInit: false, + allowLocalMutation: false, + }, +]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { generic: "Unexpected let, use const instead.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: "Disallow mutable variables.", @@ -65,15 +87,20 @@ const meta: RuleMetaData = { * Check if the given VariableDeclaration violates this rule. */ function checkVariableDeclaration( - node: TSESTree.VariableDeclaration, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [optionsObject] = options; + const { allowInForLoopInit } = optionsObject; + if ( node.kind !== "let" || - shouldIgnoreLocalMutation(node, context, options) || - shouldIgnorePattern(node, context, options) || - (options.allowInForLoopInit && inForLoopInitializer(node)) + shouldIgnoreLocalMutation(node, context, optionsObject) || + shouldIgnorePattern(node, context, optionsObject) || + (allowInForLoopInit && inForLoopInitializer(node)) ) { return { context, diff --git a/src/rules/no-loop-statement.ts b/src/rules/no-loop-statement.ts index 732d1dcbc..b3311ea47 100644 --- a/src/rules/no-loop-statement.ts +++ b/src/rules/no-loop-statement.ts @@ -1,28 +1,41 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule } from "~/util/rule"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "no-loop-statement" as const; -// The options this rule can take. -type Options = {}; +/** + * The options this rule can take. + */ +type Options = readonly [{}]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = []; -// The default options for the rule. -const defaultOptions: Options = {}; +/** + * The default options for the rule. + */ +const defaultOptions: Options = [{}]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { generic: "Unexpected loop, use map or reduce instead.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: "Disallow imperative loops.", @@ -37,12 +50,14 @@ const meta: RuleMetaData = { */ function checkLoop( node: - | TSESTree.DoWhileStatement - | TSESTree.ForInStatement - | TSESTree.ForOfStatement - | TSESTree.ForStatement - | TSESTree.WhileStatement, - context: RuleContext + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + > ): RuleResult { // All loops violate this rule. return { context, descriptors: [{ node, messageId: "generic" }] }; diff --git a/src/rules/no-method-signature.ts b/src/rules/no-method-signature.ts index 4cc46997a..1359c4087 100644 --- a/src/rules/no-method-signature.ts +++ b/src/rules/no-method-signature.ts @@ -1,19 +1,28 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule } from "~/util/rule"; import { inReadonly } from "~/util/tree"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "no-method-signature" as const; -// The options this rule can take. -type Options = { - readonly ignoreIfReadonly: boolean; -}; +/** + * The options this rule can take. + */ +type Options = readonly [ + Readonly<{ + ignoreIfReadonly: boolean; + }> +]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = [ { type: "object", @@ -27,19 +36,27 @@ const schema: JSONSchema4 = [ }, ]; -// The default options for the rule. -const defaultOptions: Options = { - ignoreIfReadonly: true, -}; +/** + * The default options for the rule. + */ +const defaultOptions: Options = [ + { + ignoreIfReadonly: true, + }, +]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { generic: "Method signature is mutable, use property signature with readonly modifier instead.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: @@ -54,10 +71,14 @@ const meta: RuleMetaData = { * Check if the given TSMethodSignature violates this rule. */ function checkTSMethodSignature( - node: TSESTree.TSMethodSignature, - context: RuleContext, - { ignoreIfReadonly }: Options + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, + options: Options ): RuleResult { + const [{ ignoreIfReadonly }] = options; + if (ignoreIfReadonly && inReadonly(node)) { return { context, descriptors: [] }; } diff --git a/src/rules/no-mixed-type.ts b/src/rules/no-mixed-type.ts index b392c1ab3..0b38ff9ac 100644 --- a/src/rules/no-mixed-type.ts +++ b/src/rules/no-mixed-type.ts @@ -1,21 +1,30 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; -import { AST_NODE_TYPES } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; +import { AST_NODE_TYPES } from "@typescript-eslint/utils"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule } from "~/util/rule"; import { isTSPropertySignature, isTSTypeLiteral } from "~/util/typeguard"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "no-mixed-type" as const; -// The options this rule can take. -type Options = { - readonly checkInterfaces: boolean; - readonly checkTypeLiterals: boolean; -}; +/** + * The options this rule can take. + */ +type Options = readonly [ + Readonly<{ + checkInterfaces: boolean; + checkTypeLiterals: boolean; + }> +]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = [ { type: "object", @@ -31,19 +40,27 @@ const schema: JSONSchema4 = [ }, ]; -// The default options for the rule. -const defaultOptions: Options = { - checkInterfaces: true, - checkTypeLiterals: true, -}; +/** + * The default options for the rule. + */ +const defaultOptions: Options = [ + { + checkInterfaces: true, + checkTypeLiterals: true, + }, +]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { generic: "Only the same kind of members allowed in types.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: @@ -58,7 +75,7 @@ const meta: RuleMetaData = { * Does the given type elements violate the rule. */ function hasTypeElementViolations( - typeElements: ReadonlyArray + typeElements: ReadonlyArray> ): boolean { type CarryType = { readonly prevMemberType: AST_NODE_TYPES | undefined; @@ -101,14 +118,18 @@ function hasTypeElementViolations( * Check if the given TSInterfaceDeclaration violates this rule. */ function checkTSInterfaceDeclaration( - node: TSESTree.TSInterfaceDeclaration, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [{ checkInterfaces }] = options; + return { context, descriptors: - options.checkInterfaces && hasTypeElementViolations(node.body.body) + checkInterfaces && hasTypeElementViolations(node.body.body) ? [{ node, messageId: "generic" }] : [], }; @@ -118,14 +139,18 @@ function checkTSInterfaceDeclaration( * Check if the given TSTypeAliasDeclaration violates this rule. */ function checkTSTypeAliasDeclaration( - node: TSESTree.TSTypeAliasDeclaration, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [{ checkTypeLiterals }] = options; + return { context, descriptors: - options.checkTypeLiterals && + checkTypeLiterals && isTSTypeLiteral(node.typeAnnotation) && hasTypeElementViolations(node.typeAnnotation.members) ? [{ node, messageId: "generic" }] diff --git a/src/rules/no-promise-reject.ts b/src/rules/no-promise-reject.ts index 0846f29f0..9548a9acc 100644 --- a/src/rules/no-promise-reject.ts +++ b/src/rules/no-promise-reject.ts @@ -1,29 +1,42 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule } from "~/util/rule"; import { isIdentifier, isMemberExpression } from "~/util/typeguard"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "no-promise-reject" as const; -// The options this rule can take. -type Options = {}; +/** + * The options this rule can take. + */ +type Options = readonly [{}]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = []; -// The default options for the rule. -const defaultOptions: Options = {}; +/** + * The default options for the rule. + */ +const defaultOptions: Options = [{}]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { generic: "Unexpected reject, return an error instead.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: "Disallow try-catch[-finally] and try-finally patterns.", @@ -37,8 +50,10 @@ const meta: RuleMetaData = { * Check if the given CallExpression violates this rule. */ function checkCallExpression( - node: TSESTree.CallExpression, - context: RuleContext + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + > ): RuleResult { return { context, diff --git a/src/rules/no-return-void.ts b/src/rules/no-return-void.ts index 1e235c642..3b74db63c 100644 --- a/src/rules/no-return-void.ts +++ b/src/rules/no-return-void.ts @@ -1,7 +1,8 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule, getTypeOfNode } from "~/util/rule"; import { isFunctionLike, @@ -13,17 +14,25 @@ import { isVoidType, } from "~/util/typeguard"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "no-return-void" as const; -// The options this rule can take. -type Options = { - readonly allowNull: boolean; - readonly allowUndefined: boolean; - readonly ignoreImplicit: boolean; -}; +/** + * The options this rule can take. + */ +type Options = readonly [ + Readonly<{ + allowNull: boolean; + allowUndefined: boolean; + ignoreImplicit: boolean; + }> +]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = [ { type: "object", @@ -42,20 +51,28 @@ const schema: JSONSchema4 = [ }, ]; -// The default options for the rule. -const defaultOptions: Options = { - allowNull: true, - allowUndefined: true, - ignoreImplicit: false, -}; +/** + * The default options for the rule. + */ +const defaultOptions: Options = [ + { + allowNull: true, + allowUndefined: true, + ignoreImplicit: false, + }, +]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { generic: "Function must return a value.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: "Disallow functions that don't return anything.", @@ -70,15 +87,19 @@ const meta: RuleMetaData = { */ function checkFunction( node: - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression - | TSESTree.TSFunctionType, - context: RuleContext, + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [{ ignoreImplicit, allowNull, allowUndefined }] = options; + if (node.returnType === undefined) { - if (!options.ignoreImplicit && isFunctionLike(node)) { + if (!ignoreImplicit && isFunctionLike(node)) { const functionType = getTypeOfNode(node, context); const returnType = functionType ?.getCallSignatures()?.[0] @@ -87,8 +108,8 @@ function checkFunction( if ( returnType !== undefined && (isVoidType(returnType) || - (!options.allowNull && isNullType(returnType)) || - (!options.allowUndefined && isUndefinedType(returnType))) + (!allowNull && isNullType(returnType)) || + (!allowUndefined && isUndefinedType(returnType))) ) { return { context, @@ -98,9 +119,8 @@ function checkFunction( } } else if ( isTSVoidKeyword(node.returnType.typeAnnotation) || - (!options.allowNull && isTSNullKeyword(node.returnType.typeAnnotation)) || - (!options.allowUndefined && - isTSUndefinedKeyword(node.returnType.typeAnnotation)) + (!allowNull && isTSNullKeyword(node.returnType.typeAnnotation)) || + (!allowUndefined && isTSUndefinedKeyword(node.returnType.typeAnnotation)) ) { return { context, diff --git a/src/rules/no-this-expression.ts b/src/rules/no-this-expression.ts index d35d1d2d6..96f012856 100644 --- a/src/rules/no-this-expression.ts +++ b/src/rules/no-this-expression.ts @@ -1,28 +1,41 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule } from "~/util/rule"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "no-this-expression" as const; -// The options this rule can take. -type Options = {}; +/** + * The options this rule can take. + */ +type Options = readonly [{}]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = []; -// The default options for the rule. -const defaultOptions: Options = {}; +/** + * The default options for the rule. + */ +const defaultOptions: Options = [{}]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { generic: "Unexpected this, use functions not classes.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: "Disallow this access.", @@ -36,8 +49,10 @@ const meta: RuleMetaData = { * Check if the given ThisExpression violates this rule. */ function checkThisExpression( - node: TSESTree.ThisExpression, - context: RuleContext + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + > ): RuleResult { // All throw statements violate this rule. return { context, descriptors: [{ node, messageId: "generic" }] }; diff --git a/src/rules/no-throw-statement.ts b/src/rules/no-throw-statement.ts index 7dafde0eb..5fcb7c00e 100644 --- a/src/rules/no-throw-statement.ts +++ b/src/rules/no-throw-statement.ts @@ -1,28 +1,41 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule } from "~/util/rule"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "no-throw-statement" as const; -// The options this rule can take. -type Options = {}; +/** + * The options this rule can take. + */ +type Options = readonly [{}]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = []; -// The default options for the rule. -const defaultOptions: Options = {}; +/** + * The default options for the rule. + */ +const defaultOptions: Options = [{}]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { generic: "Unexpected throw, throwing exceptions is not functional.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: "Disallow throwing exceptions.", @@ -36,8 +49,10 @@ const meta: RuleMetaData = { * Check if the given ThrowStatement violates this rule. */ function checkThrowStatement( - node: TSESTree.ThrowStatement, - context: RuleContext + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + > ): RuleResult { // All throw statements violate this rule. return { context, descriptors: [{ node, messageId: "generic" }] }; diff --git a/src/rules/no-try-statement.ts b/src/rules/no-try-statement.ts index 208e456ea..cf88e7a73 100644 --- a/src/rules/no-try-statement.ts +++ b/src/rules/no-try-statement.ts @@ -1,19 +1,28 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule } from "~/util/rule"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "no-try-statement" as const; -// The options this rule can take. -type Options = { - readonly allowCatch: boolean; - readonly allowFinally: boolean; -}; +/** + * The options this rule can take. + */ +type Options = readonly [ + Readonly<{ + allowCatch: boolean; + allowFinally: boolean; + }> +]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = [ { type: "object", @@ -29,20 +38,28 @@ const schema: JSONSchema4 = [ }, ]; -// The default options for the rule. -const defaultOptions: Options = { - allowCatch: false, - allowFinally: false, -}; +/** + * The default options for the rule. + */ +const defaultOptions: Options = [ + { + allowCatch: false, + allowFinally: false, + }, +]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { catch: "Unexpected try-catch, this pattern is not functional.", finally: "Unexpected try-finally, this pattern is not functional.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: "Disallow try-catch[-finally] and try-finally patterns.", @@ -56,16 +73,20 @@ const meta: RuleMetaData = { * Check if the given TryStatement violates this rule. */ function checkTryStatement( - node: TSESTree.TryStatement, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [{ allowCatch, allowFinally }] = options; + return { context, descriptors: - !options.allowCatch && node.handler !== null + !allowCatch && node.handler !== null ? [{ node, messageId: "catch" }] - : !options.allowFinally && node.finalizer !== null + : !allowFinally && node.finalizer !== null ? [{ node, messageId: "finally" }] : [], }; diff --git a/src/rules/prefer-readonly-type.ts b/src/rules/prefer-readonly-type.ts index 36626ea13..59c40bfa2 100644 --- a/src/rules/prefer-readonly-type.ts +++ b/src/rules/prefer-readonly-type.ts @@ -1,6 +1,7 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; import { deepmerge } from "deepmerge-ts"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; import type { AllowLocalMutationOption, @@ -18,7 +19,7 @@ import { ignoreInterfaceOptionSchema, ignorePatternOptionSchema, } from "~/common/ignore-options"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule, getTypeOfNode } from "~/util/rule"; import { isInReturnType } from "~/util/tree"; import { @@ -34,29 +35,38 @@ import { isTSTypeOperator, } from "~/util/typeguard"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "prefer-readonly-type" as const; -// The options this rule can take. -type Options = AllowLocalMutationOption & - IgnoreClassOption & - IgnoreInterfaceOption & - IgnorePatternOption & { - readonly allowMutableReturnType: boolean; - readonly checkImplicit: boolean; - readonly ignoreCollections: boolean; - }; +/** + * The options this rule can take. + */ +type Options = readonly [ + AllowLocalMutationOption & + IgnoreClassOption & + IgnoreInterfaceOption & + IgnorePatternOption & + Readonly<{ + allowMutableReturnType: boolean; + checkImplicit: boolean; + ignoreCollections: boolean; + }> +]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = [ - deepmerge( - allowLocalMutationOptionSchema, - ignorePatternOptionSchema, - ignoreClassOptionSchema, - ignoreInterfaceOptionSchema, - { - type: "object", - properties: { + { + type: "object", + properties: deepmerge( + allowLocalMutationOptionSchema, + ignorePatternOptionSchema, + ignoreClassOptionSchema, + ignoreInterfaceOptionSchema, + { allowMutableReturnType: { type: "boolean", }, @@ -66,23 +76,29 @@ const schema: JSONSchema4 = [ ignoreCollections: { type: "boolean", }, - }, - additionalProperties: false, - } - ), + } + ), + additionalProperties: false, + }, ]; -// The default options for the rule. -const defaultOptions: Options = { - checkImplicit: false, - ignoreClass: false, - ignoreInterface: false, - ignoreCollections: false, - allowLocalMutation: false, - allowMutableReturnType: false, -}; +/** + * The default options for the rule. + */ +const defaultOptions: Options = [ + { + checkImplicit: false, + ignoreClass: false, + ignoreInterface: false, + ignoreCollections: false, + allowLocalMutation: false, + allowMutableReturnType: false, + }, +]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { array: "Only readonly arrays allowed.", implicit: "Implicitly a mutable array. Only readonly arrays allowed.", @@ -91,8 +107,10 @@ const errorMessages = { type: "Only readonly types allowed.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: "Prefer readonly array over mutable arrays.", @@ -120,16 +138,21 @@ const mutableTypeRegex = new RegExp( * Check if the given ArrayType or TupleType violates this rule. */ function checkArrayOrTupleType( - node: TSESTree.TSArrayType | TSESTree.TSTupleType, - context: RuleContext, + node: ReadonlyDeep | ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [optionsObject] = options; + const { allowMutableReturnType, ignoreCollections } = optionsObject; + if ( - shouldIgnoreClass(node, context, options) || - shouldIgnoreInterface(node, context, options) || - shouldIgnoreLocalMutation(node, context, options) || - shouldIgnorePattern(node, context, options) || - options.ignoreCollections + shouldIgnoreClass(node, context, optionsObject) || + shouldIgnoreInterface(node, context, optionsObject) || + shouldIgnoreLocalMutation(node, context, optionsObject) || + shouldIgnorePattern(node, context, optionsObject) || + ignoreCollections ) { return { context, @@ -143,7 +166,7 @@ function checkArrayOrTupleType( (node.parent === undefined || !isTSTypeOperator(node.parent) || node.parent.operator !== "readonly") && - (!options.allowMutableReturnType || !isInReturnType(node)) + (!allowMutableReturnType || !isInReturnType(node)) ? [ { node, @@ -151,10 +174,17 @@ function checkArrayOrTupleType( fix: node.parent !== undefined && isTSArrayType(node.parent) ? (fixer) => [ - fixer.insertTextBefore(node, "(readonly "), - fixer.insertTextAfter(node, ")"), + fixer.insertTextBefore( + node as TSESTree.Node, + "(readonly " + ), + fixer.insertTextAfter(node as TSESTree.Node, ")"), ] - : (fixer) => fixer.insertTextBefore(node, "readonly "), + : (fixer) => + fixer.insertTextBefore( + node as TSESTree.Node, + "readonly " + ), }, ] : [], @@ -165,15 +195,19 @@ function checkArrayOrTupleType( * Check if the given TSMappedType violates this rule. */ function checkMappedType( - node: TSESTree.TSMappedType, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [optionsObject] = options; + if ( - shouldIgnoreClass(node, context, options) || - shouldIgnoreInterface(node, context, options) || - shouldIgnoreLocalMutation(node, context, options) || - shouldIgnorePattern(node, context, options) + shouldIgnoreClass(node, context, optionsObject) || + shouldIgnoreInterface(node, context, optionsObject) || + shouldIgnoreLocalMutation(node, context, optionsObject) || + shouldIgnorePattern(node, context, optionsObject) ) { return { context, @@ -204,15 +238,20 @@ function checkMappedType( * Check if the given TypeReference violates this rule. */ function checkTypeReference( - node: TSESTree.TSTypeReference, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [optionsObject] = options; + const { allowMutableReturnType, ignoreCollections } = optionsObject; + if ( - shouldIgnoreClass(node, context, options) || - shouldIgnoreInterface(node, context, options) || - shouldIgnoreLocalMutation(node, context, options) || - shouldIgnorePattern(node, context, options) + shouldIgnoreClass(node, context, optionsObject) || + shouldIgnoreInterface(node, context, optionsObject) || + shouldIgnoreLocalMutation(node, context, optionsObject) || + shouldIgnorePattern(node, context, optionsObject) ) { return { context, @@ -221,10 +260,7 @@ function checkTypeReference( } if (isIdentifier(node.typeName)) { - if ( - options.ignoreCollections && - mutableTypeRegex.test(node.typeName.name) - ) { + if (ignoreCollections && mutableTypeRegex.test(node.typeName.name)) { return { context, descriptors: [], @@ -236,12 +272,16 @@ function checkTypeReference( descriptors: immutableType !== undefined && immutableType.length > 0 && - (!options.allowMutableReturnType || !isInReturnType(node)) + (!allowMutableReturnType || !isInReturnType(node)) ? [ { node, messageId: "type", - fix: (fixer) => fixer.replaceText(node.typeName, immutableType), + fix: (fixer) => + fixer.replaceText( + node.typeName as TSESTree.Node, + immutableType + ), }, ] : [], @@ -258,18 +298,23 @@ function checkTypeReference( */ function checkProperty( node: - | TSESTree.PropertyDefinition - | TSESTree.TSIndexSignature - | TSESTree.TSParameterProperty - | TSESTree.TSPropertySignature, - context: RuleContext, + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [optionsObject] = options; + const { allowMutableReturnType } = optionsObject; + if ( - shouldIgnoreClass(node, context, options) || - shouldIgnoreInterface(node, context, options) || - shouldIgnoreLocalMutation(node, context, options) || - shouldIgnorePattern(node, context, options) + shouldIgnoreClass(node, context, optionsObject) || + shouldIgnoreInterface(node, context, optionsObject) || + shouldIgnoreLocalMutation(node, context, optionsObject) || + shouldIgnorePattern(node, context, optionsObject) ) { return { context, @@ -281,18 +326,26 @@ function checkProperty( context, descriptors: node.readonly !== true && - (!options.allowMutableReturnType || !isInReturnType(node)) + (!allowMutableReturnType || !isInReturnType(node)) ? [ { node, messageId: "property", fix: isTSIndexSignature(node) || isTSPropertySignature(node) - ? (fixer) => fixer.insertTextBefore(node, "readonly ") + ? (fixer) => + fixer.insertTextBefore(node as TSESTree.Node, "readonly ") : isTSParameterProperty(node) ? (fixer) => - fixer.insertTextBefore(node.parameter, "readonly ") - : (fixer) => fixer.insertTextBefore(node.key, "readonly "), + fixer.insertTextBefore( + node.parameter as TSESTree.Node, + "readonly " + ) + : (fixer) => + fixer.insertTextBefore( + node.key as TSESTree.Node, + "readonly " + ), }, ] : [], @@ -304,19 +357,24 @@ function checkProperty( */ function checkImplicitType( node: - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression - | TSESTree.VariableDeclaration, - context: RuleContext, + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { + const [optionsObject] = options; + const { checkImplicit, ignoreCollections } = optionsObject; + if ( - !options.checkImplicit || - shouldIgnoreClass(node, context, options) || - shouldIgnoreInterface(node, context, options) || - shouldIgnoreLocalMutation(node, context, options) || - shouldIgnorePattern(node, context, options) + !checkImplicit || + shouldIgnoreClass(node, context, optionsObject) || + shouldIgnoreInterface(node, context, optionsObject) || + shouldIgnoreLocalMutation(node, context, optionsObject) || + shouldIgnorePattern(node, context, optionsObject) ) { return { context, @@ -358,7 +416,7 @@ function checkImplicitType( declarator.id.typeAnnotation === undefined && declarator.init !== null && isArrayType(getTypeOfNode(declarator.init, context)) && - !options.ignoreCollections + !ignoreCollections ? [ { node: declarator.node, diff --git a/src/rules/prefer-tacit.ts b/src/rules/prefer-tacit.ts index acbed68c5..6e3d10079 100644 --- a/src/rules/prefer-tacit.ts +++ b/src/rules/prefer-tacit.ts @@ -1,12 +1,13 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; -import type { ReportDescriptor } from "@typescript-eslint/experimental-utils/dist/ts-eslint"; +import type { ESLintUtils, TSESLint, TSESTree } from "@typescript-eslint/utils"; +import type { ReportDescriptor } from "@typescript-eslint/utils/dist/ts-eslint"; import { deepmerge } from "deepmerge-ts"; import type { JSONSchema4 } from "json-schema"; +import type { ReadonlyDeep } from "type-fest"; import type { FunctionLikeDeclaration, Type } from "typescript"; import type { IgnorePatternOption } from "~/common/ignore-options"; import { ignorePatternOptionSchema } from "~/common/ignore-options"; -import type { RuleContext, RuleMetaData, RuleResult } from "~/util/rule"; +import type { RuleResult } from "~/util/rule"; import { createRule, getESTreeNode, getTypeOfNode } from "~/util/rule"; import { isBlockStatement, @@ -18,23 +19,32 @@ import { isTSFunctionType, } from "~/util/typeguard"; -// The name of this rule. +/** + * The name of this rule. + */ export const name = "prefer-tacit" as const; -// The options this rule can take. -type Options = IgnorePatternOption & { - readonly assumeTypes: - | false - | { - readonly allowFixer: boolean; - }; -}; +/** + * The options this rule can take. + */ +type Options = readonly [ + IgnorePatternOption & + Readonly<{ + assumeTypes: + | false + | Readonly<{ + allowFixer: boolean; + }>; + }> +]; -// The schema for the rule options. +/** + * The schema for the rule options. + */ const schema: JSONSchema4 = [ - deepmerge(ignorePatternOptionSchema, { + { type: "object", - properties: { + properties: deepmerge(ignorePatternOptionSchema, { ignoreImmediateMutation: { type: "boolean", }, @@ -55,23 +65,31 @@ const schema: JSONSchema4 = [ }, ], }, - }, + }), additionalProperties: false, - }), + }, ]; -// The default options for the rule. -const defaultOptions: Options = { - assumeTypes: false, -}; +/** + * The default options for the rule. + */ +const defaultOptions: Options = [ + { + assumeTypes: false, + }, +]; -// The possible error messages. +/** + * The possible error messages. + */ const errorMessages = { generic: "Potentially unnecessary function wrapper.", } as const; -// The meta data for this rule. -const meta: RuleMetaData = { +/** + * The meta data for this rule. + */ +const meta: ESLintUtils.NamedCreateRuleMeta = { type: "suggestion", docs: { description: "Replaces `x => f(x)` with just `f`.", @@ -86,9 +104,12 @@ const meta: RuleMetaData = { * From the callee's type, does it follow that the caller violates this rule. */ function isCallerViolation( - caller: TSESTree.CallExpression, + caller: ReadonlyDeep, + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type calleeType: Type, - context: RuleContext + context: ReadonlyDeep< + TSESLint.RuleContext + > ): boolean { if (calleeType.symbol === undefined) { return false; @@ -121,13 +142,17 @@ function isCallerViolation( */ function getCallDescriptors( node: - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression, - context: RuleContext, + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options, - caller: TSESTree.CallExpression -): Array> { + caller: ReadonlyDeep +): Array>> { + const [{ assumeTypes }] = options; + if ( isIdentifier(caller.callee) && node.params.length === caller.arguments.length && @@ -143,7 +168,7 @@ function getCallDescriptors( const calleeType = getTypeOfNode(caller.callee, context); const assumingTypes = (calleeType === null || calleeType.symbol === undefined) && - options.assumeTypes !== false; + assumeTypes !== false; if ( assumingTypes || @@ -156,12 +181,11 @@ function getCallDescriptors( messageId: "generic", fix: // No fixer when assuming types as this is dangerous. - (typeof options.assumeTypes !== "object" && assumingTypes) || + (typeof assumeTypes !== "object" && assumingTypes) || // Unless user specifies they want it. - (typeof options.assumeTypes === "object" && - !options.assumeTypes.allowFixer) + (typeof assumeTypes === "object" && !assumeTypes.allowFixer) ? null - : (fixer) => fixer.replaceText(node, calleeName), + : (fixer) => fixer.replaceText(node as TSESTree.Node, calleeName), }, ]; } @@ -175,12 +199,14 @@ function getCallDescriptors( */ function getDirectCallDescriptors( node: - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression, - context: RuleContext, + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options -): Array> { +): Array>> { if (isCallExpression(node.body)) { return getCallDescriptors(node, context, options, node.body); } @@ -192,12 +218,14 @@ function getDirectCallDescriptors( */ function getNestedCallDescriptors( node: - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression, - context: RuleContext, + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options -): Array> { +): Array>> { if ( isBlockStatement(node.body) && node.body.body.length === 1 && @@ -220,10 +248,12 @@ function getNestedCallDescriptors( */ function checkFunction( node: - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression, - context: RuleContext, + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep, + context: ReadonlyDeep< + TSESLint.RuleContext + >, options: Options ): RuleResult { return { diff --git a/src/util/misc.ts b/src/util/misc.ts index 2e3f7c808..0b2cbf3c6 100644 --- a/src/util/misc.ts +++ b/src/util/misc.ts @@ -1,9 +1,9 @@ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; -import { AST_NODE_TYPES } from "@typescript-eslint/experimental-utils"; +import type { TSESTree } from "@typescript-eslint/utils"; +import { AST_NODE_TYPES } from "@typescript-eslint/utils"; +import type { ReadonlyDeep } from "type-fest"; /** - * Returns a function that checks if the given value is the same as the expected - * value. + * Higher order function to check if the two given values are the same. */ export function isExpected(expected: T): (actual: T) => boolean { return (actual) => actual === expected; @@ -13,7 +13,7 @@ export function isExpected(expected: T): (actual: T) => boolean { * Does the given ExpressionStatement specify directive prologues. */ export function isDirectivePrologue( - node: TSESTree.ExpressionStatement + node: ReadonlyDeep ): boolean { return ( node.expression.type === AST_NODE_TYPES.Literal && diff --git a/src/util/rule.ts b/src/util/rule.ts index 6235ac08c..6042ef035 100644 --- a/src/util/rule.ts +++ b/src/util/rule.ts @@ -1,63 +1,56 @@ -import type { TSESLint, TSESTree } from "@typescript-eslint/experimental-utils"; -import { ESLintUtils } from "@typescript-eslint/experimental-utils"; +import type { TSESLint, TSESTree } from "@typescript-eslint/utils"; +import { ESLintUtils } from "@typescript-eslint/utils"; import type { Rule } from "eslint"; -import type { Node, Type } from "typescript"; +import type { ReadonlyDeep } from "type-fest"; +import type { Node as TSNode, Type } from "typescript"; // eslint-disable-next-line @typescript-eslint/naming-convention, no-underscore-dangle -- This is a special var. const __VERSION__ = "0.0.0-development"; -export type BaseOptions = object; - -// "url" will be set automatically. -export type RuleMetaDataDocs = Omit; - -// "docs.url" will be set automatically. -export type RuleMetaData = Omit< - TSESLint.RuleMetaData, - "docs" -> & { - readonly docs: RuleMetaDataDocs; -}; - -export type RuleContext< - MessageIds extends string, - Options extends BaseOptions -> = TSESLint.RuleContext; +/** + * All options must extends this type. + */ +export type BaseOptions = ReadonlyArray; +/** + * The result all rules return. + */ export type RuleResult< MessageIds extends string, Options extends BaseOptions -> = { - readonly context: RuleContext; - readonly descriptors: ReadonlyArray>; -}; +> = Readonly<{ + context: ReadonlyDeep>; + descriptors: ReadonlyArray< + ReadonlyDeep> + >; +}>; -export type RuleFunctionsMap< +/** + * A map of nodes to functions that a rule operate on. + */ +type RuleFunctionsMap< + Node extends ReadonlyDeep, MessageIds extends string, Options extends BaseOptions -> = { - readonly [K in keyof TSESLint.RuleListener]: ( - node: NonNullable extends TSESLint.RuleFunction< - infer U - > - ? U - : never, - context: RuleContext, +> = Readonly<{ + [K in keyof TSESLint.RuleListener]: ( + node: Node, + context: ReadonlyDeep>, options: Options ) => RuleResult; -}; +}>; // This function can't be functional as it needs to interact with 3rd-party // libraries that aren't functional. -/* eslint-disable functional/no-return-void, functional/no-conditional-statement, functional/no-expression-statement */ +/* eslint-disable functional/no-return-void, functional/no-expression-statement */ /** * Create a function that processes common options and then runs the given * check. */ function checkNode< MessageIds extends string, - Context extends RuleContext, - Node extends TSESTree.Node, + Context extends ReadonlyDeep>, + Node extends ReadonlyDeep, Options extends BaseOptions >( check: ( @@ -73,11 +66,13 @@ function checkNode< // eslint-disable-next-line functional/no-loop-statement -- can't really be avoided. for (const descriptor of result.descriptors) { - result.context.report(descriptor); + result.context.report( + descriptor as TSESLint.ReportDescriptor + ); } }; } -/* eslint-enable functional/no-return-void, functional/no-conditional-statement, functional/no-expression-statement */ +/* eslint-enable functional/no-return-void, functional/no-expression-statement */ /** * Create a rule. @@ -87,38 +82,39 @@ export function createRule< Options extends BaseOptions >( name: string, - meta: RuleMetaData, + meta: ESLintUtils.NamedCreateRuleMeta, defaultOptions: Options, - ruleFunctionsMap: RuleFunctionsMap -): Rule.RuleModule { + ruleFunctionsMap: RuleFunctionsMap +) { return ESLintUtils.RuleCreator( - (name) => - `https://github.com/jonaskello/eslint-plugin-functional/blob/v${__VERSION__}/docs/rules/${name}.md` + (ruleName) => + `https://github.com/jonaskello/eslint-plugin-functional/blob/v${__VERSION__}/docs/rules/${ruleName}.md` )({ name, meta, - defaultOptions: [defaultOptions], - create: ( - context: TSESLint.RuleContext, - [options]: readonly [Options] - ) => + defaultOptions, + create: (context, options) => Object.fromEntries( Object.entries(ruleFunctionsMap).map(([nodeSelector, ruleFunction]) => [ nodeSelector, - checkNode(ruleFunction, context, options), + checkNode( + ruleFunction, + context as unknown as ReadonlyDeep< + TSESLint.RuleContext + >, + options as unknown as Options + ), ]) ), - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - } as any) as any; + }) as unknown as Rule.RuleModule; } /** * Get the type of the the given node. */ -export function getTypeOfNode>( - node: TSESTree.Node, - context: Context -): Type | null { +export function getTypeOfNode< + Context extends ReadonlyDeep> +>(node: ReadonlyDeep, context: Context): Type | null { const { parserServices } = context; if ( @@ -130,7 +126,7 @@ export function getTypeOfNode>( } const checker = parserServices.program.getTypeChecker(); const nodeType = checker.getTypeAtLocation( - parserServices.esTreeNodeToTSNodeMap.get(node) + parserServices.esTreeNodeToTSNodeMap.get(node as TSESTree.Node) ); const constrained = checker.getBaseConstraintOfType(nodeType); return constrained ?? nodeType; @@ -139,8 +135,11 @@ export function getTypeOfNode>( /** * Get the es tree node from the given ts node. */ -export function getESTreeNode>( - node: Node, +export function getESTreeNode< + Context extends ReadonlyDeep> +>( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Node + node: TSNode, context: Context ): TSESTree.Node | null { const { parserServices } = context; diff --git a/src/util/tree.ts b/src/util/tree.ts index 25eaaf1c0..bd8271ee9 100644 --- a/src/util/tree.ts +++ b/src/util/tree.ts @@ -1,5 +1,5 @@ -import { ASTUtils } from "@typescript-eslint/experimental-utils"; -import type { TSESTree } from "@typescript-eslint/experimental-utils"; +import type { TSESTree } from "@typescript-eslint/utils"; +import type { ReadonlyDeep } from "type-fest"; import { isCallExpression, @@ -23,10 +23,13 @@ import { /** * Return the first ancestor that meets the given check criteria. */ -function getAncestorOfType( - checker: (node: TSESTree.Node, child: TSESTree.Node | null) => node is T, - node: TSESTree.Node, - child: TSESTree.Node | null = null +function getAncestorOfType>( + checker: ( + node: ReadonlyDeep, + child: ReadonlyDeep | null + ) => node is T, + node: ReadonlyDeep, + child: ReadonlyDeep | null = null ): T | null { return checker(node, child) ? node @@ -38,7 +41,7 @@ function getAncestorOfType( /** * Test if the given node is in a function's body. */ -export function inFunctionBody(node: TSESTree.Node): boolean { +export function inFunctionBody(node: ReadonlyDeep): boolean { return ( getAncestorOfType( (n, c): n is TSESTree.Node => isFunctionLike(n) && n.body === c, @@ -50,14 +53,16 @@ export function inFunctionBody(node: TSESTree.Node): boolean { /** * Test if the given node is in a class. */ -export function inClass(node: TSESTree.Node): boolean { +export function inClass(node: ReadonlyDeep): boolean { return getAncestorOfType(isClassLike, node) !== null; } /** * Test if the given node is in a for loop initializer. */ -export function inForLoopInitializer(node: TSESTree.Node): boolean { +export function inForLoopInitializer( + node: ReadonlyDeep +): boolean { return ( getAncestorOfType( (n, c): n is TSESTree.ForStatement => isForStatement(n) && n.init === c, @@ -69,7 +74,7 @@ export function inForLoopInitializer(node: TSESTree.Node): boolean { /** * Test if the given node is shallowly inside a `Readonly<{...}>`. */ -export function inReadonly(node: TSESTree.Node): boolean { +export function inReadonly(node: ReadonlyDeep): boolean { // For nested cases, we shouldn't look for any parent, but the immediate parent. if ( isDefined(node.parent) && @@ -84,7 +89,8 @@ export function inReadonly(node: TSESTree.Node): boolean { getAncestorOfType(isTSTypeReference, node)?.typeName ?? getAncestorOfType(isTSInterfaceHeritage, node)?.expression; return ( - ASTUtils.isIdentifier(expressionOrTypeName) && + expressionOrTypeName !== undefined && + isIdentifier(expressionOrTypeName) && expressionOrTypeName.name === "Readonly" ); } @@ -92,14 +98,14 @@ export function inReadonly(node: TSESTree.Node): boolean { /** * Test if the given node is in a TS Property Signature. */ -export function inInterface(node: TSESTree.Node): boolean { +export function inInterface(node: ReadonlyDeep): boolean { return getAncestorOfType(isTSInterfaceBody, node) !== null; } /** * Test if the given node is in a Constructor. */ -export function inConstructor(node: TSESTree.Node): boolean { +export function inConstructor(node: ReadonlyDeep): boolean { const methodDefinition = getAncestorOfType(isMethodDefinition, node); return ( methodDefinition !== null && @@ -111,7 +117,7 @@ export function inConstructor(node: TSESTree.Node): boolean { /** * Is the given node in the return type. */ -export function isInReturnType(node: TSESTree.Node): boolean { +export function isInReturnType(node: ReadonlyDeep): boolean { return ( getAncestorOfType( (n): n is TSESTree.Node => @@ -126,7 +132,9 @@ export function isInReturnType(node: TSESTree.Node): boolean { /** * Is the given identifier a property of an object? */ -export function isPropertyAccess(node: TSESTree.Identifier): boolean { +export function isPropertyAccess( + node: ReadonlyDeep +): boolean { return ( node.parent !== undefined && isMemberExpression(node.parent) && @@ -137,7 +145,9 @@ export function isPropertyAccess(node: TSESTree.Identifier): boolean { /** * Is the given identifier a property name? */ -export function isPropertyName(node: TSESTree.Identifier): boolean { +export function isPropertyName( + node: ReadonlyDeep +): boolean { return ( node.parent !== undefined && isProperty(node.parent) && @@ -148,7 +158,7 @@ export function isPropertyName(node: TSESTree.Identifier): boolean { /** * Is the given function an IIFE? */ -export function isIIFE(node: TSESTree.Node): boolean { +export function isIIFE(node: ReadonlyDeep): boolean { return ( isFunctionExpressionLike(node) && node.parent !== undefined && @@ -161,7 +171,7 @@ export function isIIFE(node: TSESTree.Node): boolean { * Get the key the given node is assigned to in its parent ObjectExpression. */ export function getKeyOfValueInObjectExpression( - node: TSESTree.Node + node: ReadonlyDeep ): string | null { if (!isDefined(node.parent)) { return null; diff --git a/src/util/typeguard.ts b/src/util/typeguard.ts index 16ae9f730..f900a2892 100644 --- a/src/util/typeguard.ts +++ b/src/util/typeguard.ts @@ -2,18 +2,13 @@ * @file Functions that typeguard the given node/type. */ -import type { TSESTree } from "@typescript-eslint/experimental-utils"; -import { AST_NODE_TYPES } from "@typescript-eslint/experimental-utils"; -// TS import - only use this for types, will be stripped out by rollup. +import type { TSESTree } from "@typescript-eslint/utils"; +import { AST_NODE_TYPES } from "@typescript-eslint/utils"; +import type { ReadonlyDeep } from "type-fest"; import type { Type, UnionType } from "typescript"; -// TS import - conditionally imported only when typescript is available. import ts from "~/conditional-imports/typescript"; -// Any JSDoc for these functions would be tedious. -// eslint-disable-next-line eslint-comments/disable-enable-pair -/* eslint-disable jsdoc/require-jsdoc */ - /* * TS Types. */ @@ -51,44 +46,44 @@ export function isReadonlyArray( */ export function isArrayExpression( - node: TSESTree.Node -): node is TSESTree.ArrayExpression { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.ArrayExpression; } export function isAssignmentExpression( - node: TSESTree.Node -): node is TSESTree.AssignmentExpression { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.AssignmentExpression; } export function isAssignmentPattern( - node: TSESTree.Node -): node is TSESTree.AssignmentPattern { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.AssignmentPattern; } export function isBlockStatement( - node: TSESTree.Node -): node is TSESTree.BlockStatement { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.BlockStatement; } export function isBreakStatement( - node: TSESTree.Node -): node is TSESTree.BreakStatement { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.BreakStatement; } export function isCallExpression( - node: TSESTree.Node -): node is TSESTree.CallExpression { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.CallExpression; } export function isPropertyDefinition( - node: TSESTree.Node -): node is TSESTree.PropertyDefinition { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.PropertyDefinition; } @@ -98,8 +93,8 @@ export function isPropertyDefinition( * It doesn't matter what type of class. */ export function isClassLike( - node: TSESTree.Node -): node is TSESTree.ClassDeclaration | TSESTree.ClassExpression { + node: ReadonlyDeep +): node is ReadonlyDeep { return ( node.type === AST_NODE_TYPES.ClassDeclaration || node.type === AST_NODE_TYPES.ClassExpression @@ -107,26 +102,26 @@ export function isClassLike( } export function isContinueStatement( - node: TSESTree.Node -): node is TSESTree.ContinueStatement { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.ContinueStatement; } export function isExpressionStatement( - node: TSESTree.Node -): node is TSESTree.ExpressionStatement { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.ExpressionStatement; } export function isForStatement( - node: TSESTree.Node -): node is TSESTree.ForStatement { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.ForStatement; } export function isFunctionDeclaration( - node: TSESTree.Node -): node is TSESTree.FunctionDeclaration { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.FunctionDeclaration; } @@ -136,8 +131,10 @@ export function isFunctionDeclaration( * It doesn't matter what type of function expression. */ export function isFunctionExpressionLike( - node: TSESTree.Node -): node is TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression { + node: ReadonlyDeep +): node is ReadonlyDeep< + TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression +> { return ( node.type === AST_NODE_TYPES.FunctionExpression || node.type === AST_NODE_TYPES.ArrowFunctionExpression @@ -150,199 +147,203 @@ export function isFunctionExpressionLike( * It doesn't matter what type of function. */ export function isFunctionLike( - node: TSESTree.Node + node: ReadonlyDeep ): node is - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression { + | ReadonlyDeep + | ReadonlyDeep + | ReadonlyDeep { return isFunctionDeclaration(node) || isFunctionExpressionLike(node); } -export function isIdentifier(node: TSESTree.Node): node is TSESTree.Identifier { +export function isIdentifier( + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.Identifier; } export function isIfStatement( - node: TSESTree.Node -): node is TSESTree.IfStatement { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.IfStatement; } export function isMemberExpression( - node: TSESTree.Node -): node is TSESTree.MemberExpression { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.MemberExpression; } export function isMethodDefinition( - node: TSESTree.Node -): node is TSESTree.MethodDefinition { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.MethodDefinition; } export function isNewExpression( - node: TSESTree.Node -): node is TSESTree.NewExpression { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.NewExpression; } export function isObjectExpression( - node: TSESTree.Node -): node is TSESTree.ObjectExpression { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.ObjectExpression; } -export function isProperty(node: TSESTree.Node): node is TSESTree.Property { +export function isProperty( + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.Property; } export function isRestElement( - node: TSESTree.Node -): node is TSESTree.RestElement { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.RestElement; } export function isReturnStatement( - node: TSESTree.Node -): node is TSESTree.ReturnStatement { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.ReturnStatement; } export function isSwitchStatement( - node: TSESTree.Node -): node is TSESTree.SwitchStatement { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.SwitchStatement; } export function isThisExpression( - node: TSESTree.Node -): node is TSESTree.ThisExpression { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.ThisExpression; } export function isThrowStatement( - node: TSESTree.Node -): node is TSESTree.ThrowStatement { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.ThrowStatement; } export function isTSArrayType( - node: TSESTree.Node -): node is TSESTree.TSArrayType { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSArrayType; } export function isTSFunctionType( - node: TSESTree.Node -): node is TSESTree.TSFunctionType { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSFunctionType; } export function isTSIndexSignature( - node: TSESTree.Node -): node is TSESTree.TSIndexSignature { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSIndexSignature; } export function isTSInterfaceBody( - node: TSESTree.Node -): node is TSESTree.TSInterfaceBody { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSInterfaceBody; } export function isTSInterfaceHeritage( - node: TSESTree.Node -): node is TSESTree.TSInterfaceHeritage { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSInterfaceHeritage; } export function isTSNullKeyword( - node: TSESTree.Node -): node is TSESTree.TSNullKeyword { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSNullKeyword; } export function isTSParameterProperty( - node: TSESTree.Node -): node is TSESTree.TSParameterProperty { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSParameterProperty; } export function isTSPropertySignature( - node: TSESTree.Node -): node is TSESTree.TSPropertySignature { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSPropertySignature; } export function isTSTupleType( - node: TSESTree.Node -): node is TSESTree.TSTupleType { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSTupleType; } export function isTSTypeAnnotation( - node: TSESTree.Node -): node is TSESTree.TSTypeAnnotation { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSTypeAnnotation; } export function isTSTypeLiteral( - node: TSESTree.Node -): node is TSESTree.TSTypeLiteral { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSTypeLiteral; } export function isTSTypeOperator( - node: TSESTree.Node -): node is TSESTree.TSTypeOperator { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSTypeOperator; } export function isTSTypeReference( - node: TSESTree.Node -): node is TSESTree.TSTypeReference { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSTypeReference; } export function isTSUndefinedKeyword( - node: TSESTree.Node -): node is TSESTree.TSUndefinedKeyword { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSUndefinedKeyword; } export function isTSVoidKeyword( - node: TSESTree.Node -): node is TSESTree.TSVoidKeyword { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.TSVoidKeyword; } export function isUnaryExpression( - node: TSESTree.Node -): node is TSESTree.UnaryExpression { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.UnaryExpression; } export function isVariableDeclaration( - node: TSESTree.Node -): node is TSESTree.VariableDeclaration { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.VariableDeclaration; } export function isVariableDeclarator( - node: TSESTree.Node -): node is TSESTree.VariableDeclarator { + node: ReadonlyDeep +): node is ReadonlyDeep { return node.type === AST_NODE_TYPES.VariableDeclarator; } export function hasID( - node: TSESTree.Node -): node is TSESTree.Node & { readonly id: unknown } { + node: ReadonlyDeep +): node is ReadonlyDeep> { return Object.prototype.hasOwnProperty.call(node, "id"); } export function hasKey( - node: TSESTree.Node -): node is TSESTree.Node & { readonly key: unknown } { + node: ReadonlyDeep +): node is ReadonlyDeep> { return Object.prototype.hasOwnProperty.call(node, "key"); } @@ -354,30 +355,39 @@ export function isDefined(value: T | null | undefined): value is T { * TS types type guards. */ -export function isUnionType(type: Type): type is UnionType { +export function isUnionType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type + type: Type +): type is UnionType { return ts !== undefined && type.flags === ts.TypeFlags.Union; } -export function isArrayType(type: Type | null): type is ArrayType; export function isArrayType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type + type: Type | null +): type is ArrayType; +export function isArrayType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type type: Type, assumeType: false, node: null ): type is ArrayType; export function isArrayType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type type: Type | null, assumeType: boolean, - node: TSESTree.Node | null + node: ReadonlyDeep ): type is ArrayType; export function isArrayType( type: null, assumeType: true, - node: TSESTree.Node + node: ReadonlyDeep ): boolean; export function isArrayType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type type: Type | null, assumeType = false, - node: TSESTree.Node | null = null + node: ReadonlyDeep | null = null ): boolean { return assumeType === true && type === null ? node !== null @@ -388,27 +398,31 @@ export function isArrayType( } export function isArrayConstructorType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type type: Type | null ): type is ArrayConstructorType; export function isArrayConstructorType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type type: Type, assumeType: false, node: null ): type is ArrayConstructorType; export function isArrayConstructorType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type type: Type | null, assumeType: boolean, - node: TSESTree.Node | null + node: ReadonlyDeep ): type is ArrayConstructorType; export function isArrayConstructorType( type: null, assumeType: true, - node: TSESTree.Node + node: ReadonlyDeep ): boolean; export function isArrayConstructorType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type type: Type | null, assumeType = false, - node: TSESTree.Node | null = null + node: ReadonlyDeep | null = null ): boolean { return assumeType === true && type === null ? node !== null && isIdentifier(node) && node.name === "Array" @@ -420,27 +434,31 @@ export function isArrayConstructorType( } export function isObjectConstructorType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type type: Type | null ): type is ObjectConstructorType; export function isObjectConstructorType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type type: Type, assumeType: false, node: null ): type is ObjectConstructorType; export function isObjectConstructorType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type type: Type | null, assumeType: boolean, - node: TSESTree.Node | null + node: ReadonlyDeep ): type is ObjectConstructorType; export function isObjectConstructorType( type: null, assumeType: true, - node: TSESTree.Node + node: ReadonlyDeep ): boolean; export function isObjectConstructorType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type type: Type | null, assumeType = false, - node: TSESTree.Node | null = null + node: ReadonlyDeep | null = null ): boolean { return assumeType === true && type === null ? node !== null && isIdentifier(node) && node.name === "Object" @@ -451,18 +469,30 @@ export function isObjectConstructorType( type.types.some((t) => isObjectConstructorType(t, false, null)))); } -export function isNeverType(type: Type): boolean { +export function isNeverType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type + type: Type +): boolean { return ts !== undefined && type.flags === ts.TypeFlags.Never; } -export function isVoidType(type: Type): boolean { +export function isVoidType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type + type: Type +): boolean { return ts !== undefined && type.flags === ts.TypeFlags.Void; } -export function isNullType(type: Type): boolean { +export function isNullType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type + type: Type +): boolean { return ts !== undefined && type.flags === ts.TypeFlags.Null; } -export function isUndefinedType(type: Type): boolean { +export function isUndefinedType( + // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ignore TS Type + type: Type +): boolean { return ts !== undefined && type.flags === ts.TypeFlags.Undefined; } diff --git a/tests/common/ignore-options.test.ts b/tests/common/ignore-options.test.ts index 40cca7b07..97e362fb3 100644 --- a/tests/common/ignore-options.test.ts +++ b/tests/common/ignore-options.test.ts @@ -1,9 +1,10 @@ import assert from "assert"; -import type { TSESLint, TSESTree } from "@typescript-eslint/experimental-utils"; +import type { TSESLint, TSESTree } from "@typescript-eslint/utils"; import test from "ava"; import dedent from "dedent"; import RuleTester from "eslint-ava-rule-tester"; +import type { ReadonlyDeep } from "type-fest"; import type { AllowLocalMutationOption, @@ -21,14 +22,11 @@ import { import { filename, configs } from "~/tests/helpers/configs"; import { testWrapper } from "~/tests/helpers/testers"; import { addFilename, createDummyRule } from "~/tests/helpers/util"; -import type { BaseOptions, RuleContext } from "~/util/rule"; +import type { BaseOptions } from "~/util/rule"; -/** - * - */ function shouldIgnore( - node: TSESTree.Node, - context: RuleContext, + node: ReadonlyDeep, + context: ReadonlyDeep>, options: Partial< AllowLocalMutationOption & IgnoreAccessorPatternOption & diff --git a/tests/helpers/testers.ts b/tests/helpers/testers.ts index a937d91d2..dba8ddd08 100644 --- a/tests/helpers/testers.ts +++ b/tests/helpers/testers.ts @@ -3,6 +3,7 @@ import test from "ava"; import type { Implementation } from "ava"; import type { Rule } from "eslint"; import RuleTester from "eslint-ava-rule-tester"; +import type { ReadonlyDeep } from "type-fest"; import { configs } from "./configs"; import { @@ -14,11 +15,11 @@ import type { ValidTestCase, InvalidTestCase } from "./util"; type TestFunction = ( ruleName: string, - rule: Rule.RuleModule, - tests: { - readonly valid: ReadonlyArray; - readonly invalid: ReadonlyArray; - } + rule: ReadonlyDeep, + tests: Readonly<{ + valid: ReadonlyArray; + invalid: ReadonlyArray; + }> ) => void; const testNames = new Map(); diff --git a/tests/helpers/util.ts b/tests/helpers/util.ts index 92fd76f97..6fbc43d6b 100644 --- a/tests/helpers/util.ts +++ b/tests/helpers/util.ts @@ -1,9 +1,10 @@ -import type { TSESLint } from "@typescript-eslint/experimental-utils"; +import type { TSESLint } from "@typescript-eslint/utils"; import type { Rule, RuleTester as ESLintRuleTester } from "eslint"; +import type { ReadonlyDeep } from "type-fest"; import ts from "~/conditional-imports/typescript"; -import { filename } from "./configs"; +import { filename as dummyFilename } from "./configs"; type OptionsSet = { /** @@ -13,11 +14,14 @@ type OptionsSet = { readonly optionsSet: ReadonlyArray; }; -export type ValidTestCase = Omit & +export type ValidTestCase = Omit< + ReadonlyDeep, + "options" +> & OptionsSet; export type InvalidTestCase = Omit< - ESLintRuleTester.InvalidTestCase, + ReadonlyDeep, "options" > & OptionsSet; @@ -32,10 +36,10 @@ export function processInvalidTestCase( testCase.optionsSet.map((options) => { const { optionsSet, ...eslintTestCase } = testCase; return { - filename, + filename: dummyFilename, ...eslintTestCase, options, - }; + } as ESLintRuleTester.InvalidTestCase; }) ); } @@ -58,7 +62,7 @@ export function processValidTestCase( export function createDummyRule( create: ( /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - context: TSESLint.RuleContext<"generic", any> + context: ReadonlyDeep> ) => TSESLint.RuleListener ): Rule.RuleModule { const meta: TSESLint.RuleMetaData<"generic"> = { @@ -82,9 +86,7 @@ export function createDummyRule( } export type RuleTesterTests = { - // eslint-disable-next-line functional/prefer-readonly-type valid?: Array; - // eslint-disable-next-line functional/prefer-readonly-type invalid?: ESLintRuleTester.InvalidTestCase[]; }; @@ -93,11 +95,14 @@ export type RuleTesterTests = { */ export function addFilename( filename: string, - tests: RuleTesterTests + tests: ReadonlyDeep ): RuleTesterTests { const { valid, invalid } = tests; return { - invalid: invalid?.map((test) => ({ ...test, filename })), + invalid: invalid?.map((test) => ({ + ...(test as ESLintRuleTester.InvalidTestCase), + filename, + })), valid: valid?.map((test) => typeof test === "string" ? { code: test, filename } diff --git a/yarn.lock b/yarn.lock index e3861f744..4ee741e6d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1181,12 +1181,12 @@ dependencies: "@octokit/openapi-types" "^11.2.0" -"@rebeccastevens/eslint-config@^1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@rebeccastevens/eslint-config/-/eslint-config-1.1.5.tgz#9b640323c99f0969f0aa59a3198f5609b3f82e94" - integrity sha512-Gd/g6CqCPOuycycsD3RFAaQ84wobusEA+60dyXvODj66nLaqvS78HcCrWbL69Y28XL5SuXJB8ukB/LK52AbE3A== +"@rebeccastevens/eslint-config@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@rebeccastevens/eslint-config/-/eslint-config-1.2.1.tgz#a7d445af3e15ac5354a8f58af77f7d73be9ca197" + integrity sha512-w2Hoiv+78syG3syluWTs/oFu7MFEULJf3T8JW9pf1/ATzxRQ42QpEOV/ZSDRJohRDxnE49QJkcNzT24Zb6W+Ww== dependencies: - deepmerge-ts "^1.0.1" + deepmerge-ts "^2.0.1" "@rollup/plugin-commonjs@^21.0.0": version "21.0.1" @@ -1508,13 +1508,14 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.0.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.3.0.tgz#a55ae72d28ffeb6badd817fe4566c9cced1f5e29" - integrity sha512-ARUEJHJrq85aaiCqez7SANeahDsJTD3AEua34EoQN9pHS6S5Bq9emcIaGGySt/4X2zSi+vF5hAH52sEen7IO7g== +"@typescript-eslint/eslint-plugin@^5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.0.tgz#e90afea96dff8620892ad216b0e4ccdf8ee32d3a" + integrity sha512-XXVKnMsq2fuu9K2KsIxPUGqb6xAImz8MEChClbXmE3VbveFtBUU5bzM6IPVWqzyADIgdkS2Ws/6Xo7W2TeZWjQ== dependencies: - "@typescript-eslint/experimental-utils" "5.3.0" - "@typescript-eslint/scope-manager" "5.3.0" + "@typescript-eslint/scope-manager" "5.10.0" + "@typescript-eslint/type-utils" "5.10.0" + "@typescript-eslint/utils" "5.10.0" debug "^4.3.2" functional-red-black-tree "^1.0.1" ignore "^5.1.8" @@ -1522,94 +1523,69 @@ semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@5.3.0", "@typescript-eslint/experimental-utils@^5.0.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.3.0.tgz#ee56b4957547ed2b0fc7451205e41502e664f546" - integrity sha512-NFVxYTjKj69qB0FM+piah1x3G/63WB8vCBMnlnEHUsiLzXSTWb9FmFn36FD9Zb4APKBLY3xRArOGSMQkuzTF1w== - dependencies: - "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.3.0" - "@typescript-eslint/types" "5.3.0" - "@typescript-eslint/typescript-estree" "5.3.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" - -"@typescript-eslint/parser@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.0.0.tgz#50d1be2e0def82d73e863cceba74aeeac9973592" - integrity sha512-B6D5rmmQ14I1fdzs71eL3DAuvnPHTY/t7rQABrL9BLnx/H51Un8ox1xqYAchs0/V2trcoyxB1lMJLlrwrJCDgw== +"@typescript-eslint/parser@^5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.10.0.tgz#8f59e036f5f1cffc178cacbd5ccdd02aeb96c91c" + integrity sha512-pJB2CCeHWtwOAeIxv8CHVGJhI5FNyJAIpx5Pt72YkK3QfEzt6qAlXZuyaBmyfOdM62qU0rbxJzNToPTVeJGrQw== dependencies: - "@typescript-eslint/scope-manager" "5.0.0" - "@typescript-eslint/types" "5.0.0" - "@typescript-eslint/typescript-estree" "5.0.0" - debug "^4.3.1" + "@typescript-eslint/scope-manager" "5.10.0" + "@typescript-eslint/types" "5.10.0" + "@typescript-eslint/typescript-estree" "5.10.0" + debug "^4.3.2" -"@typescript-eslint/scope-manager@5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.0.0.tgz#aea0fb0e2480c1169a02e89d9005ac3f2835713f" - integrity sha512-5RFjdA/ain/MDUHYXdF173btOKncIrLuBmA9s6FJhzDrRAyVSA+70BHg0/MW6TE+UiKVyRtX91XpVS0gVNwVDQ== +"@typescript-eslint/scope-manager@5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.10.0.tgz#bb5d872e8b9e36203908595507fbc4d3105329cb" + integrity sha512-tgNgUgb4MhqK6DoKn3RBhyZ9aJga7EQrw+2/OiDk5hKf3pTVZWyqBi7ukP+Z0iEEDMF5FDa64LqODzlfE4O/Dg== dependencies: - "@typescript-eslint/types" "5.0.0" - "@typescript-eslint/visitor-keys" "5.0.0" - -"@typescript-eslint/scope-manager@5.3.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.3.0.tgz#97d0ccc7c9158e89e202d5e24ce6ba49052d432e" - integrity sha512-22Uic9oRlTsPppy5Tcwfj+QET5RWEnZ5414Prby465XxQrQFZ6nnm5KnXgnsAJefG4hEgMnaxTB3kNEyjdjj6A== - dependencies: - "@typescript-eslint/types" "5.3.0" - "@typescript-eslint/visitor-keys" "5.3.0" - -"@typescript-eslint/types@5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.0.0.tgz#25d93f6d269b2d25fdc51a0407eb81ccba60eb0f" - integrity sha512-dU/pKBUpehdEqYuvkojmlv0FtHuZnLXFBn16zsDmlFF3LXkOpkAQ2vrKc3BidIIve9EMH2zfTlxqw9XM0fFN5w== - -"@typescript-eslint/types@5.3.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.3.0.tgz#af29fd53867c2df0028c57c36a655bd7e9e05416" - integrity sha512-fce5pG41/w8O6ahQEhXmMV+xuh4+GayzqEogN24EK+vECA3I6pUwKuLi5QbXO721EMitpQne5VKXofPonYlAQg== + "@typescript-eslint/types" "5.10.0" + "@typescript-eslint/visitor-keys" "5.10.0" -"@typescript-eslint/typescript-estree@5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.0.0.tgz#bc20f413c6e572c7309dbe5fa3be027984952af3" - integrity sha512-V/6w+PPQMhinWKSn+fCiX5jwvd1vRBm7AX7SJQXEGQtwtBvjMPjaU3YTQ1ik2UF1u96X7tsB96HMnulG3eLi9Q== +"@typescript-eslint/type-utils@5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.10.0.tgz#8524b9479c19c478347a7df216827e749e4a51e5" + integrity sha512-TzlyTmufJO5V886N+hTJBGIfnjQDQ32rJYxPaeiyWKdjsv2Ld5l8cbS7pxim4DeNs62fKzRSt8Q14Evs4JnZyQ== dependencies: - "@typescript-eslint/types" "5.0.0" - "@typescript-eslint/visitor-keys" "5.0.0" - debug "^4.3.1" - globby "^11.0.3" - is-glob "^4.0.1" - semver "^7.3.5" + "@typescript-eslint/utils" "5.10.0" + debug "^4.3.2" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@5.3.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.3.0.tgz#4f68ddd46dc2983182402d2ab21fb44ad94988cf" - integrity sha512-FJ0nqcaUOpn/6Z4Jwbtf+o0valjBLkqc3MWkMvrhA2TvzFXtcclIM8F4MBEmYa2kgcI8EZeSAzwoSrIC8JYkug== +"@typescript-eslint/types@5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.10.0.tgz#beb3cb345076f5b088afe996d57bcd1dfddaa75c" + integrity sha512-wUljCgkqHsMZbw60IbOqT/puLfyqqD5PquGiBo1u1IS3PLxdi3RDGlyf032IJyh+eQoGhz9kzhtZa+VC4eWTlQ== + +"@typescript-eslint/typescript-estree@5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.0.tgz#4be24a3dea0f930bb1397c46187d0efdd955a224" + integrity sha512-x+7e5IqfwLwsxTdliHRtlIYkgdtYXzE0CkFeV6ytAqq431ZyxCFzNMNR5sr3WOlIG/ihVZr9K/y71VHTF/DUQA== dependencies: - "@typescript-eslint/types" "5.3.0" - "@typescript-eslint/visitor-keys" "5.3.0" + "@typescript-eslint/types" "5.10.0" + "@typescript-eslint/visitor-keys" "5.10.0" debug "^4.3.2" globby "^11.0.4" is-glob "^4.0.3" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.0.0.tgz#b789f7cd105e59bee5c0983a353942a5a48f56df" - integrity sha512-yRyd2++o/IrJdyHuYMxyFyBhU762MRHQ/bAGQeTnN3pGikfh+nEmM61XTqaDH1XDp53afZ+waXrk0ZvenoZ6xw== +"@typescript-eslint/utils@5.10.0", "@typescript-eslint/utils@^5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.10.0.tgz#c3d152a85da77c400e37281355561c72fb1b5a65" + integrity sha512-IGYwlt1CVcFoE2ueW4/ioEwybR60RAdGeiJX/iDAw0t5w0wK3S7QncDwpmsM70nKgGTuVchEWB8lwZwHqPAWRg== dependencies: - "@typescript-eslint/types" "5.0.0" - eslint-visitor-keys "^3.0.0" + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.10.0" + "@typescript-eslint/types" "5.10.0" + "@typescript-eslint/typescript-estree" "5.10.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" -"@typescript-eslint/visitor-keys@5.3.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.0.tgz#a6258790f3b7b2547f70ed8d4a1e0c3499994523" - integrity sha512-oVIAfIQuq0x2TFDNLVavUn548WL+7hdhxYn+9j3YdJJXB7mH9dAmZNJsPDa7Jc+B9WGqoiex7GUDbyMxV0a/aw== +"@typescript-eslint/visitor-keys@5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.0.tgz#770215497ad67cd15a572b52089991d5dfe06281" + integrity sha512-GMxj0K1uyrFLPKASLmZzCuSddmjZVbVj3Ouy5QVuIGKZopxvOr24JsS7gruz6C3GExE01mublZ3mIBOaon9zuQ== dependencies: - "@typescript-eslint/types" "5.3.0" + "@typescript-eslint/types" "5.10.0" eslint-visitor-keys "^3.0.0" JSONStream@^1.0.4: @@ -2947,13 +2923,6 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -deepmerge-ts@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/deepmerge-ts/-/deepmerge-ts-1.1.2.tgz#6a34d713e74987e89fff694e33b6533d83e29045" - integrity sha512-LpyBT0gwpvPd0i8tnYnqZxpeKZWAy38b0twQVsmooiOd9g3/DAl+kSJSBtdyeC5PySTneztgTFU2c3bJ5C73lA== - dependencies: - is-plain-object "^5.0.0" - deepmerge-ts@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/deepmerge-ts/-/deepmerge-ts-2.0.1.tgz#08e3bf4b535736a360c57fb162ddbffc253fe9f2" @@ -4107,7 +4076,7 @@ globals@^13.6.0, globals@^13.9.0: dependencies: type-fest "^0.20.2" -globby@^11.0.0, globby@^11.0.1, globby@^11.0.3, globby@^11.0.4: +globby@^11.0.0, globby@^11.0.1, globby@^11.0.4: version "11.0.4" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== @@ -7880,6 +7849,11 @@ type-fest@^0.8.0, type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.9.0.tgz#7a2d430dd966f52b6bc723da2aaa2c9867530551" + integrity sha512-uC0hJKi7eAGXUJ/YKk53RhnKxMwzHWgzf4t92oz8Qez28EBgVTfpDTB59y9hMYLzc/Wl85cD7Tv1hLZZoEJtrg== + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"