diff --git a/.github/ISSUE_TEMPLATE/06-bug-report-other.yaml b/.github/ISSUE_TEMPLATE/06-bug-report-other.yaml index 3eb5e066206b..59b8783d1e75 100644 --- a/.github/ISSUE_TEMPLATE/06-bug-report-other.yaml +++ b/.github/ISSUE_TEMPLATE/06-bug-report-other.yaml @@ -38,6 +38,7 @@ body: description: Select the package against which you want to report the bug. options: - ast-spec + - eslint-plugin - parser - rule-tester - scope-manager diff --git a/.github/ISSUE_TEMPLATE/07-enhancement-other.yaml b/.github/ISSUE_TEMPLATE/07-enhancement-other.yaml index 7b13e0f95272..604ce5468161 100644 --- a/.github/ISSUE_TEMPLATE/07-enhancement-other.yaml +++ b/.github/ISSUE_TEMPLATE/07-enhancement-other.yaml @@ -24,6 +24,7 @@ body: description: Select the package against which you want to report the bug. options: - ast-spec + - eslint-plugin - parser - rule-tester - scope-manager diff --git a/CHANGELOG.md b/CHANGELOG.md index cf59d2eafd8e..f6bbc3751139 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +## 8.6.0 (2024-09-16) + + +### 🚀 Features + +- add `allow` option for `restrict-template-expressions` ([#8556](https://github.com/typescript-eslint/typescript-eslint/pull/8556)) +- **eslint-plugin:** [no-unnecessary-condition] check switch cases ([#9912](https://github.com/typescript-eslint/typescript-eslint/pull/9912)) +- **eslint-plugin:** [no-misused-promises] check array predicate return ([#9955](https://github.com/typescript-eslint/typescript-eslint/pull/9955)) +- **type-utils:** isNullableType add Void logic ([#9937](https://github.com/typescript-eslint/typescript-eslint/pull/9937)) +- **typescript-estree:** disable plugin loading by default in project service ([#9964](https://github.com/typescript-eslint/typescript-eslint/pull/9964)) + +### 🩹 Fixes + +- **eslint-plugin:** [no-deprecated] don't report recursive types in destructuring assignment twice ([#9969](https://github.com/typescript-eslint/typescript-eslint/pull/9969)) +- **eslint-plugin:** [no-deprecated] report on deprecated variables used in destructuring assignment ([#9978](https://github.com/typescript-eslint/typescript-eslint/pull/9978)) +- **eslint-plugin:** [no-deprecated] report on deprecated properties with function-like types ([#9977](https://github.com/typescript-eslint/typescript-eslint/pull/9977)) +- **eslint-plugin:** [no-unnecessary-condition] properly reflect multiple negations in message ([#9940](https://github.com/typescript-eslint/typescript-eslint/pull/9940)) +- **typescript-estree:** don't throw on missing tsconfig.json by default in project service ([#9989](https://github.com/typescript-eslint/typescript-eslint/pull/9989)) + +### ❤️ Thank You + +- Abraham Guo +- auvred @auvred +- Josh Goldberg ✨ +- Kim Sang Du @developer-bandi +- YeonJuan @yeonjuan + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 8.5.0 (2024-09-09) @@ -145,6 +174,8 @@ You can read about our [versioning strategy](https://main--typescript-eslint.net # 8.0.0 (2024-07-31) +> 👉 See [Announcing typescript-eslint v8](https://typescript-eslint.io/blog/announcing-typescript-eslint-v8) for an upgrade guide and the full list of changes. + ### ⚠️ Breaking Changes - **typescript-estree:** split TSMappedType typeParameter into constraint and key ([#7065](https://github.com/typescript-eslint/typescript-eslint/pull/7065)) @@ -824,6 +855,7 @@ You can read about our [versioning strategy](https://main--typescript-eslint.net # 7.0.0 (2024-02-12) +> 👉 See [Announcing typescript-eslint v7](https://typescript-eslint.io/blog/announcing-typescript-eslint-v7) for an upgrade guide and the full list of changes. ### 🚀 Features @@ -1468,6 +1500,8 @@ You can read about our [versioning strategy](https://main--typescript-eslint.net # [6.0.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.62.0...v6.0.0) (2023-07-10) +> 👉 See [Announcing typescript-eslint v6](https://typescript-eslint.io/blog/announcing-typescript-eslint-v6) for an upgrade guide and the full list of changes. + ### Bug Fixes diff --git a/docs/developers/Custom_Rules.mdx b/docs/developers/Custom_Rules.mdx index 9e51c2b36b05..55587f433d78 100644 --- a/docs/developers/Custom_Rules.mdx +++ b/docs/developers/Custom_Rules.mdx @@ -399,7 +399,7 @@ const ruleTester = new RuleTester({ languageOptions: { parserOptions: { projectService: { - allowDefaultProjectForFiles: ['*.ts*'], + allowDefaultProject: ['*.ts*'], }, tsconfigRootDir: __dirname, }, diff --git a/docs/developers/ESLint_Plugins.mdx b/docs/developers/ESLint_Plugins.mdx index 71b41215f86a..657f5c80b24d 100644 --- a/docs/developers/ESLint_Plugins.mdx +++ b/docs/developers/ESLint_Plugins.mdx @@ -42,6 +42,9 @@ Include the following to enforce the version range allowed without making users' Those are all packages consumers are expected to be using already. +You don't need to declare any dependencies on `typescript-eslint` or `@typescript-eslint/eslint-plugin`. +Our setup guide ensures that the parser is automatically registered when configuring ESLint. + ## `RuleCreator` Usage We recommend including at least the following three properties in your plugin's [`RuleCreator` extra rule docs types](./Custom_Rules.mdx#extra-rule-docs-types): diff --git a/docs/packages/Parser.mdx b/docs/packages/Parser.mdx index da732838e4ca..12ee7c9777cd 100644 --- a/docs/packages/Parser.mdx +++ b/docs/packages/Parser.mdx @@ -369,6 +369,24 @@ It takes in a string path that will be resolved relative to the [`tsconfigRootDi `projectService.defaultProject` only impacts the "out-of-project" files included by [`allowDefaultProject`](#allowdefaultproject). +##### `loadTypeScriptPlugins` + +> Default `false` + +Whether the project service should be allowed to load [TypeScript plugins](https://www.typescriptlang.org/tsconfig/plugins.html). +This is `false` by default to prevent plugins from registering persistent file watchers or other operations that might prevent ESLint processes from exiting when run on the command-line. + +If your project is configured with custom rules that interact with TypeScript plugins, it may be useful to turn this on in your editor. +For example, only enabling this option when running within VS Code: + +```js +parserOptions: { + projectService: { + loadTypeScriptPlugins: !!process.env.VSCODE_PID, + } +} +``` + ##### `maximumDefaultProjectFileMatchCount_THIS_WILL_SLOW_DOWN_LINTING` > Default: `8`. diff --git a/docs/packages/Rule_Tester.mdx b/docs/packages/Rule_Tester.mdx index b0cf1a52068a..b5d63077803c 100644 --- a/docs/packages/Rule_Tester.mdx +++ b/docs/packages/Rule_Tester.mdx @@ -123,7 +123,7 @@ ruleTester.run('my-rule', rule, { ### Type-Aware Testing Type-aware rules can be tested in almost exactly the same way as regular code, using `parserOptions.projectService`. -Most rule tests specify `parserOptions.allowDefaultProjectForFiles: ["*.ts*"]` to enable type checking on all test files. +Most rule tests specify `parserOptions.allowDefaultProject: ["*.ts*"]` to enable type checking on all test files. You can then test your rule by providing the type-aware config: diff --git a/docs/packages/TypeScript_ESTree.mdx b/docs/packages/TypeScript_ESTree.mdx index 73e630827786..d865ebb717dc 100644 --- a/docs/packages/TypeScript_ESTree.mdx +++ b/docs/packages/TypeScript_ESTree.mdx @@ -279,6 +279,11 @@ interface ProjectServiceOptions { */ defaultProject?: string; + /** + * Whether to load TypeScript plugins as configured in the TSConfig. + */ + loadTypeScriptPlugins?: boolean; + /** * The maximum number of files {@link allowDefaultProject} may match. * Each file match slows down linting, so if you do need to use this, please diff --git a/knip.ts b/knip.ts index 8c45a68d568c..e1b4d2ced348 100644 --- a/knip.ts +++ b/knip.ts @@ -87,7 +87,7 @@ export default { '@docusaurus/mdx-loader', '@docusaurus/types', '@docusaurus/plugin-content-docs', - '@docusaurus/plugin-content-blog/client', + '@docusaurus/plugin-content-blog', '@docusaurus/theme-search-algolia', '@docusaurus/ExecutionEnvironment', '@docusaurus/Link', diff --git a/packages/ast-spec/CHANGELOG.md b/packages/ast-spec/CHANGELOG.md index c813577a3ae0..cc6fbe1a3982 100644 --- a/packages/ast-spec/CHANGELOG.md +++ b/packages/ast-spec/CHANGELOG.md @@ -1,3 +1,9 @@ +## 8.6.0 (2024-09-16) + +This was a version bump only for ast-spec to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 8.5.0 (2024-09-09) This was a version bump only for ast-spec to align it with other projects, there were no code changes. diff --git a/packages/ast-spec/package.json b/packages/ast-spec/package.json index 441379ec07cd..ca0618bdb437 100644 --- a/packages/ast-spec/package.json +++ b/packages/ast-spec/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/ast-spec", - "version": "8.5.0", + "version": "8.6.0", "description": "Complete specification for the TypeScript-ESTree AST", "private": true, "keywords": [ diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index d249560387b2..9e4a005bbfe1 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -1,3 +1,38 @@ +## 8.6.0 (2024-09-16) + + +### 🚀 Features + +- add `allow` option for `restrict-template-expressions` + +- **type-utils:** isNullableType add Void logic + +- **eslint-plugin:** [no-unnecessary-condition] check switch cases + +- **eslint-plugin:** [no-misused-promises] check array predicate return + + +### 🩹 Fixes + +- **eslint-plugin:** [no-deprecated] don't report recursive types in destructuring assignment twice + +- **eslint-plugin:** [no-deprecated] report on deprecated variables used in destructuring assignment + +- **eslint-plugin:** [no-deprecated] report on deprecated properties with function-like types + +- **eslint-plugin:** [no-unnecessary-condition] properly reflect multiple negations in message + + +### ❤️ Thank You + +- Abraham Guo +- auvred +- Josh Goldberg ✨ +- Kim Sang Du +- YeonJuan + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 8.5.0 (2024-09-09) diff --git a/packages/eslint-plugin/docs/rules/no-misused-promises.mdx b/packages/eslint-plugin/docs/rules/no-misused-promises.mdx index bc8ae0f33b6e..f4fd23fec126 100644 --- a/packages/eslint-plugin/docs/rules/no-misused-promises.mdx +++ b/packages/eslint-plugin/docs/rules/no-misused-promises.mdx @@ -134,6 +134,8 @@ if (promise) { const val = promise ? 123 : 456; +[1, 2, 3].filter(() => promise); + while (promise) { // Do something } @@ -152,6 +154,9 @@ if (await promise) { const val = (await promise) ? 123 : 456; +const returnVal = await promise; +[1, 2, 3].filter(() => returnVal); + while (await promise) { // Do something } diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-condition.mdx b/packages/eslint-plugin/docs/rules/no-unnecessary-condition.mdx index 165388ef3a0f..ec5f234ac981 100644 --- a/packages/eslint-plugin/docs/rules/no-unnecessary-condition.mdx +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-condition.mdx @@ -16,6 +16,7 @@ The following expressions are checked: - Arguments to the `&&`, `||` and `?:` (ternary) operators - Conditions for `if`, `for`, `while`, and `do-while` statements +- `case`s in `switch` statements - Base values of optional chain expressions ## Examples diff --git a/packages/eslint-plugin/docs/rules/restrict-template-expressions.mdx b/packages/eslint-plugin/docs/rules/restrict-template-expressions.mdx index 2d6b03041224..60ea75a7c65e 100644 --- a/packages/eslint-plugin/docs/rules/restrict-template-expressions.mdx +++ b/packages/eslint-plugin/docs/rules/restrict-template-expressions.mdx @@ -85,7 +85,7 @@ const msg2 = `arg = ${arg || 'not truthy'}`; ### `allowAny` -Whether to `any` typed values in template expressions. +Whether to allow `any` typed values in template expressions. Examples of additional **correct** code for this rule with `{ allowAny: true }`: @@ -124,7 +124,7 @@ const msg1 = `arg = ${arg}`; ### `allowNever` -Whether to `never` typed values in template expressions. +Whether to allow `never` typed values in template expressions. Examples of additional **correct** code for this rule with `{ allowNever: true }`: @@ -135,7 +135,7 @@ const msg1 = typeof arg === 'string' ? arg : `arg = ${arg}`; ### `allowArray` -Whether to `Array` typed values in template expressions. +Whether to allow `Array` typed values in template expressions. Examples of additional **correct** code for this rule with `{ allowArray: true }`: @@ -144,6 +144,19 @@ const arg = ['foo', 'bar']; const msg1 = `arg = ${arg}`; ``` +### `allow` + +Whether to allow additional types in template expressions. + +This option takes the shared [`TypeOrValueSpecifier` format](/packages/type-utils/type-or-value-specifier). + +Examples of additional **correct** code for this rule with the default option `{ allow: [{ from: 'lib', name: 'Error' }, { from: 'lib', name: 'URL' }, { from: 'lib', name: 'URLSearchParams' }] }`: + +```ts showPlaygroundButton +const error = new Error(); +const msg1 = `arg = ${error}`; +``` + ## When Not To Use It If you're not worried about incorrectly stringifying non-string values in template literals, then you likely don't need this rule. diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 8098f13e18b2..424409b491db 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin", - "version": "8.5.0", + "version": "8.6.0", "description": "TypeScript plugin for ESLint", "files": [ "dist", @@ -60,10 +60,10 @@ }, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.5.0", - "@typescript-eslint/type-utils": "8.5.0", - "@typescript-eslint/utils": "8.5.0", - "@typescript-eslint/visitor-keys": "8.5.0", + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/type-utils": "8.6.0", + "@typescript-eslint/utils": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -74,8 +74,8 @@ "@types/marked": "^5.0.2", "@types/mdast": "^4.0.3", "@types/natural-compare": "*", - "@typescript-eslint/rule-schema-to-typescript-types": "8.5.0", - "@typescript-eslint/rule-tester": "8.5.0", + "@typescript-eslint/rule-schema-to-typescript-types": "8.6.0", + "@typescript-eslint/rule-tester": "8.6.0", "ajv": "^6.12.6", "cross-env": "^7.0.3", "cross-fetch": "*", diff --git a/packages/eslint-plugin/src/rules/class-literal-property-style.ts b/packages/eslint-plugin/src/rules/class-literal-property-style.ts index 816c7803604a..28d4a5cc78ba 100644 --- a/packages/eslint-plugin/src/rules/class-literal-property-style.ts +++ b/packages/eslint-plugin/src/rules/class-literal-property-style.ts @@ -3,9 +3,11 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { createRule, + getStaticMemberAccessValue, getStaticStringValue, isAssignee, isFunction, + isStaticMemberAccessOfValue, nullThrows, } from '../util'; @@ -79,10 +81,6 @@ export default createRule({ create(context, [style]) { const propertiesInfoStack: PropertiesInfo[] = []; - function getStringValue(node: TSESTree.Node): string { - return getStaticStringValue(node) ?? context.sourceCode.getText(node); - } - function enterClassBody(): void { propertiesInfoStack.push({ properties: [], @@ -102,8 +100,8 @@ export default createRule({ return; } - const name = getStringValue(node.key); - if (excludeSet.has(name)) { + const name = getStaticMemberAccessValue(node, context); + if (name && excludeSet.has(name)) { return; } @@ -167,15 +165,17 @@ export default createRule({ return; } - const name = getStringValue(node.key); - - const hasDuplicateKeySetter = node.parent.body.some(element => { - return ( - element.type === AST_NODE_TYPES.MethodDefinition && - element.kind === 'set' && - getStringValue(element.key) === name - ); - }); + const name = getStaticMemberAccessValue(node, context); + + const hasDuplicateKeySetter = + name && + node.parent.body.some(element => { + return ( + element.type === AST_NODE_TYPES.MethodDefinition && + element.kind === 'set' && + isStaticMemberAccessOfValue(element, context, name) + ); + }); if (hasDuplicateKeySetter) { return; } diff --git a/packages/eslint-plugin/src/rules/class-methods-use-this.ts b/packages/eslint-plugin/src/rules/class-methods-use-this.ts index 4f574471e03b..6e27a6229f12 100644 --- a/packages/eslint-plugin/src/rules/class-methods-use-this.ts +++ b/packages/eslint-plugin/src/rules/class-methods-use-this.ts @@ -5,7 +5,7 @@ import { createRule, getFunctionHeadLoc, getFunctionNameWithKind, - getStaticStringValue, + getStaticMemberAccessValue, } from '../util'; type Options = [ @@ -182,10 +182,7 @@ export default createRule({ const hashIfNeeded = node.key.type === AST_NODE_TYPES.PrivateIdentifier ? '#' : ''; - const name = - node.key.type === AST_NODE_TYPES.Literal - ? getStaticStringValue(node.key) - : node.key.name || ''; + const name = getStaticMemberAccessValue(node, context); return !exceptMethods.has(hashIfNeeded + (name ?? '')); } diff --git a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts index 2da841b06e82..2bcd6cf4df43 100644 --- a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts +++ b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts @@ -2,7 +2,7 @@ import { DefinitionType } from '@typescript-eslint/scope-manager'; import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; -import { createRule, isFunction } from '../util'; +import { createRule, isFunction, isStaticMemberAccessOfValue } from '../util'; import type { FunctionExpression, FunctionInfo, @@ -268,21 +268,11 @@ export default createRule({ (node.type === AST_NODE_TYPES.Property && node.method) || node.type === AST_NODE_TYPES.PropertyDefinition ) { - if ( - node.key.type === AST_NODE_TYPES.Literal && - typeof node.key.value === 'string' - ) { - return options.allowedNames.includes(node.key.value); - } - if ( - node.key.type === AST_NODE_TYPES.TemplateLiteral && - node.key.expressions.length === 0 - ) { - return options.allowedNames.includes(node.key.quasis[0].value.raw); - } - if (!node.computed && node.key.type === AST_NODE_TYPES.Identifier) { - return options.allowedNames.includes(node.key.name); - } + return isStaticMemberAccessOfValue( + node, + context, + ...options.allowedNames, + ); } return false; diff --git a/packages/eslint-plugin/src/rules/no-deprecated.ts b/packages/eslint-plugin/src/rules/no-deprecated.ts index 9c9a30cc00bc..ddbe08eb0926 100644 --- a/packages/eslint-plugin/src/rules/no-deprecated.ts +++ b/packages/eslint-plugin/src/rules/no-deprecated.ts @@ -52,19 +52,17 @@ export default createRule({ return parent.key === node; case AST_NODE_TYPES.Property: + // foo in "const { foo } = bar" will be processed twice, as parent.key + // and parent.value. The second is treated as a declaration. return ( (parent.shorthand && parent.value === node) || parent.parent.type === AST_NODE_TYPES.ObjectExpression ); case AST_NODE_TYPES.AssignmentPattern: - return ( - parent.left === node && - !( - parent.parent.type === AST_NODE_TYPES.Property && - parent.parent.shorthand - ) - ); + // foo in "const { foo = "" } = bar" will be processed twice, as parent.parent.key + // and parent.left. The second is treated as a declaration. + return parent.left === node; case AST_NODE_TYPES.ArrowFunctionExpression: case AST_NODE_TYPES.FunctionDeclaration: @@ -174,15 +172,32 @@ export default createRule({ const signature = checker.getResolvedSignature( tsNode as ts.CallLikeExpression, ); + const symbol = services.getSymbolAtLocation(node); if (signature) { const signatureDeprecation = getJsDocDeprecation(signature); if (signatureDeprecation !== undefined) { return signatureDeprecation; } + + // Properties with function-like types have "deprecated" jsdoc + // on their symbols, not on their signatures: + // + // interface Props { + // /** @deprecated */ + // property: () => 'foo' + // ^symbol^ ^signature^ + // } + const symbolDeclarationKind = symbol?.declarations?.[0].kind; + if ( + symbolDeclarationKind !== ts.SyntaxKind.MethodDeclaration && + symbolDeclarationKind !== ts.SyntaxKind.FunctionDeclaration && + symbolDeclarationKind !== ts.SyntaxKind.MethodSignature + ) { + return getJsDocDeprecation(symbol); + } } // Or it could be a ClassDeclaration or a variable set to a ClassExpression. - const symbol = services.getSymbolAtLocation(node); const symbolAtLocation = symbol && checker.getTypeOfSymbolAtLocation(symbol, tsNode).getSymbol(); @@ -195,10 +210,7 @@ export default createRule({ function getSymbol( node: IdentifierLike, ): ts.Signature | ts.Symbol | undefined { - if ( - node.parent.type === AST_NODE_TYPES.AssignmentPattern || - node.parent.type === AST_NODE_TYPES.Property - ) { + if (node.parent.type === AST_NODE_TYPES.Property) { return services .getTypeAtLocation(node.parent.parent) .getProperty(node.name); diff --git a/packages/eslint-plugin/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts index 3b0265e1718f..9c1cc46525c4 100644 --- a/packages/eslint-plugin/src/rules/no-floating-promises.ts +++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts @@ -8,11 +8,12 @@ import { createRule, getOperatorPrecedence, getParserServices, + getStaticMemberAccessValue, isBuiltinSymbolLike, OperatorPrecedence, readonlynessOptionsDefaults, readonlynessOptionsSchema, - typeMatchesSpecifier, + typeMatchesSomeSpecifier, } from '../util'; type Options = [ @@ -238,8 +239,10 @@ export default createRule({ const type = services.getTypeAtLocation(node.callee); - return allowForKnownSafeCalls.some(allowedType => - typeMatchesSpecifier(type, allowedType, services.program), + return typeMatchesSomeSpecifier( + type, + allowForKnownSafeCalls, + services.program, ); } @@ -334,27 +337,38 @@ export default createRule({ // If the outer expression is a call, a `.catch()` or `.then()` with // rejection handler handles the promise. - const catchRejectionHandler = getRejectionHandlerFromCatchCall(node); - if (catchRejectionHandler) { - if (isValidRejectionHandler(catchRejectionHandler)) { - return { isUnhandled: false }; + const { callee } = node; + if (callee.type === AST_NODE_TYPES.MemberExpression) { + const methodName = getStaticMemberAccessValue(callee, context); + const catchRejectionHandler = + methodName === 'catch' && node.arguments.length >= 1 + ? node.arguments[0] + : undefined; + if (catchRejectionHandler) { + if (isValidRejectionHandler(catchRejectionHandler)) { + return { isUnhandled: false }; + } + return { isUnhandled: true, nonFunctionHandler: true }; } - return { isUnhandled: true, nonFunctionHandler: true }; - } - const thenRejectionHandler = getRejectionHandlerFromThenCall(node); - if (thenRejectionHandler) { - if (isValidRejectionHandler(thenRejectionHandler)) { - return { isUnhandled: false }; + const thenRejectionHandler = + methodName === 'then' && node.arguments.length >= 2 + ? node.arguments[1] + : undefined; + if (thenRejectionHandler) { + if (isValidRejectionHandler(thenRejectionHandler)) { + return { isUnhandled: false }; + } + return { isUnhandled: true, nonFunctionHandler: true }; } - return { isUnhandled: true, nonFunctionHandler: true }; - } - // `x.finally()` is transparent to resolution of the promise, so check `x`. - // ("object" in this context is the `x` in `x.finally()`) - const promiseFinallyObject = getObjectFromFinallyCall(node); - if (promiseFinallyObject) { - return isUnhandledPromise(checker, promiseFinallyObject); + // `x.finally()` is transparent to resolution of the promise, so check `x`. + // ("object" in this context is the `x` in `x.finally()`) + const promiseFinallyObject = + methodName === 'finally' ? callee.object : undefined; + if (promiseFinallyObject) { + return isUnhandledPromise(checker, promiseFinallyObject); + } } // All other cases are unhandled. @@ -407,8 +421,10 @@ export default createRule({ // The highest priority is to allow anything allowlisted if ( - allowForKnownSafePromises.some(allowedType => - typeMatchesSpecifier(type, allowedType, services.program), + typeMatchesSomeSpecifier( + type, + allowForKnownSafePromises, + services.program, ) ) { return false; @@ -485,41 +501,3 @@ function isFunctionParam( } return false; } - -function getRejectionHandlerFromCatchCall( - expression: TSESTree.CallExpression, -): TSESTree.CallExpressionArgument | undefined { - if ( - expression.callee.type === AST_NODE_TYPES.MemberExpression && - expression.callee.property.type === AST_NODE_TYPES.Identifier && - expression.callee.property.name === 'catch' && - expression.arguments.length >= 1 - ) { - return expression.arguments[0]; - } - return undefined; -} - -function getRejectionHandlerFromThenCall( - expression: TSESTree.CallExpression, -): TSESTree.CallExpressionArgument | undefined { - if ( - expression.callee.type === AST_NODE_TYPES.MemberExpression && - expression.callee.property.type === AST_NODE_TYPES.Identifier && - expression.callee.property.name === 'then' && - expression.arguments.length >= 2 - ) { - return expression.arguments[1]; - } - return undefined; -} - -function getObjectFromFinallyCall( - expression: TSESTree.CallExpression, -): TSESTree.Expression | undefined { - return expression.callee.type === AST_NODE_TYPES.MemberExpression && - expression.callee.property.type === AST_NODE_TYPES.Identifier && - expression.callee.property.name === 'finally' - ? expression.callee.object - : undefined; -} diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index c6e0978f1cb0..9edd31141e0a 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -6,6 +6,7 @@ import * as ts from 'typescript'; import { createRule, getParserServices, + isArrayMethodCallWithPredicate, isFunction, isRestParameterDeclaration, nullThrows, @@ -31,6 +32,7 @@ interface ChecksVoidReturnOptions { type MessageId = | 'conditional' + | 'predicate' | 'spread' | 'voidReturnArgument' | 'voidReturnAttribute' @@ -91,6 +93,7 @@ export default createRule({ voidReturnVariable: 'Promise-returning function provided to variable where a void return was expected.', conditional: 'Expected non-Promise value in a boolean conditional.', + predicate: 'Expected a non-Promise value to be returned.', spread: 'Expected a non-Promise value to be spreaded in an object.', }, schema: [ @@ -175,6 +178,7 @@ export default createRule({ checkConditional(node.argument, true); }, WhileStatement: checkTestConditional, + 'CallExpression > MemberExpression': checkArrayPredicates, }; checksVoidReturn = parseChecksVoidReturn(checksVoidReturn); @@ -322,6 +326,25 @@ export default createRule({ } } + function checkArrayPredicates(node: TSESTree.MemberExpression): void { + const parent = node.parent; + if (parent.type === AST_NODE_TYPES.CallExpression) { + const callback = parent.arguments.at(0); + if ( + callback && + isArrayMethodCallWithPredicate(context, services, parent) + ) { + const type = services.esTreeNodeToTSNodeMap.get(callback); + if (returnsThenable(checker, type)) { + context.report({ + messageId: 'predicate', + node: callback, + }); + } + } + } + } + function checkArguments( node: TSESTree.CallExpression | TSESTree.NewExpression, ): void { diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index 8f193dde47fa..035ba000f33d 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -9,6 +9,7 @@ import { getParserServices, getTypeName, getTypeOfPropertyOfName, + isArrayMethodCallWithPredicate, isIdentifier, isNullableType, isTypeAnyType, @@ -233,21 +234,22 @@ export default createRule({ * if the type of the node is always true or always false, it's not necessary. */ function checkNode( - node: TSESTree.Expression, + expression: TSESTree.Expression, isUnaryNotArgument = false, + node = expression, ): void { // Check if the node is Unary Negation expression and handle it if ( - node.type === AST_NODE_TYPES.UnaryExpression && - node.operator === '!' + expression.type === AST_NODE_TYPES.UnaryExpression && + expression.operator === '!' ) { - return checkNode(node.argument, true); + return checkNode(expression.argument, !isUnaryNotArgument, node); } // Since typescript array index signature types don't represent the // possibility of out-of-bounds access, if we're indexing into an array // just skip the check, to avoid false positives - if (isArrayIndexExpression(node)) { + if (isArrayIndexExpression(expression)) { return; } @@ -258,13 +260,13 @@ export default createRule({ // boolean checks if we inspect the right here, it'll usually be a constant condition on purpose. // In this case it's better to inspect the type of the expression as a whole. if ( - node.type === AST_NODE_TYPES.LogicalExpression && - node.operator !== '??' + expression.type === AST_NODE_TYPES.LogicalExpression && + expression.operator !== '??' ) { - return checkNode(node.right); + return checkNode(expression.right); } - const type = getConstrainedTypeAtLocation(services, node); + const type = getConstrainedTypeAtLocation(services, expression); // Conditional is always necessary if it involves: // `any` or `unknown` or a naked type variable @@ -362,15 +364,18 @@ export default createRule({ '===', '!=', '!==', - ]); - function checkIfBinaryExpressionIsNecessaryConditional( - node: TSESTree.BinaryExpression, + ] as const); + type BoolOperator = Parameters[0]; + const isBoolOperator = (operator: string): operator is BoolOperator => + (BOOL_OPERATORS as Set).has(operator); + function checkIfBoolExpressionIsNecessaryConditional( + node: TSESTree.Node, + left: TSESTree.Node, + right: TSESTree.Node, + operator: BoolOperator, ): void { - if (!BOOL_OPERATORS.has(node.operator)) { - return; - } - const leftType = getConstrainedTypeAtLocation(services, node.left); - const rightType = getConstrainedTypeAtLocation(services, node.right); + const leftType = getConstrainedTypeAtLocation(services, left); + const rightType = getConstrainedTypeAtLocation(services, right); if (isLiteral(leftType) && isLiteral(rightType)) { context.report({ node, messageId: 'literalBooleanExpression' }); return; @@ -389,7 +394,7 @@ export default createRule({ ts.TypeFlags.TypeVariable; // Allow loose comparison to nullish values. - if (node.operator === '==' || node.operator === '!=') { + if (operator === '==' || operator === '!=') { flag |= NULL | UNDEFINED | VOID; } @@ -457,26 +462,12 @@ export default createRule({ checkNode(node.test); } - const ARRAY_PREDICATE_FUNCTIONS = new Set([ - 'filter', - 'find', - 'some', - 'every', - ]); - function isArrayPredicateFunction(node: TSESTree.CallExpression): boolean { - const { callee } = node; - return ( - // looks like `something.filter` or `something.find` - callee.type === AST_NODE_TYPES.MemberExpression && - callee.property.type === AST_NODE_TYPES.Identifier && - ARRAY_PREDICATE_FUNCTIONS.has(callee.property.name) && - // and the left-hand side is an array, according to the types - (nodeIsArrayType(callee.object) || nodeIsTupleType(callee.object)) - ); - } function checkCallExpression(node: TSESTree.CallExpression): void { // If this is something like arr.filter(x => /*condition*/), check `condition` - if (isArrayPredicateFunction(node) && node.arguments.length) { + if ( + isArrayMethodCallWithPredicate(context, services, node) && + node.arguments.length + ) { const callback = node.arguments[0]; // Inline defined functions if ( @@ -644,10 +635,9 @@ export default createRule({ ? !isCallExpressionNullableOriginFromCallee(node) : true; - const possiblyVoid = isTypeFlagSet(type, ts.TypeFlags.Void); return ( isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown) || - (isOwnNullable && (isNullableType(type) || possiblyVoid)) + (isOwnNullable && isNullableType(type)) ); } @@ -719,13 +709,34 @@ export default createRule({ return { AssignmentExpression: checkAssignmentExpression, - BinaryExpression: checkIfBinaryExpressionIsNecessaryConditional, + BinaryExpression(node): void { + const { operator } = node; + if (isBoolOperator(operator)) { + checkIfBoolExpressionIsNecessaryConditional( + node, + node.left, + node.right, + operator, + ); + } + }, CallExpression: checkCallExpression, ConditionalExpression: (node): void => checkNode(node.test), DoWhileStatement: checkIfLoopIsNecessaryConditional, ForStatement: checkIfLoopIsNecessaryConditional, IfStatement: (node): void => checkNode(node.test), LogicalExpression: checkLogicalExpressionForUnnecessaryConditionals, + SwitchCase({ test, parent }): void { + // only check `case ...:`, not `default:` + if (test) { + checkIfBoolExpressionIsNecessaryConditional( + test, + parent.discriminant, + test, + '===', + ); + } + }, WhileStatement: checkIfLoopIsNecessaryConditional, 'MemberExpression[optional = true]': checkOptionalMemberExpression, 'CallExpression[optional = true]': checkOptionalCallExpression, diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts index 59f216fb1c3e..0d3d4122a859 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -243,7 +243,7 @@ export default createRule({ const type = getConstrainedTypeAtLocation(services, node.expression); - if (!isNullableType(type) && !isTypeFlagSet(type, ts.TypeFlags.Void)) { + if (!isNullableType(type)) { if ( node.expression.type === AST_NODE_TYPES.Identifier && isPossiblyUsedBeforeAssigned(node.expression) diff --git a/packages/eslint-plugin/src/rules/prefer-find.ts b/packages/eslint-plugin/src/rules/prefer-find.ts index 7d1c6fed86dc..389d4f581378 100644 --- a/packages/eslint-plugin/src/rules/prefer-find.ts +++ b/packages/eslint-plugin/src/rules/prefer-find.ts @@ -1,6 +1,6 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; -import type { RuleFix, Scope } from '@typescript-eslint/utils/ts-eslint'; +import type { RuleFix } from '@typescript-eslint/utils/ts-eslint'; import * as tsutils from 'ts-api-utils'; import type { Type } from 'typescript'; @@ -9,6 +9,7 @@ import { getConstrainedTypeAtLocation, getParserServices, getStaticValue, + isStaticMemberAccessOfValue, nullThrows, } from '../util'; @@ -89,7 +90,7 @@ export default createRule({ // or the optional chaining variants. if (callee.type === AST_NODE_TYPES.MemberExpression) { const isBracketSyntaxForFilter = callee.computed; - if (isStaticMemberAccessOfValue(callee, 'filter', globalScope)) { + if (isStaticMemberAccessOfValue(callee, context, 'filter')) { const filterNode = callee.property; const filteredObjectType = getConstrainedTypeAtLocation( @@ -162,7 +163,7 @@ export default createRule({ if ( callee.type === AST_NODE_TYPES.MemberExpression && !callee.optional && - isStaticMemberAccessOfValue(callee, 'at', globalScope) + isStaticMemberAccessOfValue(callee, context, 'at') ) { const atArgument = getStaticValue(node.arguments[0], globalScope); if (atArgument != null && isTreatedAsZeroByArrayAt(atArgument.value)) { @@ -321,25 +322,3 @@ export default createRule({ }; }, }); - -/** - * Answers whether the member expression looks like - * `x.memberName`, `x['memberName']`, - * or even `const mn = 'memberName'; x[mn]` (or optional variants thereof). - */ -function isStaticMemberAccessOfValue( - memberExpression: - | TSESTree.MemberExpressionComputedName - | TSESTree.MemberExpressionNonComputedName, - value: string, - scope?: Scope.Scope, -): boolean { - if (!memberExpression.computed) { - // x.memberName case. - return memberExpression.property.name === value; - } - - // x['memberName'] cases. - const staticValueResult = getStaticValue(memberExpression.property, scope); - return staticValueResult != null && value === staticValueResult.value; -} diff --git a/packages/eslint-plugin/src/rules/prefer-includes.ts b/packages/eslint-plugin/src/rules/prefer-includes.ts index 1a8f705b312f..98a24fcc8917 100644 --- a/packages/eslint-plugin/src/rules/prefer-includes.ts +++ b/packages/eslint-plugin/src/rules/prefer-includes.ts @@ -8,6 +8,7 @@ import { getConstrainedTypeAtLocation, getParserServices, getStaticValue, + isStaticMemberAccessOfValue, } from '../util'; export default createRule({ @@ -146,6 +147,9 @@ export default createRule({ node: TSESTree.MemberExpression, allowFixing: boolean, ): void { + if (!isStaticMemberAccessOfValue(node, context, 'indexOf')) { + return; + } // Check if the comparison is equivalent to `includes()`. const callNode = node.parent as TSESTree.CallExpression; const compareNode = ( @@ -204,14 +208,14 @@ export default createRule({ return { // a.indexOf(b) !== 1 - "BinaryExpression > CallExpression.left > MemberExpression.callee[property.name='indexOf'][computed=false]"( + 'BinaryExpression > CallExpression.left > MemberExpression'( node: TSESTree.MemberExpression, ): void { checkArrayIndexOf(node, /* allowFixing */ true); }, // a?.indexOf(b) !== 1 - "BinaryExpression > ChainExpression.left > CallExpression > MemberExpression.callee[property.name='indexOf'][computed=false]"( + 'BinaryExpression > ChainExpression.left > CallExpression > MemberExpression'( node: TSESTree.MemberExpression, ): void { checkArrayIndexOf(node, /* allowFixing */ false); diff --git a/packages/eslint-plugin/src/rules/prefer-promise-reject-errors.ts b/packages/eslint-plugin/src/rules/prefer-promise-reject-errors.ts index ae5a344a299e..acad1d6ef8f3 100644 --- a/packages/eslint-plugin/src/rules/prefer-promise-reject-errors.ts +++ b/packages/eslint-plugin/src/rules/prefer-promise-reject-errors.ts @@ -10,6 +10,7 @@ import { isPromiseConstructorLike, isPromiseLike, isReadonlyErrorLike, + isStaticMemberAccessOfValue, } from '../util'; export type MessageIds = 'rejectAnError'; @@ -99,13 +100,8 @@ export default createRule({ return; } - const rejectMethodCalled = callee.computed - ? callee.property.type === AST_NODE_TYPES.Literal && - callee.property.value === 'reject' - : callee.property.name === 'reject'; - if ( - !rejectMethodCalled || + !isStaticMemberAccessOfValue(callee, context, 'reject') || !typeAtLocationIsLikePromise(callee.object) ) { return; diff --git a/packages/eslint-plugin/src/rules/prefer-reduce-type-parameter.ts b/packages/eslint-plugin/src/rules/prefer-reduce-type-parameter.ts index 7eb781ed0d18..a8c820a7b370 100644 --- a/packages/eslint-plugin/src/rules/prefer-reduce-type-parameter.ts +++ b/packages/eslint-plugin/src/rules/prefer-reduce-type-parameter.ts @@ -1,5 +1,4 @@ import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import type * as ts from 'typescript'; @@ -7,6 +6,7 @@ import { createRule, getConstrainedTypeAtLocation, getParserServices, + isStaticMemberAccessOfValue, isTypeAssertion, } from '../util'; @@ -14,23 +14,6 @@ type MemberExpressionWithCallExpressionParent = TSESTree.MemberExpression & { parent: TSESTree.CallExpression; }; -const getMemberExpressionName = ( - member: TSESTree.MemberExpression, -): string | null => { - if (!member.computed) { - return member.property.name; - } - - if ( - member.property.type === AST_NODE_TYPES.Literal && - typeof member.property.value === 'string' - ) { - return member.property.value; - } - - return null; -}; - export default createRule({ name: 'prefer-reduce-type-parameter', meta: { @@ -67,7 +50,7 @@ export default createRule({ 'CallExpression > MemberExpression.callee'( callee: MemberExpressionWithCallExpressionParent, ): void { - if (getMemberExpressionName(callee) !== 'reduce') { + if (!isStaticMemberAccessOfValue(callee, context, 'reduce')) { return; } diff --git a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts index b19e0305c7bb..9254589a624e 100644 --- a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts +++ b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts @@ -9,6 +9,7 @@ import { getStaticValue, getTypeName, getWrappingFixer, + isStaticMemberAccessOfValue, } from '../util'; enum ArgumentType { @@ -98,9 +99,12 @@ export default createRule({ } return { - "CallExpression[arguments.length=1] > MemberExpression.callee[property.name='match'][computed=false]"( + 'CallExpression[arguments.length=1] > MemberExpression'( memberNode: TSESTree.MemberExpression, ): void { + if (!isStaticMemberAccessOfValue(memberNode, context, 'match')) { + return; + } const objectNode = memberNode.object; const callNode = memberNode.parent as TSESTree.CallExpression; const [argumentNode] = callNode.arguments; diff --git a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts index f6a22152043c..d65fd9736ad5 100644 --- a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts +++ b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts @@ -9,6 +9,7 @@ import { getStaticValue, getTypeName, isNotClosingParenToken, + isStaticMemberAccessOfValue, nullThrows, NullThrowsReasons, } from '../util'; @@ -580,11 +581,12 @@ export default createRule({ // foo.substring(foo.length - 3) === 'bar' // foo.substring(foo.length - 3, foo.length) === 'bar' [[ - 'BinaryExpression > CallExpression.left > MemberExpression.callee[property.name="slice"][computed=false]', - 'BinaryExpression > CallExpression.left > MemberExpression.callee[property.name="substring"][computed=false]', - 'BinaryExpression > ChainExpression.left > CallExpression > MemberExpression.callee[property.name="slice"][computed=false]', - 'BinaryExpression > ChainExpression.left > CallExpression > MemberExpression.callee[property.name="substring"][computed=false]', + 'BinaryExpression > CallExpression.left > MemberExpression', + 'BinaryExpression > ChainExpression.left > CallExpression > MemberExpression', ].join(', ')](node: TSESTree.MemberExpression): void { + if (!isStaticMemberAccessOfValue(node, context, 'slice', 'substring')) { + return; + } const callNode = getParent(node) as TSESTree.CallExpression; const parentNode = getParent(callNode); diff --git a/packages/eslint-plugin/src/rules/require-array-sort-compare.ts b/packages/eslint-plugin/src/rules/require-array-sort-compare.ts index e6f83e800bbb..ec1f1e1fdb15 100644 --- a/packages/eslint-plugin/src/rules/require-array-sort-compare.ts +++ b/packages/eslint-plugin/src/rules/require-array-sort-compare.ts @@ -5,6 +5,7 @@ import { getConstrainedTypeAtLocation, getParserServices, getTypeName, + isStaticMemberAccessOfValue, isTypeArrayTypeOrUnionOfArrayTypes, } from '../util'; @@ -66,6 +67,9 @@ export default createRule({ } function checkSortArgument(callee: TSESTree.MemberExpression): void { + if (!isStaticMemberAccessOfValue(callee, context, 'sort', 'toSorted')) { + return; + } const calleeObjType = getConstrainedTypeAtLocation( services, callee.object, @@ -81,9 +85,7 @@ export default createRule({ } return { - "CallExpression[arguments.length=0] > MemberExpression[property.name='sort'][computed=false]": - checkSortArgument, - "CallExpression[arguments.length=0] > MemberExpression[property.name='toSorted'][computed=false]": + 'CallExpression[arguments.length=0] > MemberExpression': checkSortArgument, }; }, diff --git a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts index 5aa90084ac63..54b94aa0b193 100644 --- a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts +++ b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts @@ -1,8 +1,13 @@ +import { + typeMatchesSomeSpecifier, + typeOrValueSpecifiersSchema, +} from '@typescript-eslint/type-utils'; import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { Type, TypeChecker } from 'typescript'; import { TypeFlags } from 'typescript'; +import type { TypeOrValueSpecifier } from '../util'; import { createRule, getConstrainedTypeAtLocation, @@ -43,14 +48,16 @@ const optionTesters = ( (type, checker): boolean => getTypeName(checker, type) === 'RegExp', ], ['Never', isTypeNeverType], - ] satisfies [string, OptionTester][] + ] as const satisfies [string, OptionTester][] ).map(([type, tester]) => ({ type, option: `allow${type}` as const, tester, })); type Options = [ - { [Type in (typeof optionTesters)[number]['option']]?: boolean }, + { [Type in (typeof optionTesters)[number]['option']]?: boolean } & { + allow?: TypeOrValueSpecifier[]; + }, ]; type MessageId = 'invalidType'; @@ -84,15 +91,21 @@ export default createRule({ { type: 'object', additionalProperties: false, - properties: Object.fromEntries( - optionTesters.map(({ option, type }) => [ - option, - { - description: `Whether to allow \`${type.toLowerCase()}\` typed values in template expressions.`, - type: 'boolean', - }, - ]), - ), + properties: { + ...Object.fromEntries( + optionTesters.map(({ option, type }) => [ + option, + { + description: `Whether to allow \`${type.toLowerCase()}\` typed values in template expressions.`, + type: 'boolean', + }, + ]), + ), + allow: { + description: `Types to allow in template expressions.`, + ...typeOrValueSpecifiersSchema, + }, + }, }, ], }, @@ -103,11 +116,13 @@ export default createRule({ allowNullish: true, allowNumber: true, allowRegExp: true, + allow: [{ from: 'lib', name: ['Error', 'URL', 'URLSearchParams'] }], }, ], - create(context, [options]) { + create(context, [{ allow, ...options }]) { const services = getParserServices(context); - const checker = services.program.getTypeChecker(); + const { program } = services; + const checker = program.getTypeChecker(); const enabledOptionTesters = optionTesters.filter( ({ option }) => options[option], ); @@ -147,6 +162,7 @@ export default createRule({ return ( isTypeFlagSet(innerType, TypeFlags.StringLike) || + typeMatchesSomeSpecifier(innerType, allow, program) || enabledOptionTesters.some(({ tester }) => tester(innerType, checker, recursivelyCheckType), ) diff --git a/packages/eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts b/packages/eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts index 0c9cf63adf2b..a6060cd3f00e 100644 --- a/packages/eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts +++ b/packages/eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts @@ -1,4 +1,3 @@ -import type { Scope } from '@typescript-eslint/scope-manager'; import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import type { ReportDescriptor } from '@typescript-eslint/utils/ts-eslint'; @@ -8,7 +7,7 @@ import type * as ts from 'typescript'; import { createRule, getParserServices, - getStaticValue, + getStaticMemberAccessValue, isParenlessArrowFunction, isRestParameterDeclaration, nullThrows, @@ -26,19 +25,6 @@ type MessageIds = const useUnknownMessageBase = 'Prefer the safe `: unknown` for a `{{method}}`{{append}} callback variable.'; -/** - * `x.memberName` => 'memberKey' - * - * `const mk = 'memberKey'; x[mk]` => 'memberKey' - * - * `const mk = 1234; x[mk]` => 1234 - */ -const getStaticMemberAccessKey = ( - { computed, property }: TSESTree.MemberExpression, - scope: Scope, -): { value: unknown } | null => - computed ? getStaticValue(property, scope) : { value: property.name }; - export default createRule<[], MessageIds>({ name: 'use-unknown-in-catch-callback-variable', meta: { @@ -242,9 +228,9 @@ export default createRule<[], MessageIds>({ return; } - const staticMemberAccessKey = getStaticMemberAccessKey( + const staticMemberAccessKey = getStaticMemberAccessValue( callee, - context.sourceCode.getScope(callee), + context, ); if (!staticMemberAccessKey) { return; @@ -259,7 +245,7 @@ export default createRule<[], MessageIds>({ append: string; argIndexToCheck: number; }[] - ).find(({ method }) => staticMemberAccessKey.value === method); + ).find(({ method }) => staticMemberAccessKey === method); if (!promiseMethodInfo) { return; } diff --git a/packages/eslint-plugin/src/util/index.ts b/packages/eslint-plugin/src/util/index.ts index 58f34597653b..b13b5855231d 100644 --- a/packages/eslint-plugin/src/util/index.ts +++ b/packages/eslint-plugin/src/util/index.ts @@ -21,6 +21,7 @@ export * from './scopeUtils'; export * from './types'; export * from './isAssignee'; export * from './getFixOrSuggest'; +export * from './isArrayMethodCallWithPredicate'; // this is done for convenience - saves migrating all of the old rules export * from '@typescript-eslint/type-utils'; diff --git a/packages/eslint-plugin/src/util/isArrayMethodCallWithPredicate.ts b/packages/eslint-plugin/src/util/isArrayMethodCallWithPredicate.ts new file mode 100644 index 000000000000..746e9003722c --- /dev/null +++ b/packages/eslint-plugin/src/util/isArrayMethodCallWithPredicate.ts @@ -0,0 +1,43 @@ +import { getConstrainedTypeAtLocation } from '@typescript-eslint/type-utils'; +import type { + ParserServicesWithTypeInformation, + TSESTree, +} from '@typescript-eslint/utils'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; +import type { RuleContext } from '@typescript-eslint/utils/ts-eslint'; +import * as tsutils from 'ts-api-utils'; + +import { getStaticMemberAccessValue } from './misc'; + +const ARRAY_PREDICATE_FUNCTIONS = new Set([ + 'filter', + 'find', + 'findIndex', + 'findLast', + 'findLastIndex', + 'some', + 'every', +]); + +export function isArrayMethodCallWithPredicate( + context: RuleContext, + services: ParserServicesWithTypeInformation, + node: TSESTree.CallExpression, +): boolean { + if (node.callee.type !== AST_NODE_TYPES.MemberExpression) { + return false; + } + + const staticAccessValue = getStaticMemberAccessValue(node.callee, context); + + if (!staticAccessValue || !ARRAY_PREDICATE_FUNCTIONS.has(staticAccessValue)) { + return false; + } + + const checker = services.program.getTypeChecker(); + const type = getConstrainedTypeAtLocation(services, node.callee.object); + return tsutils + .unionTypeParts(type) + .flatMap(part => tsutils.intersectionTypeParts(part)) + .some(t => checker.isArrayType(t) || checker.isTupleType(t)); +} diff --git a/packages/eslint-plugin/src/util/misc.ts b/packages/eslint-plugin/src/util/misc.ts index 64f23f2e81bc..aeed9f96d039 100644 --- a/packages/eslint-plugin/src/util/misc.ts +++ b/packages/eslint-plugin/src/util/misc.ts @@ -1,13 +1,13 @@ /** * @fileoverview Really small utility functions that didn't deserve their own files */ - import { requiresQuoting } from '@typescript-eslint/type-utils'; import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; +import type { RuleContext } from '@typescript-eslint/utils/ts-eslint'; import * as ts from 'typescript'; -import { isParenthesized } from './astUtils'; +import { getStaticValue, isParenthesized } from './astUtils'; const DEFINITION_EXTENSIONS = [ ts.Extension.Dts, @@ -232,6 +232,46 @@ function isParenlessArrowFunction( ); } +type NodeWithKey = + | TSESTree.MemberExpression + | TSESTree.MethodDefinition + | TSESTree.Property + | TSESTree.PropertyDefinition + | TSESTree.TSAbstractMethodDefinition + | TSESTree.TSAbstractPropertyDefinition; +function getStaticMemberAccessValue( + node: NodeWithKey, + { sourceCode }: RuleContext, +): string | undefined { + const key = + node.type === AST_NODE_TYPES.MemberExpression ? node.property : node.key; + if (!node.computed) { + return key.type === AST_NODE_TYPES.Literal + ? `${key.value}` + : (key as TSESTree.Identifier | TSESTree.PrivateIdentifier).name; + } + const value = getStaticValue(key, sourceCode.getScope(node))?.value as + | string + | number + | null + | undefined; + return value == null ? undefined : `${value}`; +} + +/** + * Answers whether the member expression looks like + * `x.memberName`, `x['memberName']`, + * or even `const mn = 'memberName'; x[mn]` (or optional variants thereof). + */ +const isStaticMemberAccessOfValue = ( + memberExpression: NodeWithKey, + context: RuleContext, + ...values: string[] +): boolean => + (values as (string | undefined)[]).includes( + getStaticMemberAccessValue(memberExpression, context), + ); + export { arrayGroupByToMap, arraysAreEqual, @@ -240,11 +280,13 @@ export { findFirstResult, formatWordList, getEnumNames, + getStaticMemberAccessValue, getNameFromIndexSignature, getNameFromMember, isDefinitionFile, isRestParameterDeclaration, isParenlessArrowFunction, + isStaticMemberAccessOfValue, MemberNameType, type RequireKeys, typeNodeRequiresParentheses, diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-misused-promises.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-misused-promises.shot index 17d39d27c875..8c9f4989d46b 100644 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-misused-promises.shot +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-misused-promises.shot @@ -14,6 +14,9 @@ if (promise) { const val = promise ? 123 : 456; ~~~~~~~ Expected non-Promise value in a boolean conditional. +[1, 2, 3].filter(() => promise); + ~~~~~~~~~~~~~ Expected a non-Promise value to be returned. + while (promise) { ~~~~~~~ Expected non-Promise value in a boolean conditional. // Do something @@ -34,6 +37,9 @@ if (await promise) { const val = (await promise) ? 123 : 456; +const returnVal = await promise; +[1, 2, 3].filter(() => returnVal); + while (await promise) { // Do something } diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/restrict-template-expressions.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/restrict-template-expressions.shot index 06f982288ebc..18fcf8c5ba31 100644 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/restrict-template-expressions.shot +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/restrict-template-expressions.shot @@ -91,3 +91,11 @@ const arg = ['foo', 'bar']; const msg1 = \`arg = \${arg}\`; " `; + +exports[`Validating rule docs restrict-template-expressions.mdx code examples ESLint output 11`] = ` +" + +const error = new Error(); +const msg1 = \`arg = \${error}\`; +" +`; diff --git a/packages/eslint-plugin/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts index ee518640efed..fd8405d62833 100644 --- a/packages/eslint-plugin/tests/docs.test.ts +++ b/packages/eslint-plugin/tests/docs.test.ts @@ -153,6 +153,7 @@ describe('Validating rule docs', () => { const ignoredFiles = new Set([ 'README.md', 'TEMPLATE.md', + 'shared', // These rule docs were left behind on purpose for legacy reasons. See the // comments in the files for more information. 'ban-types.md', diff --git a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts index fc7bbe927d41..bdfe10135349 100644 --- a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts +++ b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts @@ -100,6 +100,32 @@ ruleTester.run('no-deprecated', rule, { a('b'); `, + ` + class A { + a(value: 'b'): void; + /** @deprecated */ + a(value: 'c'): void; + } + declare const foo: A; + foo.a('b'); + `, + ` + type A = { + (value: 'b'): void; + /** @deprecated */ + (value: 'c'): void; + }; + declare const foo: A; + foo('b'); + `, + ` + declare const a: { + new (value: 'b'): void; + /** @deprecated */ + new (value: 'c'): void; + }; + new a('b'); + `, ` namespace assert { export function fail(message?: string | Error): never; @@ -157,6 +183,30 @@ ruleTester.run('no-deprecated', rule, { export type D = A.C | A.D; `, + ` + interface Props { + anchor: 'foo'; + } + declare const x: Props; + const { anchor = '' } = x; + `, + ` + interface Props { + anchor: 'foo'; + } + declare const x: { bar: Props }; + const { + bar: { anchor = '' }, + } = x; + `, + ` + interface Props { + anchor: 'foo'; + } + declare const x: [item: Props]; + const [{ anchor = 'bar' }] = x; + `, + 'function fn(/** @deprecated */ foo = 4) {}', ], invalid: [ { @@ -271,6 +321,38 @@ ruleTester.run('no-deprecated', rule, { }, ], }, + { + code: ` + /** @deprecated */ const a = { b: 1 }; + const { c = a } = {}; + `, + errors: [ + { + column: 21, + endColumn: 22, + line: 3, + endLine: 3, + data: { name: 'a' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + /** @deprecated */ const a = { b: 1 }; + const [c = a] = []; + `, + errors: [ + { + column: 20, + endColumn: 21, + line: 3, + endLine: 3, + data: { name: 'a' }, + messageId: 'deprecated', + }, + ], + }, { code: ` /** @deprecated */ const a = { b: 1 }; @@ -595,6 +677,26 @@ ruleTester.run('no-deprecated', rule, { }, ], }, + { + code: ` + declare const A: { + /** @deprecated */ + new (): string; + }; + + new A(); + `, + errors: [ + { + column: 13, + endColumn: 14, + line: 7, + endLine: 7, + data: { name: 'A' }, + messageId: 'deprecated', + }, + ], + }, { code: ` /** @deprecated */ @@ -681,6 +783,99 @@ ruleTester.run('no-deprecated', rule, { }, ], }, + { + code: ` + declare class A { + /** @deprecated */ + b: () => string; + } + + declare const a: A; + + a.b; + `, + errors: [ + { + column: 11, + endColumn: 12, + line: 9, + endLine: 9, + data: { name: 'b' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + declare class A { + /** @deprecated */ + b: () => string; + } + + declare const a: A; + + a.b(); + `, + only: false, + errors: [ + { + column: 11, + endColumn: 12, + line: 9, + endLine: 9, + data: { name: 'b' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + interface A { + /** @deprecated */ + b: () => string; + } + + declare const a: A; + + a.b(); + `, + only: false, + errors: [ + { + column: 11, + endColumn: 12, + line: 9, + endLine: 9, + data: { name: 'b' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + class A { + /** @deprecated */ + b(): string { + return ''; + } + } + + declare const a: A; + + a.b(); + `, + only: false, + errors: [ + { + column: 11, + endColumn: 12, + line: 11, + endLine: 11, + data: { name: 'b' }, + messageId: 'deprecated', + }, + ], + }, { code: ` declare class A { @@ -1099,6 +1294,27 @@ ruleTester.run('no-deprecated', rule, { }, ], }, + { + code: ` + type A = { + (value: 'b'): void; + /** @deprecated */ + (value: 'c'): void; + }; + declare const foo: A; + foo('c'); + `, + errors: [ + { + column: 9, + endColumn: 12, + line: 8, + endLine: 8, + data: { name: 'foo' }, + messageId: 'deprecated', + }, + ], + }, { code: ` function a( @@ -1286,5 +1502,87 @@ ruleTester.run('no-deprecated', rule, { }, ], }, + { + code: ` + interface Props { + /** @deprecated */ + anchor: 'foo'; + } + declare const x: Props; + const { anchor = '' } = x; + `, + errors: [ + { + column: 17, + endColumn: 23, + line: 7, + endLine: 7, + data: { name: 'anchor' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + interface Props { + /** @deprecated */ + anchor: 'foo'; + } + declare const x: { bar: Props }; + const { + bar: { anchor = '' }, + } = x; + `, + errors: [ + { + column: 18, + endColumn: 24, + line: 8, + endLine: 8, + data: { name: 'anchor' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + interface Props { + /** @deprecated */ + anchor: 'foo'; + } + declare const x: [item: Props]; + const [{ anchor = 'bar' }] = x; + `, + errors: [ + { + column: 18, + endColumn: 24, + line: 7, + endLine: 7, + data: { name: 'anchor' }, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + interface Props { + /** @deprecated */ + foo: Props; + } + declare const x: Props; + const { foo = x } = x; + `, + errors: [ + { + column: 17, + endColumn: 20, + line: 7, + endLine: 7, + data: { name: 'foo' }, + messageId: 'deprecated', + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts index 072e1444fea0..0c6b33582825 100644 --- a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts +++ b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts @@ -1047,6 +1047,10 @@ interface MyInterface extends MyCall, MyIndex, MyConstruct, MyMethods { 'const notAFn3: boolean = true;', 'const notAFn4: { prop: 1 } = { prop: 1 };', 'const notAFn5: {} = {};', + ` +const array: number[] = [1, 2, 3]; +array.filter(a => a > 1); + `, ], invalid: [ @@ -2269,5 +2273,54 @@ interface MyInterface extends MyCall, MyIndex, MyConstruct, MyMethods { }, ], }, + { + code: ` +declare function isTruthy(value: unknown): Promise; +[0, 1, 2].filter(isTruthy); + `, + errors: [ + { + line: 3, + messageId: 'predicate', + }, + ], + }, + { + code: ` +const array: number[] = []; +array.every(() => Promise.resolve(true)); + `, + errors: [ + { + line: 3, + messageId: 'predicate', + }, + ], + }, + { + code: ` +const array: (string[] & { foo: 'bar' }) | (number[] & { bar: 'foo' }) = []; +array.every(() => Promise.resolve(true)); + `, + errors: [ + { + line: 3, + messageId: 'predicate', + }, + ], + }, + { + code: ` +const tuple: [number, number, number] = [1, 2, 3]; +tuple.find(() => Promise.resolve(false)); + `, + options: [{ checksConditionals: true }], + errors: [ + { + line: 3, + messageId: 'predicate', + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts index b8a9ab9981d9..9fbd0eb7ac6f 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -75,6 +75,10 @@ for (let i = 0; b1 && b2; i++) { } const t1 = b1 && b2 ? 'yes' : 'no'; for (;;) {} +switch (b1) { + case true: + default: +} `, ` declare function foo(): number | void; @@ -867,6 +871,15 @@ foo?.()?.toExponential(); type Foo = { [key: string]: () => number | undefined } | null; declare const foo: Foo; foo?.['bar']()?.toExponential(); + `, + ` +declare function foo(): void | { key: string }; +const bar = foo()?.key; + `, + ` +type fn = () => void; +declare function foo(): void | fn; +const bar = foo()?.(); `, { languageOptions: { parserOptions: optionsWithExactOptionalPropertyTypes }, @@ -901,6 +914,10 @@ for (let i = 0; b1 && b2; i++) { } const t1 = b1 && b2 ? 'yes' : 'no'; const t1 = b2 && b1 ? 'yes' : 'no'; +switch (b1) { + case true: + default: +} `, output: null, errors: [ @@ -913,6 +930,7 @@ const t1 = b2 && b1 ? 'yes' : 'no'; ruleError(12, 17, 'alwaysTruthy'), ruleError(15, 12, 'alwaysTruthy'), ruleError(16, 18, 'alwaysTruthy'), + ruleError(18, 8, 'literalBooleanExpression'), ], }, // Ensure that it's complaining about the right things @@ -1282,11 +1300,13 @@ function truthy() { function falsy() {} [1, 3, 5].filter(truthy); [1, 2, 3].find(falsy); +[1, 2, 3].findLastIndex(falsy); `, output: null, errors: [ ruleError(6, 18, 'alwaysTruthyFunc'), ruleError(7, 16, 'alwaysFalsyFunc'), + ruleError(8, 25, 'alwaysFalsyFunc'), ], }, // Supports generics @@ -1909,7 +1929,7 @@ if (!a) { } `, output: null, - errors: [ruleError(3, 6, 'alwaysTruthy')], + errors: [ruleError(3, 5, 'alwaysTruthy')], }, { code: ` @@ -1918,7 +1938,7 @@ if (!a) { } `, output: null, - errors: [ruleError(3, 6, 'alwaysFalsy')], + errors: [ruleError(3, 5, 'alwaysFalsy')], }, { code: ` @@ -1931,7 +1951,7 @@ if (!speech) { } `, output: null, - errors: [ruleError(7, 6, 'never')], + errors: [ruleError(7, 5, 'never')], }, { code: ` @@ -2277,6 +2297,14 @@ foo?.['bar']?.().toExponential(); }, ], }, + { + code: ` + const a = true; + if (!!a) { + } + `, + errors: [ruleError(3, 13, 'alwaysTruthy')], + }, // "branded" types unnecessaryConditionTest('"" & {}', 'alwaysFalsy'), diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-type-parameters.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-type-parameters.test.ts index ebc7a9ec181c..78b4be3022de 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-type-parameters.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-parameters.test.ts @@ -420,6 +420,15 @@ declare function sillyFoo( c: Constant, ): (data: T) => SillyFoo; `, + ` +const f = (setValue: (v: T) => void, getValue: () => NoInfer) => {}; + `, + ` +const f = ( + setValue: (v: T) => NoInfer, + getValue: (v: NoInfer) => NoInfer, +) => {}; + `, ], invalid: [ diff --git a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts index c60c2be4b0aa..e7d6872eaf85 100644 --- a/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts +++ b/packages/eslint-plugin/tests/rules/restrict-template-expressions.test.ts @@ -344,6 +344,12 @@ ruleTester.run('restrict-template-expressions', rule, { } `, }, + // allow + { + options: [{ allow: [{ from: 'lib', name: 'Promise' }] }], + code: 'const msg = `arg = ${Promise.resolve()}`;', + }, + 'const msg = `arg = ${new Error()}`;', 'const msg = `arg = ${false}`;', 'const msg = `arg = ${null}`;', 'const msg = `arg = ${undefined}`;', @@ -422,6 +428,15 @@ ruleTester.run('restrict-template-expressions', rule, { ], options: [{ allowNullish: false, allowArray: true }], }, + { + code: 'const msg = `arg = ${Promise.resolve()}`;', + errors: [{ messageId: 'invalidType' }], + }, + { + code: 'const msg = `arg = ${new Error()}`;', + options: [{ allow: [] }], + errors: [{ messageId: 'invalidType' }], + }, { code: ` declare const arg: [number | undefined, string]; diff --git a/packages/eslint-plugin/tests/schema-snapshots/restrict-template-expressions.shot b/packages/eslint-plugin/tests/schema-snapshots/restrict-template-expressions.shot index 1a4d828e764e..9a2625cd73c3 100644 --- a/packages/eslint-plugin/tests/schema-snapshots/restrict-template-expressions.shot +++ b/packages/eslint-plugin/tests/schema-snapshots/restrict-template-expressions.shot @@ -8,6 +8,101 @@ exports[`Rule schemas should be convertible to TS types for documentation purpos { "additionalProperties": false, "properties": { + "allow": { + "description": "Types to allow in template expressions.", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "additionalProperties": false, + "properties": { + "from": { + "enum": ["file"], + "type": "string" + }, + "name": { + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array", + "uniqueItems": true + } + ] + }, + "path": { + "type": "string" + } + }, + "required": ["from", "name"], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "from": { + "enum": ["lib"], + "type": "string" + }, + "name": { + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array", + "uniqueItems": true + } + ] + } + }, + "required": ["from", "name"], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "from": { + "enum": ["package"], + "type": "string" + }, + "name": { + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array", + "uniqueItems": true + } + ] + }, + "package": { + "type": "string" + } + }, + "required": ["from", "name", "package"], + "type": "object" + } + ] + }, + "type": "array" + }, "allowAny": { "description": "Whether to allow \`any\` typed values in template expressions.", "type": "boolean" @@ -46,6 +141,24 @@ exports[`Rule schemas should be convertible to TS types for documentation purpos type Options = [ { + /** Types to allow in template expressions. */ + allow?: ( + | { + from: 'file'; + name: [string, ...string[]] | string; + path?: string; + } + | { + from: 'lib'; + name: [string, ...string[]] | string; + } + | { + from: 'package'; + name: [string, ...string[]] | string; + package: string; + } + | string + )[]; /** Whether to allow \`any\` typed values in template expressions. */ allowAny?: boolean; /** Whether to allow \`array\` typed values in template expressions. */ diff --git a/packages/integration-tests/fixtures/flat-config-types-@types__eslint-v8/package.json b/packages/integration-tests/fixtures/flat-config-types-@types__eslint-v8/package.json index 940026b1558f..19cb9e67084b 100644 --- a/packages/integration-tests/fixtures/flat-config-types-@types__eslint-v8/package.json +++ b/packages/integration-tests/fixtures/flat-config-types-@types__eslint-v8/package.json @@ -6,7 +6,7 @@ "@types/eslint__js": "latest", "@eslint/js": "latest", "@types/eslint": "^8", - "eslint": "latest", + "eslint": "9.9.1", "@stylistic/eslint-plugin": "2.3.0", "eslint-plugin-deprecation": "latest", "eslint-plugin-jest": "latest" diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md index 00f39c99c202..e4e3b58b7a44 100644 --- a/packages/parser/CHANGELOG.md +++ b/packages/parser/CHANGELOG.md @@ -1,3 +1,9 @@ +## 8.6.0 (2024-09-16) + +This was a version bump only for parser to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 8.5.0 (2024-09-09) This was a version bump only for parser to align it with other projects, there were no code changes. diff --git a/packages/parser/package.json b/packages/parser/package.json index 09d3f75116f2..57c7d2decba8 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/parser", - "version": "8.5.0", + "version": "8.6.0", "description": "An ESLint custom parser which leverages TypeScript ESTree", "files": [ "dist", @@ -52,10 +52,10 @@ "eslint": "^8.57.0 || ^9.0.0" }, "dependencies": { - "@typescript-eslint/scope-manager": "8.5.0", - "@typescript-eslint/types": "8.5.0", - "@typescript-eslint/typescript-estree": "8.5.0", - "@typescript-eslint/visitor-keys": "8.5.0", + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/typescript-estree": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", "debug": "^4.3.4" }, "devDependencies": { diff --git a/packages/rule-schema-to-typescript-types/CHANGELOG.md b/packages/rule-schema-to-typescript-types/CHANGELOG.md index 0ed8caf454b0..ea8491e34be7 100644 --- a/packages/rule-schema-to-typescript-types/CHANGELOG.md +++ b/packages/rule-schema-to-typescript-types/CHANGELOG.md @@ -1,3 +1,9 @@ +## 8.6.0 (2024-09-16) + +This was a version bump only for rule-schema-to-typescript-types to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 8.5.0 (2024-09-09) This was a version bump only for rule-schema-to-typescript-types to align it with other projects, there were no code changes. diff --git a/packages/rule-schema-to-typescript-types/package.json b/packages/rule-schema-to-typescript-types/package.json index 0a1ccae645f8..508958d87e2f 100644 --- a/packages/rule-schema-to-typescript-types/package.json +++ b/packages/rule-schema-to-typescript-types/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/rule-schema-to-typescript-types", - "version": "8.5.0", + "version": "8.6.0", "private": true, "type": "commonjs", "exports": { @@ -34,8 +34,8 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@typescript-eslint/type-utils": "8.5.0", - "@typescript-eslint/utils": "8.5.0", + "@typescript-eslint/type-utils": "8.6.0", + "@typescript-eslint/utils": "8.6.0", "natural-compare": "^1.4.0", "prettier": "^3.2.5" }, diff --git a/packages/rule-tester/CHANGELOG.md b/packages/rule-tester/CHANGELOG.md index 72b91401f855..80196faf86cf 100644 --- a/packages/rule-tester/CHANGELOG.md +++ b/packages/rule-tester/CHANGELOG.md @@ -1,3 +1,9 @@ +## 8.6.0 (2024-09-16) + +This was a version bump only for rule-tester to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 8.5.0 (2024-09-09) This was a version bump only for rule-tester to align it with other projects, there were no code changes. diff --git a/packages/rule-tester/package.json b/packages/rule-tester/package.json index 40c5823ef6db..e22d75355525 100644 --- a/packages/rule-tester/package.json +++ b/packages/rule-tester/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/rule-tester", - "version": "8.5.0", + "version": "8.6.0", "description": "Tooling to test ESLint rules", "files": [ "dist", @@ -48,8 +48,8 @@ }, "//": "NOTE - AJV is out-of-date, but it's intentionally synced with ESLint - https://github.com/eslint/eslint/blob/ad9dd6a933fd098a0d99c6a9aa059850535c23ee/package.json#L70", "dependencies": { - "@typescript-eslint/typescript-estree": "8.5.0", - "@typescript-eslint/utils": "8.5.0", + "@typescript-eslint/typescript-estree": "8.6.0", + "@typescript-eslint/utils": "8.6.0", "ajv": "^6.12.6", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "4.6.2", @@ -62,7 +62,7 @@ "@jest/types": "29.6.3", "@types/json-stable-stringify-without-jsonify": "^1.0.2", "@types/lodash.merge": "4.6.9", - "@typescript-eslint/parser": "8.5.0", + "@typescript-eslint/parser": "8.6.0", "chai": "^4.4.1", "eslint-visitor-keys": "^4.0.0", "espree": "^10.0.1", diff --git a/packages/scope-manager/CHANGELOG.md b/packages/scope-manager/CHANGELOG.md index 5e1dd0809342..ed90bc47b140 100644 --- a/packages/scope-manager/CHANGELOG.md +++ b/packages/scope-manager/CHANGELOG.md @@ -1,3 +1,9 @@ +## 8.6.0 (2024-09-16) + +This was a version bump only for scope-manager to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 8.5.0 (2024-09-09) This was a version bump only for scope-manager to align it with other projects, there were no code changes. diff --git a/packages/scope-manager/package.json b/packages/scope-manager/package.json index 996d831e7a73..0369a15611f1 100644 --- a/packages/scope-manager/package.json +++ b/packages/scope-manager/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/scope-manager", - "version": "8.5.0", + "version": "8.6.0", "description": "TypeScript scope analyser for ESLint", "files": [ "dist", @@ -46,13 +46,13 @@ "typecheck": "npx nx typecheck" }, "dependencies": { - "@typescript-eslint/types": "8.5.0", - "@typescript-eslint/visitor-keys": "8.5.0" + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0" }, "devDependencies": { "@jest/types": "29.6.3", "@types/glob": "*", - "@typescript-eslint/typescript-estree": "8.5.0", + "@typescript-eslint/typescript-estree": "8.6.0", "glob": "*", "jest-specific-snapshot": "*", "make-dir": "*", diff --git a/packages/type-utils/CHANGELOG.md b/packages/type-utils/CHANGELOG.md index f56ca48108d0..d0b9fc22e94f 100644 --- a/packages/type-utils/CHANGELOG.md +++ b/packages/type-utils/CHANGELOG.md @@ -1,3 +1,23 @@ +## 8.6.0 (2024-09-16) + + +### 🚀 Features + +- add `allow` option for `restrict-template-expressions` + +- **type-utils:** isNullableType add Void logic + + +### ❤️ Thank You + +- Abraham Guo +- auvred +- Josh Goldberg ✨ +- Kim Sang Du +- YeonJuan + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 8.5.0 (2024-09-09) This was a version bump only for type-utils to align it with other projects, there were no code changes. diff --git a/packages/type-utils/package.json b/packages/type-utils/package.json index e5786590b6c9..b630c55ed655 100644 --- a/packages/type-utils/package.json +++ b/packages/type-utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/type-utils", - "version": "8.5.0", + "version": "8.6.0", "description": "Type utilities for working with TypeScript + ESLint together", "files": [ "dist", @@ -46,14 +46,14 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@typescript-eslint/typescript-estree": "8.5.0", - "@typescript-eslint/utils": "8.5.0", + "@typescript-eslint/typescript-estree": "8.6.0", + "@typescript-eslint/utils": "8.6.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, "devDependencies": { "@jest/types": "29.6.3", - "@typescript-eslint/parser": "8.5.0", + "@typescript-eslint/parser": "8.6.0", "ajv": "^6.12.6", "downlevel-dts": "*", "jest": "29.7.0", diff --git a/packages/type-utils/src/TypeOrValueSpecifier.ts b/packages/type-utils/src/TypeOrValueSpecifier.ts index 422e83769208..d9c50f315b91 100644 --- a/packages/type-utils/src/TypeOrValueSpecifier.ts +++ b/packages/type-utils/src/TypeOrValueSpecifier.ts @@ -66,97 +66,100 @@ export type TypeOrValueSpecifier = | PackageSpecifier | string; -export const typeOrValueSpecifierSchema: JSONSchema4 = { - oneOf: [ - { - type: 'string', - }, - { - type: 'object', - additionalProperties: false, - properties: { - from: { - type: 'string', - enum: ['file'], - }, - name: { - oneOf: [ - { - type: 'string', - }, - { - type: 'array', - minItems: 1, - uniqueItems: true, - items: { +export const typeOrValueSpecifiersSchema = { + type: 'array', + items: { + oneOf: [ + { + type: 'string', + }, + { + type: 'object', + additionalProperties: false, + properties: { + from: { + type: 'string', + enum: ['file'], + }, + name: { + oneOf: [ + { type: 'string', }, - }, - ], - }, - path: { - type: 'string', + { + type: 'array', + minItems: 1, + uniqueItems: true, + items: { + type: 'string', + }, + }, + ], + }, + path: { + type: 'string', + }, }, + required: ['from', 'name'], }, - required: ['from', 'name'], - }, - { - type: 'object', - additionalProperties: false, - properties: { - from: { - type: 'string', - enum: ['lib'], - }, - name: { - oneOf: [ - { - type: 'string', - }, - { - type: 'array', - minItems: 1, - uniqueItems: true, - items: { + { + type: 'object', + additionalProperties: false, + properties: { + from: { + type: 'string', + enum: ['lib'], + }, + name: { + oneOf: [ + { type: 'string', }, - }, - ], + { + type: 'array', + minItems: 1, + uniqueItems: true, + items: { + type: 'string', + }, + }, + ], + }, }, + required: ['from', 'name'], }, - required: ['from', 'name'], - }, - { - type: 'object', - additionalProperties: false, - properties: { - from: { - type: 'string', - enum: ['package'], - }, - name: { - oneOf: [ - { - type: 'string', - }, - { - type: 'array', - minItems: 1, - uniqueItems: true, - items: { + { + type: 'object', + additionalProperties: false, + properties: { + from: { + type: 'string', + enum: ['package'], + }, + name: { + oneOf: [ + { type: 'string', }, - }, - ], - }, - package: { - type: 'string', + { + type: 'array', + minItems: 1, + uniqueItems: true, + items: { + type: 'string', + }, + }, + ], + }, + package: { + type: 'string', + }, }, + required: ['from', 'name', 'package'], }, - required: ['from', 'name', 'package'], - }, - ], -}; + ], + }, +} as const satisfies JSONSchema4; export function typeMatchesSpecifier( type: ts.Type, @@ -191,3 +194,10 @@ export function typeMatchesSpecifier( ); } } + +export const typeMatchesSomeSpecifier = ( + type: ts.Type, + specifiers: TypeOrValueSpecifier[] = [], + program: ts.Program, +): boolean => + specifiers.some(specifier => typeMatchesSpecifier(type, specifier, program)); diff --git a/packages/type-utils/src/isTypeReadonly.ts b/packages/type-utils/src/isTypeReadonly.ts index c8f89ca2fd12..cc44a0f5f252 100644 --- a/packages/type-utils/src/isTypeReadonly.ts +++ b/packages/type-utils/src/isTypeReadonly.ts @@ -6,8 +6,8 @@ import * as ts from 'typescript'; import { getTypeOfPropertyOfType } from './propertyTypes'; import type { TypeOrValueSpecifier } from './TypeOrValueSpecifier'; import { - typeMatchesSpecifier, - typeOrValueSpecifierSchema, + typeMatchesSomeSpecifier, + typeOrValueSpecifiersSchema, } from './TypeOrValueSpecifier'; const enum Readonlyness { @@ -31,10 +31,7 @@ export const readonlynessOptionsSchema = { treatMethodsAsReadonly: { type: 'boolean', }, - allow: { - type: 'array', - items: typeOrValueSpecifierSchema, - }, + allow: typeOrValueSpecifiersSchema, }, } satisfies JSONSchema4; @@ -232,11 +229,7 @@ function isTypeReadonlyRecurser( const checker = program.getTypeChecker(); seenTypes.add(type); - if ( - options.allow?.some(specifier => - typeMatchesSpecifier(type, specifier, program), - ) - ) { + if (typeMatchesSomeSpecifier(type, options.allow, program)) { return Readonlyness.Readonly; } diff --git a/packages/type-utils/src/predicates.ts b/packages/type-utils/src/predicates.ts index 8927f63a80d9..5c0680c8f7d5 100644 --- a/packages/type-utils/src/predicates.ts +++ b/packages/type-utils/src/predicates.ts @@ -15,7 +15,8 @@ export function isNullableType(type: ts.Type): boolean { ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Null | - ts.TypeFlags.Undefined, + ts.TypeFlags.Undefined | + ts.TypeFlags.Void, ); } diff --git a/packages/type-utils/tests/TypeOrValueSpecifier.test.ts b/packages/type-utils/tests/TypeOrValueSpecifier.test.ts index fd676a289939..b38b84cbb867 100644 --- a/packages/type-utils/tests/TypeOrValueSpecifier.test.ts +++ b/packages/type-utils/tests/TypeOrValueSpecifier.test.ts @@ -4,23 +4,20 @@ import { parseForESLint } from '@typescript-eslint/parser'; import type { TSESTree } from '@typescript-eslint/utils'; import Ajv from 'ajv'; -import type { TypeOrValueSpecifier } from '../src/TypeOrValueSpecifier'; -import { - typeMatchesSpecifier, - typeOrValueSpecifierSchema, -} from '../src/TypeOrValueSpecifier'; +import type { TypeOrValueSpecifier } from '../src'; +import { typeMatchesSpecifier, typeOrValueSpecifiersSchema } from '../src'; describe('TypeOrValueSpecifier', () => { describe('Schema', () => { const ajv = new Ajv(); - const validate = ajv.compile(typeOrValueSpecifierSchema); + const validate = ajv.compile(typeOrValueSpecifiersSchema); - function runTestPositive(data: unknown): void { - expect(validate(data)).toBe(true); + function runTestPositive(typeOrValueSpecifier: unknown): void { + expect(validate([typeOrValueSpecifier])).toBe(true); } - function runTestNegative(data: unknown): void { - expect(validate(data)).toBe(false); + function runTestNegative(typeOrValueSpecifier: unknown): void { + expect(validate([typeOrValueSpecifier])).toBe(false); } it.each([['MyType'], ['myValue'], ['any'], ['void'], ['never']])( @@ -448,7 +445,7 @@ describe('TypeOrValueSpecifier', () => { 'import type {Node as TsNode} from "typescript"; type Test = TsNode;', { from: 'package', name: 'TsNode', package: 'typescript' }, ], - ])("doesn't match a mismatched lib specifier: %s", runTestNegative); + ])("doesn't match a mismatched package specifier: %s", runTestNegative); it.each<[string, TypeOrValueSpecifier]>([ [ diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 81990c940257..b4976ba45fa2 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,3 +1,21 @@ +## 8.6.0 (2024-09-16) + + +### 🚀 Features + +- **typescript-estree:** disable plugin loading by default in project service + + +### ❤️ Thank You + +- Abraham Guo +- auvred +- Josh Goldberg ✨ +- Kim Sang Du +- YeonJuan + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 8.5.0 (2024-09-09) diff --git a/packages/types/package.json b/packages/types/package.json index 1c648f812298..786b7b8afd4d 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/types", - "version": "8.5.0", + "version": "8.6.0", "description": "Types for the TypeScript-ESTree AST spec", "files": [ "dist", diff --git a/packages/types/src/parser-options.ts b/packages/types/src/parser-options.ts index b7a856f528c0..e6f3b404c96f 100644 --- a/packages/types/src/parser-options.ts +++ b/packages/types/src/parser-options.ts @@ -54,6 +54,11 @@ interface ProjectServiceOptions { */ defaultProject?: string; + /** + * Whether to allow TypeScript plugins as configured in the TSConfig. + */ + loadTypeScriptPlugins?: boolean; + /** * The maximum number of files {@link allowDefaultProject} may match. * Each file match slows down linting, so if you do need to use this, please diff --git a/packages/typescript-eslint/CHANGELOG.md b/packages/typescript-eslint/CHANGELOG.md index 85a945813be0..946c37edb01b 100644 --- a/packages/typescript-eslint/CHANGELOG.md +++ b/packages/typescript-eslint/CHANGELOG.md @@ -1,3 +1,9 @@ +## 8.6.0 (2024-09-16) + +This was a version bump only for typescript-eslint to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 8.5.0 (2024-09-09) This was a version bump only for typescript-eslint to align it with other projects, there were no code changes. diff --git a/packages/typescript-eslint/package.json b/packages/typescript-eslint/package.json index 45a2c70c3c61..9628438305e3 100644 --- a/packages/typescript-eslint/package.json +++ b/packages/typescript-eslint/package.json @@ -1,6 +1,6 @@ { "name": "typescript-eslint", - "version": "8.5.0", + "version": "8.6.0", "description": "Tooling which enables you to use TypeScript with ESLint", "files": [ "dist", @@ -52,9 +52,9 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@typescript-eslint/eslint-plugin": "8.5.0", - "@typescript-eslint/parser": "8.5.0", - "@typescript-eslint/utils": "8.5.0" + "@typescript-eslint/eslint-plugin": "8.6.0", + "@typescript-eslint/parser": "8.6.0", + "@typescript-eslint/utils": "8.6.0" }, "devDependencies": { "@jest/types": "29.6.3", diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md index d8afcf525694..449cd2176f73 100644 --- a/packages/typescript-estree/CHANGELOG.md +++ b/packages/typescript-estree/CHANGELOG.md @@ -1,3 +1,26 @@ +## 8.6.0 (2024-09-16) + + +### 🚀 Features + +- **typescript-estree:** disable plugin loading by default in project service + + +### 🩹 Fixes + +- **typescript-estree:** don't throw on missing tsconfig.json by default in project service + + +### ❤️ Thank You + +- Abraham Guo +- auvred +- Josh Goldberg ✨ +- Kim Sang Du +- YeonJuan + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 8.5.0 (2024-09-09) diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index d6cc213c9a4c..7344425bbc58 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/typescript-estree", - "version": "8.5.0", + "version": "8.6.0", "description": "A parser that converts TypeScript source code into an ESTree compatible form", "files": [ "dist", @@ -54,8 +54,8 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@typescript-eslint/types": "8.5.0", - "@typescript-eslint/visitor-keys": "8.5.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", diff --git a/packages/typescript-estree/src/create-program/createProjectService.ts b/packages/typescript-estree/src/create-program/createProjectService.ts index a9634985242c..89158d15e068 100644 --- a/packages/typescript-estree/src/create-program/createProjectService.ts +++ b/packages/typescript-estree/src/create-program/createProjectService.ts @@ -42,9 +42,10 @@ export function createProjectService( jsDocParsingMode: ts.JSDocParsingMode | undefined, tsconfigRootDir: string | undefined, ): ProjectServiceSettings { + const optionsRawObject = typeof optionsRaw === 'object' ? optionsRaw : {}; const options = { defaultProject: 'tsconfig.json', - ...(typeof optionsRaw === 'object' && optionsRaw), + ...optionsRawObject, }; validateDefaultProjectForFilesGlob(options.allowDefaultProject); @@ -65,6 +66,18 @@ export function createProjectService( setTimeout, watchDirectory: createStubFileWatcher, watchFile: createStubFileWatcher, + + // We stop loading any TypeScript plugins by default, to prevent them from attaching disk watchers + // See https://github.com/typescript-eslint/typescript-eslint/issues/9905 + ...(!options.loadTypeScriptPlugins && { + require: () => ({ + module: undefined, + error: { + message: + 'TypeScript plugins are not required when using parserOptions.projectService.', + }, + }), + }), }; const logger: ts.server.Logger = { @@ -126,7 +139,7 @@ export function createProjectService( }); log('Enabling default project: %s', options.defaultProject); - let configFile: ts.ParsedCommandLine; + let configFile: ts.ParsedCommandLine | undefined; try { configFile = getParsedConfigFile( @@ -135,18 +148,22 @@ export function createProjectService( tsconfigRootDir, ); } catch (error) { - throw new Error( - `Could not read project service default project '${options.defaultProject}': ${(error as Error).message}`, - ); + if (optionsRawObject.defaultProject) { + throw new Error( + `Could not read project service default project '${options.defaultProject}': ${(error as Error).message}`, + ); + } } - service.setCompilerOptionsForInferredProjects( - // NOTE: The inferred projects API is not intended for source files when a tsconfig - // exists. There is no API that generates an InferredProjectCompilerOptions suggesting - // it is meant for hard coded options passed in. Hard asserting as a work around. - // See https://github.com/microsoft/TypeScript/blob/27bcd4cb5a98bce46c9cdd749752703ead021a4b/src/server/protocol.ts#L1904 - configFile.options as ts.server.protocol.InferredProjectCompilerOptions, - ); + if (configFile) { + service.setCompilerOptionsForInferredProjects( + // NOTE: The inferred projects API is not intended for source files when a tsconfig + // exists. There is no API that generates an InferredProjectCompilerOptions suggesting + // it is meant for hard coded options passed in. Hard asserting as a work around. + // See https://github.com/microsoft/TypeScript/blob/27bcd4cb5a98bce46c9cdd749752703ead021a4b/src/server/protocol.ts#L1904 + configFile.options as ts.server.protocol.InferredProjectCompilerOptions, + ); + } return { allowDefaultProject: options.allowDefaultProject, diff --git a/packages/typescript-estree/tests/lib/createProjectService.test.ts b/packages/typescript-estree/tests/lib/createProjectService.test.ts index d175e3de0b1e..d75b8b5d9a8d 100644 --- a/packages/typescript-estree/tests/lib/createProjectService.test.ts +++ b/packages/typescript-estree/tests/lib/createProjectService.test.ts @@ -14,13 +14,15 @@ jest.mock('typescript/lib/tsserverlibrary', () => ({ server: { ...jest.requireActual('typescript/lib/tsserverlibrary').server, ProjectService: class { - logger: ts.server.Logger; eventHandler: ts.server.ProjectServiceEventHandler | undefined; + host: ts.server.ServerHost; + logger: ts.server.Logger; constructor( ...args: ConstructorParameters ) { - this.logger = args[0].logger; this.eventHandler = args[0].eventHandler; + this.host = args[0].host; + this.logger = args[0].logger; if (this.eventHandler) { this.eventHandler({ eventName: 'projectLoadingStart', @@ -65,7 +67,7 @@ describe('createProjectService', () => { expect(settings.allowDefaultProject).toBeUndefined(); }); - it('throws an error with a local path when options.defaultProject is not provided and getParsedConfigFile throws a diagnostic error', () => { + it('does not throw an error when options.defaultProject is not provided and getParsedConfigFile throws a diagnostic error', () => { mockGetParsedConfigFile.mockImplementation(() => { throw new Error('tsconfig.json(1,1): error TS1234: Oh no!'); }); @@ -78,9 +80,7 @@ describe('createProjectService', () => { undefined, undefined, ), - ).toThrow( - /Could not read project service default project 'tsconfig.json': .+ error TS1234: Oh no!/, - ); + ).not.toThrow(); }); it('throws an error with a relative path when options.defaultProject is set to a relative path and getParsedConfigFile throws a diagnostic error', () => { @@ -132,6 +132,7 @@ describe('createProjectService', () => { createProjectService( { allowDefaultProject: ['file.js'], + defaultProject: 'tsconfig.json', }, undefined, undefined, @@ -311,6 +312,34 @@ describe('createProjectService', () => { expect(process.stderr.write).toHaveBeenCalledTimes(0); }); + it('provides a stub require to the host system when loadTypeScriptPlugins is falsy', () => { + const { service } = createProjectService({}, undefined, undefined); + + const required = service.host.require(); + + expect(required).toEqual({ + module: undefined, + error: { + message: + 'TypeScript plugins are not required when using parserOptions.projectService.', + }, + }); + }); + + it('does not provide a require to the host system when loadTypeScriptPlugins is truthy', () => { + const { service } = createProjectService( + { + loadTypeScriptPlugins: true, + }, + undefined, + undefined, + ); + + expect(service.host.require).toBe( + jest.requireActual('typescript/lib/tsserverlibrary').sys.require, + ); + }); + it('sets a host configuration', () => { const { service } = createProjectService( { diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index d5e25793e8a5..01c5c0820c32 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -1,3 +1,21 @@ +## 8.6.0 (2024-09-16) + + +### 🩹 Fixes + +- **eslint-plugin:** [no-deprecated] report on deprecated properties with function-like types + + +### ❤️ Thank You + +- Abraham Guo +- auvred +- Josh Goldberg ✨ +- Kim Sang Du +- YeonJuan + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 8.5.0 (2024-09-09) This was a version bump only for utils to align it with other projects, there were no code changes. diff --git a/packages/utils/package.json b/packages/utils/package.json index 98ed8e557597..10bd4aec62ba 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/utils", - "version": "8.5.0", + "version": "8.6.0", "description": "Utilities for working with TypeScript + ESLint together", "files": [ "dist", @@ -64,9 +64,9 @@ }, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.5.0", - "@typescript-eslint/types": "8.5.0", - "@typescript-eslint/typescript-estree": "8.5.0" + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/typescript-estree": "8.6.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0" diff --git a/packages/utils/tests/ts-eslint/ESLint.test.ts b/packages/utils/tests/ts-eslint/ESLint.test.ts index f252b7eba28a..8278a0e0568b 100644 --- a/packages/utils/tests/ts-eslint/ESLint.test.ts +++ b/packages/utils/tests/ts-eslint/ESLint.test.ts @@ -13,6 +13,7 @@ describe('ESLint', () => { expect(eslint).toBeInstanceOf(FlatESLint); }); it('legacy', () => { + // eslint-disable-next-line @typescript-eslint/no-deprecated const eslint = new ESLint.LegacyESLint({ // accepts legacy configs baseConfig: { diff --git a/packages/visitor-keys/CHANGELOG.md b/packages/visitor-keys/CHANGELOG.md index 57216e8a8c9c..74b233c65975 100644 --- a/packages/visitor-keys/CHANGELOG.md +++ b/packages/visitor-keys/CHANGELOG.md @@ -1,3 +1,9 @@ +## 8.6.0 (2024-09-16) + +This was a version bump only for visitor-keys to align it with other projects, there were no code changes. + +You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website. + ## 8.5.0 (2024-09-09) This was a version bump only for visitor-keys to align it with other projects, there were no code changes. diff --git a/packages/visitor-keys/package.json b/packages/visitor-keys/package.json index 8dce253f06b9..0fda5d287905 100644 --- a/packages/visitor-keys/package.json +++ b/packages/visitor-keys/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/visitor-keys", - "version": "8.5.0", + "version": "8.6.0", "description": "Visitor keys used to help traverse the TypeScript-ESTree AST", "files": [ "dist", @@ -47,7 +47,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/types": "8.6.0", "eslint-visitor-keys": "^3.4.3" }, "devDependencies": { diff --git a/packages/website/blog/2024-07-31-announcing-typescript-eslint-v8.md b/packages/website/blog/2024-07-31-announcing-typescript-eslint-v8.md index 7a17f4a4b90a..d1ccf1faecf8 100644 --- a/packages/website/blog/2024-07-31-announcing-typescript-eslint-v8.md +++ b/packages/website/blog/2024-07-31-announcing-typescript-eslint-v8.md @@ -14,7 +14,7 @@ We've been working on a set of breaking changes and general features that we're And now, we're excited to say that typescript-eslint v8 is released as stable! 🎉 We'd previously blogged about v8 in [Announcing typescript-eslint v8 Beta](./2024-05-27-announcing-typescript-eslint-v8-beta.mdx). -This blog post contains much of the same information as that one. +This blog post contains much of the same information as that one, and includes the list of steps you'll need to take to upgrade. diff --git a/packages/website/data/sponsors.json b/packages/website/data/sponsors.json index 4d48b83e4688..c1ad55284953 100644 --- a/packages/website/data/sponsors.json +++ b/packages/website/data/sponsors.json @@ -239,7 +239,7 @@ }, { "id": "0+X", - "image": "https://images.opencollective.com/0-x/7239aff/logo.png", + "image": "https://images.opencollective.com/0-x/707287f/logo.png", "name": "0+X", "totalDonations": 21000, "website": "https://www.0x.se" diff --git a/packages/website/package.json b/packages/website/package.json index ee01833b0077..55d3897cfbff 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -60,8 +60,9 @@ "cross-fetch": "*", "history": "^4.9.0", "make-dir": "*", + "mdast-util-from-markdown": "^2.0.1", "mdast-util-mdx": "^3.0.0", - "monaco-editor": "~0.50.0", + "monaco-editor": "~0.51.0", "raw-loader": "^4.0.2", "rimraf": "*", "stylelint": "^16.3.1", diff --git a/packages/website/plugins/generated-rule-docs/RuleDocsPage.ts b/packages/website/plugins/generated-rule-docs/RuleDocsPage.ts index e540c014b133..3c44aae99a0c 100644 --- a/packages/website/plugins/generated-rule-docs/RuleDocsPage.ts +++ b/packages/website/plugins/generated-rule-docs/RuleDocsPage.ts @@ -1,4 +1,5 @@ import type { ESLintPluginRuleModule } from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/rules'; +import { fromMarkdown } from 'mdast-util-from-markdown'; import type * as unist from 'unist'; import type { VFileWithStem } from '../utils/rules'; @@ -57,9 +58,15 @@ export class RuleDocsPage { spliceChildren( start: number, deleteCount: number, - ...items: unist.Node[] + ...items: (string | unist.Node)[] ): void { - this.#children.splice(start, deleteCount, ...items); + this.#children.splice( + start, + deleteCount, + ...items.map(item => + typeof item === 'string' ? fromMarkdown(item) : item, + ), + ); this.#headingIndices = this.#recreateHeadingIndices(); } diff --git a/packages/website/plugins/generated-rule-docs/index.ts b/packages/website/plugins/generated-rule-docs/index.ts index f29c0371748b..85c425cb38e4 100644 --- a/packages/website/plugins/generated-rule-docs/index.ts +++ b/packages/website/plugins/generated-rule-docs/index.ts @@ -6,7 +6,6 @@ import { isESLintPluginRuleModule, isVFileWithStem } from '../utils/rules'; import { addESLintHashToCodeBlocksMeta } from './addESLintHashToCodeBlocksMeta'; import { createRuleDocsPage } from './createRuleDocsPage'; import { insertBaseRuleReferences } from './insertions/insertBaseRuleReferences'; -import { insertFormattingNotice } from './insertions/insertFormattingNotice'; import { insertNewRuleReferences } from './insertions/insertNewRuleReferences'; import { insertResources } from './insertions/insertResources'; import { insertRuleDescription } from './insertions/insertRuleDescription'; @@ -28,7 +27,6 @@ export const generatedRuleDocs: Plugin = () => { removeSourceCodeNotice(page); insertRuleDescription(page); - insertFormattingNotice(page); const eslintrc = rule.meta.docs.extendsBaseRule ? insertBaseRuleReferences(page) diff --git a/packages/website/plugins/generated-rule-docs/insertions/insertBaseRuleReferences.ts b/packages/website/plugins/generated-rule-docs/insertions/insertBaseRuleReferences.ts index f88a17a85bc6..b5c1f5604f7e 100644 --- a/packages/website/plugins/generated-rule-docs/insertions/insertBaseRuleReferences.ts +++ b/packages/website/plugins/generated-rule-docs/insertions/insertBaseRuleReferences.ts @@ -10,33 +10,11 @@ export function insertBaseRuleReferences(page: RuleDocsPage): string { ? page.rule.meta.docs.extendsBaseRule : page.file.stem; - page.spliceChildren(page.headingIndices.options + 1, 0, { - children: [ - { - value: 'See ', - type: 'text', - }, - { - children: [ - { - type: 'inlineCode', - value: `eslint/${extendsBaseRuleName}`, - }, - { - type: 'text', - value: ' options', - }, - ], - type: 'link', - url: `https://eslint.org/docs/rules/${extendsBaseRuleName}#options`, - }, - { - type: 'text', - value: '.', - }, - ], - type: 'paragraph', - } as mdast.Paragraph); + page.spliceChildren( + page.headingIndices.options + 1, + 0, + `See [\`eslint/${extendsBaseRuleName}\`'s options](https://eslint.org/docs/rules/${extendsBaseRuleName}#options).`, + ); const eslintrc = getEslintrcString( extendsBaseRuleName, diff --git a/packages/website/plugins/generated-rule-docs/insertions/insertFormattingNotice.ts b/packages/website/plugins/generated-rule-docs/insertions/insertFormattingNotice.ts deleted file mode 100644 index a4ff96e120e8..000000000000 --- a/packages/website/plugins/generated-rule-docs/insertions/insertFormattingNotice.ts +++ /dev/null @@ -1,100 +0,0 @@ -import type { MdxJsxFlowElement } from 'mdast-util-mdx'; - -import type { RuleDocsPage } from '../RuleDocsPage'; - -export function insertFormattingNotice(page: RuleDocsPage): void { - const replacement = page.rule.meta.replacedBy?.find(e => - e.startsWith('@stylistic/'), - ); - if (!replacement) { - return; - } - - const url = - replacement && - `https://eslint.style/rules/${replacement.replace('@stylistic/', '')}`; - - page.spliceChildren(0, 0, { - children: [ - { - type: 'paragraph', - children: [ - { - type: 'text', - value: 'Formatting rules now live in ', - }, - { - type: 'link', - title: null, - url: 'https://eslint.style', - children: [ - { - type: 'text', - value: 'eslint-stylistic', - }, - ], - }, - { - type: 'text', - value: '. ', - }, - ...(url - ? [ - { - type: 'link', - title: null, - url, - children: [ - { - type: 'text', - value: replacement, - }, - ], - }, - { - type: 'text', - value: ' is the replacement for this rule. ', - }, - ] - : []), - { - type: 'break', - }, - { - type: 'text', - value: 'See ', - }, - { - type: 'link', - title: null, - url: '/blog/deprecating-formatting-rules', - children: [ - { - type: 'text', - value: 'Deprecating Formatting Rules', - }, - ], - }, - { - type: 'text', - value: ' for more information.', - }, - ], - }, - ], - attributes: [ - { - type: 'mdxJsxAttribute', - name: 'title', - value: 'Deprecated', - }, - { - type: 'mdxJsxAttribute', - name: 'type', - value: 'danger', - }, - ], - name: 'Admonition', - type: 'mdxJsxFlowElement', - } as MdxJsxFlowElement); -} diff --git a/packages/website/plugins/generated-rule-docs/insertions/insertNewRuleReferences.ts b/packages/website/plugins/generated-rule-docs/insertions/insertNewRuleReferences.ts index 5b601124ef35..08cca0d8e0d3 100644 --- a/packages/website/plugins/generated-rule-docs/insertions/insertNewRuleReferences.ts +++ b/packages/website/plugins/generated-rule-docs/insertions/insertNewRuleReferences.ts @@ -1,6 +1,7 @@ import { EOL } from 'node:os'; import * as path from 'node:path'; +import type { ESLintPluginDocs } from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/rules'; import { compile } from '@typescript-eslint/rule-schema-to-typescript-types'; import type * as mdast from 'mdast'; import type { MdxJsxFlowElement } from 'mdast-util-mdx'; @@ -62,12 +63,9 @@ export async function insertNewRuleReferences( page.spliceChildren( firstH2Index, 0, - { - lang: 'js', - type: 'code', - meta: 'title=".eslintrc.cjs"', - value: `module.exports = ${eslintrc};`, - } as mdast.Code, + `\`\`\`js title=".eslintrc.cjs" +module.exports = ${eslintrc}; +\`\`\``, { attributes: [ { @@ -97,46 +95,18 @@ export async function insertNewRuleReferences( : Object.keys(page.rule.meta.schema).length === 0; if (hasNoConfig) { - page.spliceChildren(page.headingIndices.options + 1, 0, { - children: [ - { - type: 'text', - value: 'This rule is not configurable.', - }, - ], - type: 'paragraph', - } as mdast.Paragraph); + page.spliceChildren( + page.headingIndices.options + 1, + 0, + 'This rule is not configurable.', + ); } else if (!COMPLICATED_RULE_OPTIONS.has(page.file.stem)) { page.spliceChildren( page.headingIndices.options + 1, 0, - { - children: - typeof page.rule.meta.docs.recommended === 'object' - ? [ - { - type: 'text', - value: - 'This rule accepts the following options, and has more strict settings in the ', - } as mdast.Text, - ...linkToConfigs( - page.rule.meta.docs.requiresTypeChecking - ? ['strict', 'strict-type-checked'] - : ['strict'], - ), - { - type: 'text', - value: ` config${page.rule.meta.docs.requiresTypeChecking ? 's' : ''}.`, - } as mdast.Text, - ] - : [ - { - type: 'text', - value: 'This rule accepts the following options:', - } as mdast.Text, - ], - type: 'paragraph', - } as mdast.Paragraph, + typeof page.rule.meta.docs.recommended === 'object' + ? linkToConfigsForObject(page.rule.meta.docs) + : 'This rule accepts the following options:', { lang: 'ts', type: 'code', @@ -156,30 +126,14 @@ export async function insertNewRuleReferences( return eslintrc; } -function linkToConfigs(configs: string[]): mdast.Node[] { - const links = configs.map( - (config): mdast.Link => ({ - children: [ - { - type: 'inlineCode', - value: config, - } as mdast.InlineCode, - ], - type: 'link', - url: `/users/configs#${config}`, - }), - ); - - return links.length === 1 - ? links - : [ - links[0], - { - type: 'text', - value: ' and ', - } as mdast.Text, - links[1], - ]; +function linkToConfigsForObject(docs: ESLintPluginDocs): string { + return [ + 'This rule accepts the following options, and has more strict settings in the', + (docs.requiresTypeChecking ? ['strict', 'strict-type-checked'] : ['strict']) + .map(config => `[${config}](/users/configs#${config})`) + .join(' and '), + `config${docs.requiresTypeChecking ? 's' : ''}.`, + ].join(' '); } function getRuleDefaultOptions(page: RuleDocsPage): string { diff --git a/packages/website/plugins/generated-rule-docs/insertions/insertResources.ts b/packages/website/plugins/generated-rule-docs/insertions/insertResources.ts index 50efedf8a71c..e6b6eb88f096 100644 --- a/packages/website/plugins/generated-rule-docs/insertions/insertResources.ts +++ b/packages/website/plugins/generated-rule-docs/insertions/insertResources.ts @@ -1,4 +1,3 @@ -import type * as mdast from 'mdast'; import type { MdxJsxFlowElement } from 'mdast-util-mdx'; import { getUrlForRuleTest, sourceUrlPrefix } from '../../utils/rules'; @@ -9,61 +8,11 @@ export function insertResources(page: RuleDocsPage): void { page.spliceChildren( page.children.length, 0, - { - children: [ - { - type: 'text', - value: 'Resources', - }, - ], - depth: 2, - type: 'heading', - } as mdast.Heading, - { - children: [ - { - children: [ - { - children: [ - { - type: 'link', - url: `${sourceUrlPrefix}src/rules/${page.file.stem}.ts`, - children: [ - { - type: 'text', - value: 'Rule source', - }, - ], - }, - ], - type: 'paragraph', - }, - ], - type: 'listItem', - }, - { - children: [ - { - children: [ - { - type: 'link', - url: getUrlForRuleTest(page.file.stem), - children: [ - { - type: 'text', - value: 'Test source', - }, - ], - }, - ], - type: 'paragraph', - }, - ], - type: 'listItem', - }, - ], - type: 'list', - } as mdast.List, + `## Resources + +- [Rule source](${sourceUrlPrefix}src/rules/${page.file.stem}.ts) +- [Test source](${getUrlForRuleTest(page.file.stem)}) + `, ); // Also add a notice about coming from ESLint core for extension rules diff --git a/packages/website/plugins/generated-rule-docs/insertions/insertRuleDescription.ts b/packages/website/plugins/generated-rule-docs/insertions/insertRuleDescription.ts index 86d0be107d30..832c35b60264 100644 --- a/packages/website/plugins/generated-rule-docs/insertions/insertRuleDescription.ts +++ b/packages/website/plugins/generated-rule-docs/insertions/insertRuleDescription.ts @@ -1,36 +1,17 @@ -import type * as mdast from 'mdast'; import type { MdxJsxFlowElement } from 'mdast-util-mdx'; import type { RuleDocsPage } from '../RuleDocsPage'; export function insertRuleDescription(page: RuleDocsPage): void { - page.spliceChildren( - 0, - 0, - { - children: [ - { - children: page.rule.meta.docs.description - .split(/`(.+?)`/) - .map((value, index, array) => ({ - type: index % 2 === 0 ? 'text' : 'inlineCode', - value: index === array.length - 1 ? `${value}.` : value, - })), - type: 'paragraph', - }, - ], - type: 'blockquote', - } as mdast.Blockquote, - { - attributes: [ - { - type: 'mdxJsxAttribute', - name: 'name', - value: page.file.stem, - }, - ], - name: 'RuleAttributes', - type: 'mdxJsxFlowElement', - } as MdxJsxFlowElement, - ); + page.spliceChildren(0, 0, `> ${page.rule.meta.docs.description}.`, { + attributes: [ + { + type: 'mdxJsxAttribute', + name: 'name', + value: page.file.stem, + }, + ], + name: 'RuleAttributes', + type: 'mdxJsxFlowElement', + } as MdxJsxFlowElement); } diff --git a/packages/website/plugins/generated-rule-docs/insertions/insertWhenNotToUseIt.ts b/packages/website/plugins/generated-rule-docs/insertions/insertWhenNotToUseIt.ts index 7365438bde60..97f03abe2061 100644 --- a/packages/website/plugins/generated-rule-docs/insertions/insertWhenNotToUseIt.ts +++ b/packages/website/plugins/generated-rule-docs/insertions/insertWhenNotToUseIt.ts @@ -1,5 +1,3 @@ -import type * as mdast from 'mdast'; - import { nodeIsHeading } from '../../utils/nodes'; import type { RuleDocsPage } from '../RuleDocsPage'; @@ -23,48 +21,8 @@ export function insertWhenNotToUseIt(page: RuleDocsPage): void { page.spliceChildren( nextHeadingIndex === -1 ? page.children.length : nextHeadingIndex - 1, 0, - { - children: [ - ...(hasExistingText ? [{ type: 'thematicBreak' }] : []), - { - type: 'text', - value: - 'Type checked lint rules are more powerful than traditional lint rules, but also require configuring ', - }, - { - type: 'link', - title: null, - url: `/getting-started/typed-linting`, - children: [ - { - type: 'text', - value: 'type checked linting', - }, - ], - }, - { - type: 'text', - value: '. See ', - }, - { - type: 'link', - title: null, - url: `/troubleshooting/typed-linting/performance`, - children: [ - { - type: 'text', - value: - 'Troubleshooting > Linting with Type Information > Performance', - }, - ], - }, - { - type: 'text', - value: - ' if you experience performance degredations after enabling type checked rules.', - }, - ], - type: 'paragraph', - } as mdast.Paragraph, + ...(hasExistingText ? ['---'] : []), + 'Type checked lint rules are more powerful than traditional lint rules, but also require configuring [type checked linting](/getting-started/typed-linting).', + 'See [Troubleshooting > Linting with Type Information > Performance](/troubleshooting/typed-linting/performance) if you experience performance degredations after enabling type checked rules.', ); } diff --git a/yarn.lock b/yarn.lock index a487669536a2..91ed28cd5251 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4450,6 +4450,13 @@ __metadata: languageName: node linkType: hard +"@rtsao/scc@npm:^1.1.0": + version: 1.1.0 + resolution: "@rtsao/scc@npm:1.1.0" + checksum: 17d04adf404e04c1e61391ed97bca5117d4c2767a76ae3e879390d6dec7b317fcae68afbf9e98badee075d0b64fa60f287729c4942021b4d19cd01db77385c01 + languageName: node + linkType: hard + "@rushstack/node-core-library@npm:5.4.1": version: 5.4.1 resolution: "@rushstack/node-core-library@npm:5.4.1" @@ -5631,7 +5638,7 @@ __metadata: languageName: unknown linkType: soft -"@typescript-eslint/eslint-plugin@8.5.0, @typescript-eslint/eslint-plugin@workspace:*, @typescript-eslint/eslint-plugin@workspace:^, @typescript-eslint/eslint-plugin@workspace:packages/eslint-plugin": +"@typescript-eslint/eslint-plugin@8.6.0, @typescript-eslint/eslint-plugin@workspace:*, @typescript-eslint/eslint-plugin@workspace:^, @typescript-eslint/eslint-plugin@workspace:packages/eslint-plugin": version: 0.0.0-use.local resolution: "@typescript-eslint/eslint-plugin@workspace:packages/eslint-plugin" dependencies: @@ -5640,12 +5647,12 @@ __metadata: "@types/marked": ^5.0.2 "@types/mdast": ^4.0.3 "@types/natural-compare": "*" - "@typescript-eslint/rule-schema-to-typescript-types": 8.5.0 - "@typescript-eslint/rule-tester": 8.5.0 - "@typescript-eslint/scope-manager": 8.5.0 - "@typescript-eslint/type-utils": 8.5.0 - "@typescript-eslint/utils": 8.5.0 - "@typescript-eslint/visitor-keys": 8.5.0 + "@typescript-eslint/rule-schema-to-typescript-types": 8.6.0 + "@typescript-eslint/rule-tester": 8.6.0 + "@typescript-eslint/scope-manager": 8.6.0 + "@typescript-eslint/type-utils": 8.6.0 + "@typescript-eslint/utils": 8.6.0 + "@typescript-eslint/visitor-keys": 8.6.0 ajv: ^6.12.6 cross-env: ^7.0.3 cross-fetch: "*" @@ -5689,16 +5696,16 @@ __metadata: languageName: unknown linkType: soft -"@typescript-eslint/parser@8.5.0, @typescript-eslint/parser@workspace:*, @typescript-eslint/parser@workspace:packages/parser": +"@typescript-eslint/parser@8.6.0, @typescript-eslint/parser@workspace:*, @typescript-eslint/parser@workspace:packages/parser": version: 0.0.0-use.local resolution: "@typescript-eslint/parser@workspace:packages/parser" dependencies: "@jest/types": 29.6.3 "@types/glob": "*" - "@typescript-eslint/scope-manager": 8.5.0 - "@typescript-eslint/types": 8.5.0 - "@typescript-eslint/typescript-estree": 8.5.0 - "@typescript-eslint/visitor-keys": 8.5.0 + "@typescript-eslint/scope-manager": 8.6.0 + "@typescript-eslint/types": 8.6.0 + "@typescript-eslint/typescript-estree": 8.6.0 + "@typescript-eslint/visitor-keys": 8.6.0 debug: ^4.3.4 downlevel-dts: "*" glob: "*" @@ -5714,28 +5721,28 @@ __metadata: languageName: unknown linkType: soft -"@typescript-eslint/rule-schema-to-typescript-types@8.5.0, @typescript-eslint/rule-schema-to-typescript-types@workspace:*, @typescript-eslint/rule-schema-to-typescript-types@workspace:packages/rule-schema-to-typescript-types": +"@typescript-eslint/rule-schema-to-typescript-types@8.6.0, @typescript-eslint/rule-schema-to-typescript-types@workspace:*, @typescript-eslint/rule-schema-to-typescript-types@workspace:packages/rule-schema-to-typescript-types": version: 0.0.0-use.local resolution: "@typescript-eslint/rule-schema-to-typescript-types@workspace:packages/rule-schema-to-typescript-types" dependencies: "@jest/types": 29.6.3 - "@typescript-eslint/type-utils": 8.5.0 - "@typescript-eslint/utils": 8.5.0 + "@typescript-eslint/type-utils": 8.6.0 + "@typescript-eslint/utils": 8.6.0 natural-compare: ^1.4.0 prettier: ^3.2.5 languageName: unknown linkType: soft -"@typescript-eslint/rule-tester@8.5.0, @typescript-eslint/rule-tester@workspace:*, @typescript-eslint/rule-tester@workspace:packages/rule-tester": +"@typescript-eslint/rule-tester@8.6.0, @typescript-eslint/rule-tester@workspace:*, @typescript-eslint/rule-tester@workspace:packages/rule-tester": version: 0.0.0-use.local resolution: "@typescript-eslint/rule-tester@workspace:packages/rule-tester" dependencies: "@jest/types": 29.6.3 "@types/json-stable-stringify-without-jsonify": ^1.0.2 "@types/lodash.merge": 4.6.9 - "@typescript-eslint/parser": 8.5.0 - "@typescript-eslint/typescript-estree": 8.5.0 - "@typescript-eslint/utils": 8.5.0 + "@typescript-eslint/parser": 8.6.0 + "@typescript-eslint/typescript-estree": 8.6.0 + "@typescript-eslint/utils": 8.6.0 ajv: ^6.12.6 chai: ^4.4.1 eslint-visitor-keys: ^4.0.0 @@ -5753,15 +5760,15 @@ __metadata: languageName: unknown linkType: soft -"@typescript-eslint/scope-manager@8.5.0, @typescript-eslint/scope-manager@workspace:*, @typescript-eslint/scope-manager@workspace:^, @typescript-eslint/scope-manager@workspace:packages/scope-manager": +"@typescript-eslint/scope-manager@8.6.0, @typescript-eslint/scope-manager@workspace:*, @typescript-eslint/scope-manager@workspace:^, @typescript-eslint/scope-manager@workspace:packages/scope-manager": version: 0.0.0-use.local resolution: "@typescript-eslint/scope-manager@workspace:packages/scope-manager" dependencies: "@jest/types": 29.6.3 "@types/glob": "*" - "@typescript-eslint/types": 8.5.0 - "@typescript-eslint/typescript-estree": 8.5.0 - "@typescript-eslint/visitor-keys": 8.5.0 + "@typescript-eslint/types": 8.6.0 + "@typescript-eslint/typescript-estree": 8.6.0 + "@typescript-eslint/visitor-keys": 8.6.0 glob: "*" jest-specific-snapshot: "*" make-dir: "*" @@ -5780,14 +5787,14 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@8.5.0, @typescript-eslint/type-utils@workspace:*, @typescript-eslint/type-utils@workspace:packages/type-utils": +"@typescript-eslint/type-utils@8.6.0, @typescript-eslint/type-utils@workspace:*, @typescript-eslint/type-utils@workspace:packages/type-utils": version: 0.0.0-use.local resolution: "@typescript-eslint/type-utils@workspace:packages/type-utils" dependencies: "@jest/types": 29.6.3 - "@typescript-eslint/parser": 8.5.0 - "@typescript-eslint/typescript-estree": 8.5.0 - "@typescript-eslint/utils": 8.5.0 + "@typescript-eslint/parser": 8.6.0 + "@typescript-eslint/typescript-estree": 8.6.0 + "@typescript-eslint/utils": 8.6.0 ajv: ^6.12.6 debug: ^4.3.4 downlevel-dts: "*" @@ -5802,7 +5809,7 @@ __metadata: languageName: unknown linkType: soft -"@typescript-eslint/types@8.5.0, @typescript-eslint/types@^8.3.0, @typescript-eslint/types@workspace:*, @typescript-eslint/types@workspace:^, @typescript-eslint/types@workspace:packages/types": +"@typescript-eslint/types@8.6.0, @typescript-eslint/types@^8.3.0, @typescript-eslint/types@workspace:*, @typescript-eslint/types@workspace:^, @typescript-eslint/types@workspace:packages/types": version: 0.0.0-use.local resolution: "@typescript-eslint/types@workspace:packages/types" dependencies: @@ -5903,13 +5910,13 @@ __metadata: languageName: unknown linkType: soft -"@typescript-eslint/typescript-estree@8.5.0, @typescript-eslint/typescript-estree@workspace:*, @typescript-eslint/typescript-estree@workspace:^, @typescript-eslint/typescript-estree@workspace:packages/typescript-estree": +"@typescript-eslint/typescript-estree@8.6.0, @typescript-eslint/typescript-estree@workspace:*, @typescript-eslint/typescript-estree@workspace:^, @typescript-eslint/typescript-estree@workspace:packages/typescript-estree": version: 0.0.0-use.local resolution: "@typescript-eslint/typescript-estree@workspace:packages/typescript-estree" dependencies: "@jest/types": 29.6.3 - "@typescript-eslint/types": 8.5.0 - "@typescript-eslint/visitor-keys": 8.5.0 + "@typescript-eslint/types": 8.6.0 + "@typescript-eslint/visitor-keys": 8.6.0 debug: ^4.3.4 fast-glob: ^3.3.2 glob: "*" @@ -5946,14 +5953,14 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@8.5.0, @typescript-eslint/utils@^8.3.0, @typescript-eslint/utils@workspace:*, @typescript-eslint/utils@workspace:^, @typescript-eslint/utils@workspace:packages/utils": +"@typescript-eslint/utils@8.6.0, @typescript-eslint/utils@^8.3.0, @typescript-eslint/utils@workspace:*, @typescript-eslint/utils@workspace:^, @typescript-eslint/utils@workspace:packages/utils": version: 0.0.0-use.local resolution: "@typescript-eslint/utils@workspace:packages/utils" dependencies: "@eslint-community/eslint-utils": ^4.4.0 - "@typescript-eslint/scope-manager": 8.5.0 - "@typescript-eslint/types": 8.5.0 - "@typescript-eslint/typescript-estree": 8.5.0 + "@typescript-eslint/scope-manager": 8.6.0 + "@typescript-eslint/types": 8.6.0 + "@typescript-eslint/typescript-estree": 8.6.0 downlevel-dts: "*" jest: 29.7.0 prettier: ^3.2.5 @@ -5982,13 +5989,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@8.5.0, @typescript-eslint/visitor-keys@workspace:*, @typescript-eslint/visitor-keys@workspace:packages/visitor-keys": +"@typescript-eslint/visitor-keys@8.6.0, @typescript-eslint/visitor-keys@workspace:*, @typescript-eslint/visitor-keys@workspace:packages/visitor-keys": version: 0.0.0-use.local resolution: "@typescript-eslint/visitor-keys@workspace:packages/visitor-keys" dependencies: "@jest/types": 29.6.3 "@types/eslint-visitor-keys": "*" - "@typescript-eslint/types": 8.5.0 + "@typescript-eslint/types": 8.6.0 downlevel-dts: "*" eslint-visitor-keys: ^3.4.3 jest: 29.7.0 @@ -6649,7 +6656,7 @@ __metadata: languageName: node linkType: hard -"array-includes@npm:^3.1.6, array-includes@npm:^3.1.7, array-includes@npm:^3.1.8": +"array-includes@npm:^3.1.6, array-includes@npm:^3.1.8": version: 3.1.8 resolution: "array-includes@npm:3.1.8" dependencies: @@ -6691,16 +6698,17 @@ __metadata: languageName: node linkType: hard -"array.prototype.findlastindex@npm:^1.2.3": - version: 1.2.3 - resolution: "array.prototype.findlastindex@npm:1.2.3" +"array.prototype.findlastindex@npm:^1.2.5": + version: 1.2.5 + resolution: "array.prototype.findlastindex@npm:1.2.5" dependencies: - call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - es-shim-unscopables: ^1.0.0 - get-intrinsic: ^1.2.1 - checksum: 31f35d7b370c84db56484618132041a9af401b338f51899c2e78ef7690fbba5909ee7ca3c59a7192085b328cc0c68c6fd1f6d1553db01a689a589ae510f3966e + call-bind: ^1.0.7 + define-properties: ^1.2.1 + es-abstract: ^1.23.2 + es-errors: ^1.3.0 + es-object-atoms: ^1.0.0 + es-shim-unscopables: ^1.0.2 + checksum: 2c81cff2a75deb95bf1ed89b6f5f2bfbfb882211e3b7cc59c3d6b87df774cd9d6b36949a8ae39ac476e092c1d4a4905f5ee11a86a456abb10f35f8211ae4e710 languageName: node linkType: hard @@ -6835,10 +6843,10 @@ __metadata: languageName: node linkType: hard -"axe-core@npm:^4.9.1": - version: 4.9.1 - resolution: "axe-core@npm:4.9.1" - checksum: 41d9227871781f96c2952e2a777fca73624959dd0e98864f6d82806a77602f82b4fc490852082a7e524d8cd864e50d8b4d9931819b4a150112981d8c932110c5 +"axe-core@npm:^4.10.0": + version: 4.10.0 + resolution: "axe-core@npm:4.10.0" + checksum: 7eca827fd8d98d7e4b561df65437be608155c613d8f262ae9e4a6ade02c156c7362dcbc3f71b4b526edce686f7c686280236bcff1d6725e2ef8327def72a8c41 languageName: node linkType: hard @@ -6853,12 +6861,10 @@ __metadata: languageName: node linkType: hard -"axobject-query@npm:~3.1.1": - version: 3.1.1 - resolution: "axobject-query@npm:3.1.1" - dependencies: - deep-equal: ^2.0.5 - checksum: c12a5da10dc7bab75e1cda9b6a3b5fcf10eba426ddf1a17b71ef65a434ed707ede7d1c4f013ba1609e970bc8c0cddac01365080d376204314e9b294719acd8a5 +"axobject-query@npm:^4.1.0": + version: 4.1.0 + resolution: "axobject-query@npm:4.1.0" + checksum: 7d1e87bf0aa7ae7a76cd39ab627b7c48fda3dc40181303d9adce4ba1d5b5ce73b5e5403ee6626ec8e91090448c887294d6144e24b6741a976f5be9347e3ae1df languageName: node linkType: hard @@ -9728,15 +9734,15 @@ __metadata: languageName: node linkType: hard -"eslint-module-utils@npm:^2.8.0": - version: 2.8.0 - resolution: "eslint-module-utils@npm:2.8.0" +"eslint-module-utils@npm:^2.9.0": + version: 2.11.0 + resolution: "eslint-module-utils@npm:2.11.0" dependencies: debug: ^3.2.7 peerDependenciesMeta: eslint: optional: true - checksum: 74c6dfea7641ebcfe174be61168541a11a14aa8d72e515f5f09af55cd0d0862686104b0524aa4b8e0ce66418a44aa38a94d2588743db5fd07a6b49ffd16921d2 + checksum: 8c2ecff3484835e031c8f1aa44119be65a058d195cce7b3ac827ad7ccc8bb5f9bcdd85230e2e3398981d07789bf4d90f3b81d106e67faf3cd26e0b34d73093af languageName: node linkType: hard @@ -9765,29 +9771,30 @@ __metadata: linkType: hard "eslint-plugin-import@npm:^2.29.1": - version: 2.29.1 - resolution: "eslint-plugin-import@npm:2.29.1" + version: 2.30.0 + resolution: "eslint-plugin-import@npm:2.30.0" dependencies: - array-includes: ^3.1.7 - array.prototype.findlastindex: ^1.2.3 + "@rtsao/scc": ^1.1.0 + array-includes: ^3.1.8 + array.prototype.findlastindex: ^1.2.5 array.prototype.flat: ^1.3.2 array.prototype.flatmap: ^1.3.2 debug: ^3.2.7 doctrine: ^2.1.0 eslint-import-resolver-node: ^0.3.9 - eslint-module-utils: ^2.8.0 - hasown: ^2.0.0 - is-core-module: ^2.13.1 + eslint-module-utils: ^2.9.0 + hasown: ^2.0.2 + is-core-module: ^2.15.1 is-glob: ^4.0.3 minimatch: ^3.1.2 - object.fromentries: ^2.0.7 - object.groupby: ^1.0.1 - object.values: ^1.1.7 + object.fromentries: ^2.0.8 + object.groupby: ^1.0.3 + object.values: ^1.2.0 semver: ^6.3.1 tsconfig-paths: ^3.15.0 peerDependencies: eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - checksum: e65159aef808136d26d029b71c8c6e4cb5c628e65e5de77f1eb4c13a379315ae55c9c3afa847f43f4ff9df7e54515c77ffc6489c6a6f81f7dd7359267577468c + checksum: 0ec1ad69c0d22f15bc4a49ee97ae757e4adfc3181996f2c4a1ed4d5028bd99bab38e7623e58ef4477ba1db8425f441e4e986367125273efa4c5f7ad2c4467a9a languageName: node linkType: hard @@ -9829,15 +9836,15 @@ __metadata: linkType: hard "eslint-plugin-jsx-a11y@npm:^6.8.0": - version: 6.9.0 - resolution: "eslint-plugin-jsx-a11y@npm:6.9.0" + version: 6.10.0 + resolution: "eslint-plugin-jsx-a11y@npm:6.10.0" dependencies: aria-query: ~5.1.3 array-includes: ^3.1.8 array.prototype.flatmap: ^1.3.2 ast-types-flow: ^0.0.8 - axe-core: ^4.9.1 - axobject-query: ~3.1.1 + axe-core: ^4.10.0 + axobject-query: ^4.1.0 damerau-levenshtein: ^1.0.8 emoji-regex: ^9.2.2 es-iterator-helpers: ^1.0.19 @@ -9849,8 +9856,8 @@ __metadata: safe-regex-test: ^1.0.3 string.prototype.includes: ^2.0.0 peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - checksum: 122cbd22bbd8c3e4a37f386ec183ada63a4ecfa7af7d40cd8a110777ac5ad5ff542f60644596a9e2582ed138a1cc6d96c5d5ca934105e29d5245d6c951ebc3ef + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + checksum: 1009deca12ddbe3624586bc5fc3534ca98d00a5841a2563cb6abd9339b984f0a99075dc2a703a517f4087eb84d659c87e60beda17645883de2ba1d86f2b20c96 languageName: node linkType: hard @@ -12187,12 +12194,12 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1": - version: 2.13.1 - resolution: "is-core-module@npm:2.13.1" +"is-core-module@npm:^2.13.0, is-core-module@npm:^2.15.1": + version: 2.15.1 + resolution: "is-core-module@npm:2.15.1" dependencies: - hasown: ^2.0.0 - checksum: 256559ee8a9488af90e4bad16f5583c6d59e92f0742e9e8bb4331e758521ee86b810b93bae44f390766ffbc518a0488b18d9dab7da9a5ff997d499efc9403f7c + hasown: ^2.0.2 + checksum: df134c168115690724b62018c37b2f5bba0d5745fa16960b329c5a00883a8bea6a5632fdb1e3efcce237c201826ba09f93197b7cd95577ea56b0df335be23633 languageName: node linkType: hard @@ -13450,12 +13457,13 @@ __metadata: linkType: hard "knip@npm:^5.9.4": - version: 5.27.0 - resolution: "knip@npm:5.27.0" + version: 5.30.2 + resolution: "knip@npm:5.30.2" dependencies: "@nodelib/fs.walk": 1.2.8 "@snyk/github-codeowners": 1.1.0 easy-table: 1.2.0 + enhanced-resolve: ^5.17.1 fast-glob: ^3.3.2 jiti: ^1.21.6 js-yaml: ^4.1.0 @@ -13463,7 +13471,6 @@ __metadata: picocolors: ^1.0.0 picomatch: ^4.0.1 pretty-ms: ^9.0.0 - resolve: ^1.22.8 smol-toml: ^1.1.4 strip-json-comments: 5.0.1 summary: 2.1.0 @@ -13475,7 +13482,7 @@ __metadata: bin: knip: bin/knip.js knip-bun: bin/knip-bun.js - checksum: b94273d2ae3cfdddcea7037c036412a303b03449a70e40b4f0ba7dbfcbf19810023c3fa1024b98dd62b701662a5c162dcb309faab2d3aebed593832107c16f11 + checksum: fafb6ee078c1e9f24e83c08c6b97c7ad82ef78d043eff6a064639e08354a4b81cbb132d705acc53874d0f0d02dc690d960f75efe8783ac349fde491bcbf1b742 languageName: node linkType: hard @@ -14036,7 +14043,7 @@ __metadata: languageName: node linkType: hard -"mdast-util-from-markdown@npm:^2.0.0": +"mdast-util-from-markdown@npm:^2.0.0, mdast-util-from-markdown@npm:^2.0.1": version: 2.0.1 resolution: "mdast-util-from-markdown@npm:2.0.1" dependencies: @@ -15146,10 +15153,10 @@ __metadata: languageName: node linkType: hard -"monaco-editor@npm:~0.50.0": - version: 0.50.0 - resolution: "monaco-editor@npm:0.50.0" - checksum: 841b047c89060bcd5e8864c671389f7cda322bd8372663ea71f42eb3d780edbbabc41b151791629fec04bbf3c53b407eb9e5b0829ef95292103d685df0994147 +"monaco-editor@npm:~0.51.0": + version: 0.51.0 + resolution: "monaco-editor@npm:0.51.0" + checksum: 86cd6e5a756407bc9564ae4c408cdaecbf3410b5bf88e22783e22e79e1e1d048c1cba7c26249e6b234731ffc1aad3660c27213ce1f3f441b3e04cf01a75b1f8c languageName: node linkType: hard @@ -15585,7 +15592,7 @@ __metadata: languageName: node linkType: hard -"object.fromentries@npm:^2.0.7, object.fromentries@npm:^2.0.8": +"object.fromentries@npm:^2.0.8": version: 2.0.8 resolution: "object.fromentries@npm:2.0.8" dependencies: @@ -15597,19 +15604,18 @@ __metadata: languageName: node linkType: hard -"object.groupby@npm:^1.0.1": - version: 1.0.1 - resolution: "object.groupby@npm:1.0.1" +"object.groupby@npm:^1.0.3": + version: 1.0.3 + resolution: "object.groupby@npm:1.0.3" dependencies: - call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - get-intrinsic: ^1.2.1 - checksum: d7959d6eaaba358b1608066fc67ac97f23ce6f573dc8fc661f68c52be165266fcb02937076aedb0e42722fdda0bdc0bbf74778196ac04868178888e9fd3b78b5 + call-bind: ^1.0.7 + define-properties: ^1.2.1 + es-abstract: ^1.23.2 + checksum: 0d30693ca3ace29720bffd20b3130451dca7a56c612e1926c0a1a15e4306061d84410bdb1456be2656c5aca53c81b7a3661eceaa362db1bba6669c2c9b6d1982 languageName: node linkType: hard -"object.values@npm:^1.1.6, object.values@npm:^1.1.7, object.values@npm:^1.2.0": +"object.values@npm:^1.1.6, object.values@npm:^1.2.0": version: 1.2.0 resolution: "object.values@npm:1.2.0" dependencies: @@ -17553,7 +17559,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.12.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.4, resolve@npm:^1.22.8, resolve@npm:~1.22.1, resolve@npm:~1.22.2": +"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.12.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.4, resolve@npm:~1.22.1, resolve@npm:~1.22.2": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -17579,7 +17585,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.12.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.4#~builtin, resolve@patch:resolve@^1.22.8#~builtin, resolve@patch:resolve@~1.22.1#~builtin, resolve@patch:resolve@~1.22.2#~builtin": +"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.12.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.4#~builtin, resolve@patch:resolve@~1.22.1#~builtin, resolve@patch:resolve@~1.22.2#~builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -19537,9 +19543,9 @@ __metadata: resolution: "typescript-eslint@workspace:packages/typescript-eslint" dependencies: "@jest/types": 29.6.3 - "@typescript-eslint/eslint-plugin": 8.5.0 - "@typescript-eslint/parser": 8.5.0 - "@typescript-eslint/utils": 8.5.0 + "@typescript-eslint/eslint-plugin": 8.6.0 + "@typescript-eslint/parser": 8.6.0 + "@typescript-eslint/utils": 8.6.0 downlevel-dts: "*" jest: 29.7.0 prettier: ^3.2.5 @@ -20258,8 +20264,9 @@ __metadata: konamimojisplosion: ^0.5.2 lz-string: ^1.5.0 make-dir: "*" + mdast-util-from-markdown: ^2.0.1 mdast-util-mdx: ^3.0.0 - monaco-editor: ~0.50.0 + monaco-editor: ~0.51.0 prettier: ^3.2.5 prism-react-renderer: ^2.3.1 raw-loader: ^4.0.2 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy