From a2bca884a6a9b647cec56e911c08c5624e40e091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Wed, 11 Sep 2024 20:25:06 -0400 Subject: [PATCH 01/24] chore: use mdast-util-from-markdown's fromMarkdown in website (#9931) --- packages/website/package.json | 1 + .../generated-rule-docs/RuleDocsPage.ts | 11 +- .../plugins/generated-rule-docs/index.ts | 2 - .../insertions/insertBaseRuleReferences.ts | 32 +----- .../insertions/insertFormattingNotice.ts | 100 ------------------ .../insertions/insertNewRuleReferences.ts | 86 ++++----------- .../insertions/insertResources.ts | 61 +---------- .../insertions/insertRuleDescription.ts | 41 ++----- .../insertions/insertWhenNotToUseIt.ts | 48 +-------- yarn.lock | 3 +- 10 files changed, 56 insertions(+), 329 deletions(-) delete mode 100644 packages/website/plugins/generated-rule-docs/insertions/insertFormattingNotice.ts diff --git a/packages/website/package.json b/packages/website/package.json index ee01833b0077..a9fc31f238f8 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -60,6 +60,7 @@ "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", "raw-loader": "^4.0.2", 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..fcd8066d3f5f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14036,7 +14036,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: @@ -20258,6 +20258,7 @@ __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 prettier: ^3.2.5 From 89e9c9831efc4bcc8872d4aef4e422ad82cf0912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Thu, 12 Sep 2024 02:38:05 -0400 Subject: [PATCH 02/24] chore: pin eslint version in `@types/eslint@v8` integration test (#9958) --- .../fixtures/flat-config-types-@types__eslint-v8/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" From 042746f399993c8ee49fd17350e8d3da488ab0a6 Mon Sep 17 00:00:00 2001 From: auvred <61150013+auvred@users.noreply.github.com> Date: Thu, 12 Sep 2024 10:42:15 +0000 Subject: [PATCH 03/24] fix(eslint-plugin): [no-deprecated] don't report recursive types in destructuring assignment twice (#9969) * fix(eslint-plugin): [no-deprecated] don't report recursive types in destructuring assignment twice * chore: fmt * fix test loc --- .../eslint-plugin/src/rules/no-deprecated.ts | 12 +- .../tests/rules/no-deprecated.test.ts | 106 ++++++++++++++++++ 2 files changed, 111 insertions(+), 7 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-deprecated.ts b/packages/eslint-plugin/src/rules/no-deprecated.ts index 9c9a30cc00bc..8729a945470e 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: diff --git a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts index fc7bbe927d41..55384f4b274d 100644 --- a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts +++ b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts @@ -157,6 +157,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: [ { @@ -1286,5 +1310,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', + }, + ], + }, ], }); From 3710c9c32c8bc07bf46b67317f5f0d4d5f072987 Mon Sep 17 00:00:00 2001 From: Kim Sang Du Date: Thu, 12 Sep 2024 22:32:59 +0900 Subject: [PATCH 04/24] feat(type-utils): isNullableType add Void logic (#9937) * fix: isNullableType util add Void * fix: lint error --- .../eslint-plugin/src/rules/no-unnecessary-condition.ts | 3 +-- .../src/rules/no-unnecessary-type-assertion.ts | 2 +- .../tests/rules/no-unnecessary-condition.test.ts | 9 +++++++++ packages/type-utils/src/predicates.ts | 3 ++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index 8f193dde47fa..b7c5d8aba76d 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -644,10 +644,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)) ); } 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/tests/rules/no-unnecessary-condition.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts index b8a9ab9981d9..4ac9c8b646de 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -867,6 +867,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 }, 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, ); } From ee2617ac5c23e76da826b11cc8d4a178ad244d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Thu, 12 Sep 2024 16:15:21 -0400 Subject: [PATCH 05/24] docs: clean up remaining allowDefaultProject(ForFiles) vestiges (#9959) --- docs/developers/Custom_Rules.mdx | 2 +- docs/packages/Rule_Tester.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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: From c2f93c1f264cbc38c67b8ebe0c8e602f1a968d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Thu, 12 Sep 2024 18:20:27 -0400 Subject: [PATCH 06/24] chore: allow selecting eslint-plugin in 'Another Package' issues (#9979) --- .github/ISSUE_TEMPLATE/06-bug-report-other.yaml | 1 + .github/ISSUE_TEMPLATE/07-enhancement-other.yaml | 1 + 2 files changed, 2 insertions(+) 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 From 2d6872a4c9be3b92ac466c53e98d84c7fad54a77 Mon Sep 17 00:00:00 2001 From: "typescript-eslint[bot]" <53356952+typescript-eslint[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:14:15 +0930 Subject: [PATCH 07/24] chore: update sponsors (#9980) Co-authored-by: typescript-eslint[bot] --- packages/website/data/sponsors.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" From 9a80067783638c99e09fd340310a320c8676c083 Mon Sep 17 00:00:00 2001 From: auvred <61150013+auvred@users.noreply.github.com> Date: Fri, 13 Sep 2024 01:44:34 +0000 Subject: [PATCH 08/24] fix(eslint-plugin): [no-deprecated] report on deprecated variables used in destructuring assignment (#9978) --- .../eslint-plugin/src/rules/no-deprecated.ts | 5 +-- .../tests/rules/no-deprecated.test.ts | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-deprecated.ts b/packages/eslint-plugin/src/rules/no-deprecated.ts index 8729a945470e..92a5c913c104 100644 --- a/packages/eslint-plugin/src/rules/no-deprecated.ts +++ b/packages/eslint-plugin/src/rules/no-deprecated.ts @@ -193,10 +193,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/tests/rules/no-deprecated.test.ts b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts index 55384f4b274d..f0c071fb5a84 100644 --- a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts +++ b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts @@ -295,6 +295,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 }; From 2a956b22451350588ca81ffa3442130f159dfbf5 Mon Sep 17 00:00:00 2001 From: auvred <61150013+auvred@users.noreply.github.com> Date: Fri, 13 Sep 2024 01:45:50 +0000 Subject: [PATCH 09/24] fix(eslint-plugin): [no-deprecated] report on deprecated properties with function-like types (#9977) --- .../eslint-plugin/src/rules/no-deprecated.ts | 19 ++- .../tests/rules/no-deprecated.test.ts | 160 ++++++++++++++++++ packages/utils/tests/ts-eslint/ESLint.test.ts | 1 + 3 files changed, 179 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/no-deprecated.ts b/packages/eslint-plugin/src/rules/no-deprecated.ts index 92a5c913c104..ddbe08eb0926 100644 --- a/packages/eslint-plugin/src/rules/no-deprecated.ts +++ b/packages/eslint-plugin/src/rules/no-deprecated.ts @@ -172,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(); diff --git a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts index f0c071fb5a84..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; @@ -651,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 */ @@ -737,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 { @@ -1155,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( 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: { From 2ee528c5c72b7f3fd73a39817b04cc5be8c507d9 Mon Sep 17 00:00:00 2001 From: Rainer Hahnekamp Date: Fri, 13 Sep 2024 14:52:13 +0200 Subject: [PATCH 10/24] docs: add note about typescript-eslint as peer dependency (#9820) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: add note about typescript-eslint as peer dependency Explain, that there is not need to define the parser and therefore include `typescript-eslint` as peer dependency. * Update docs/developers/ESLint_Plugins.mdx --------- Co-authored-by: Josh Goldberg ✨ --- docs/developers/ESLint_Plugins.mdx | 3 +++ 1 file changed, 3 insertions(+) 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): From c11ca06f0042d2784de7e169f49d85aba4536d5a Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Fri, 13 Sep 2024 08:24:26 -0500 Subject: [PATCH 11/24] chore(eslint-plugin): make utility for static member access (#9836) * WIP * WIP * prefer-includes * prefer-regexp-exec * string-startswith-endswith * support template literal * more places * more methods * class literal property style * use-unknown-in-catch-callback * cleanup * fix type assertion * remove unnecesssary checks --- .../src/rules/class-literal-property-style.ts | 30 +++---- .../src/rules/class-methods-use-this.ts | 7 +- .../rules/explicit-module-boundary-types.ts | 22 ++--- .../src/rules/no-floating-promises.ts | 84 +++++++------------ .../eslint-plugin/src/rules/prefer-find.ts | 29 +------ .../src/rules/prefer-includes.ts | 8 +- .../src/rules/prefer-promise-reject-errors.ts | 8 +- .../src/rules/prefer-reduce-type-parameter.ts | 21 +---- .../src/rules/prefer-regexp-exec.ts | 6 +- .../rules/prefer-string-starts-ends-with.ts | 10 ++- .../src/rules/require-array-sort-compare.ts | 8 +- .../use-unknown-in-catch-callback-variable.ts | 22 +---- packages/eslint-plugin/src/util/misc.ts | 46 +++++++++- 13 files changed, 130 insertions(+), 171 deletions(-) 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-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts index 3b0265e1718f..5a83a338055b 100644 --- a/packages/eslint-plugin/src/rules/no-floating-promises.ts +++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts @@ -8,6 +8,7 @@ import { createRule, getOperatorPrecedence, getParserServices, + getStaticMemberAccessValue, isBuiltinSymbolLike, OperatorPrecedence, readonlynessOptionsDefaults, @@ -334,27 +335,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. @@ -485,41 +497,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/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/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/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, From 77e65df393a40225dcfa3f5f3a4760655f359397 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Fri, 13 Sep 2024 08:38:57 -0500 Subject: [PATCH 12/24] fix(eslint-plugin): [no-unnecessary-condition] properly reflect multiple negations in message (#9940) * add failing test * fix * expand reporting range --- .../src/rules/no-unnecessary-condition.ts | 19 ++++++++++--------- .../rules/no-unnecessary-condition.test.ts | 14 +++++++++++--- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index b7c5d8aba76d..83869ec9ad3c 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -233,21 +233,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 +259,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 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 4ac9c8b646de..b83fee7fd4f8 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -1918,7 +1918,7 @@ if (!a) { } `, output: null, - errors: [ruleError(3, 6, 'alwaysTruthy')], + errors: [ruleError(3, 5, 'alwaysTruthy')], }, { code: ` @@ -1927,7 +1927,7 @@ if (!a) { } `, output: null, - errors: [ruleError(3, 6, 'alwaysFalsy')], + errors: [ruleError(3, 5, 'alwaysFalsy')], }, { code: ` @@ -1940,7 +1940,7 @@ if (!speech) { } `, output: null, - errors: [ruleError(7, 6, 'never')], + errors: [ruleError(7, 5, 'never')], }, { code: ` @@ -2286,6 +2286,14 @@ foo?.['bar']?.().toExponential(); }, ], }, + { + code: ` + const a = true; + if (!!a) { + } + `, + errors: [ruleError(3, 13, 'alwaysTruthy')], + }, // "branded" types unnecessaryConditionTest('"" & {}', 'alwaysFalsy'), From b353a8653a6ed4c733726451ad657cc2150efb8c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 14 Sep 2024 17:25:07 +0000 Subject: [PATCH 13/24] chore(deps): update dependency knip to v5.30.0 (#9952) * chore(deps): update dependency knip to v5.30.0 * Update knip.ts --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: auvred <61150013+auvred@users.noreply.github.com> --- knip.ts | 2 +- yarn.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) 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/yarn.lock b/yarn.lock index fcd8066d3f5f..ebf5959a84ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13450,12 +13450,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 +13464,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 +13475,7 @@ __metadata: bin: knip: bin/knip.js knip-bun: bin/knip-bun.js - checksum: b94273d2ae3cfdddcea7037c036412a303b03449a70e40b4f0ba7dbfcbf19810023c3fa1024b98dd62b701662a5c162dcb309faab2d3aebed593832107c16f11 + checksum: fafb6ee078c1e9f24e83c08c6b97c7ad82ef78d043eff6a064639e08354a4b81cbb132d705acc53874d0f0d02dc690d960f75efe8783ac349fde491bcbf1b742 languageName: node linkType: hard @@ -17553,7 +17553,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 +17579,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: From ef3384e06028eda0fc6aef641f1fa93618f451f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Sun, 15 Sep 2024 11:48:24 -0400 Subject: [PATCH 14/24] fix(typescript-estree): don't throw on missing tsconfig.json by default in project service (#9989) --- .../create-program/createProjectService.ts | 29 +++++++++++-------- .../tests/lib/createProjectService.test.ts | 7 ++--- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/packages/typescript-estree/src/create-program/createProjectService.ts b/packages/typescript-estree/src/create-program/createProjectService.ts index a9634985242c..9b8070c56219 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); @@ -126,7 +127,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 +136,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..fed68f8ec176 100644 --- a/packages/typescript-estree/tests/lib/createProjectService.test.ts +++ b/packages/typescript-estree/tests/lib/createProjectService.test.ts @@ -65,7 +65,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 +78,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 +130,7 @@ describe('createProjectService', () => { createProjectService( { allowDefaultProject: ['file.js'], + defaultProject: 'tsconfig.json', }, undefined, undefined, From 8651a07060a514655f62f964e981a287eae036f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Mon, 16 Sep 2024 08:47:20 -0400 Subject: [PATCH 15/24] feat(typescript-estree): disable plugin loading by default in project service (#9964) * feat(typescript-estree): disable plugin loading by default in project service * Apply suggestions from code review Co-authored-by: Kirk Waiblinger --------- Co-authored-by: Kirk Waiblinger --- docs/packages/Parser.mdx | 18 ++++++++++ docs/packages/TypeScript_ESTree.mdx | 5 +++ packages/types/src/parser-options.ts | 5 +++ .../create-program/createProjectService.ts | 12 +++++++ .../tests/lib/createProjectService.test.ts | 34 +++++++++++++++++-- 5 files changed, 72 insertions(+), 2 deletions(-) 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/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/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-estree/src/create-program/createProjectService.ts b/packages/typescript-estree/src/create-program/createProjectService.ts index 9b8070c56219..89158d15e068 100644 --- a/packages/typescript-estree/src/create-program/createProjectService.ts +++ b/packages/typescript-estree/src/create-program/createProjectService.ts @@ -66,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 = { diff --git a/packages/typescript-estree/tests/lib/createProjectService.test.ts b/packages/typescript-estree/tests/lib/createProjectService.test.ts index fed68f8ec176..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', @@ -310,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( { From 682299eabfb5e9133e3e3e39390a83ad40fefe94 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Mon, 16 Sep 2024 07:47:42 -0500 Subject: [PATCH 16/24] feat(eslint-plugin): [no-unnecessary-condition] check switch cases (#9912) * add new feature without testing it * add tests & cleanup * forgot to update the docs * refine report location * type-validate operator * WIP tests * refine tests * remove unused import * clarify docs Co-authored-by: Kirk Waiblinger * lint --------- Co-authored-by: Kirk Waiblinger --- .../docs/rules/no-unnecessary-condition.mdx | 1 + .../src/rules/no-unnecessary-condition.ts | 44 ++++++++++++++----- .../rules/no-unnecessary-condition.test.ts | 9 ++++ 3 files changed, 44 insertions(+), 10 deletions(-) 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/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index 83869ec9ad3c..663bd20ce140 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -363,15 +363,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; @@ -390,7 +393,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; } @@ -719,13 +722,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/tests/rules/no-unnecessary-condition.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts index b83fee7fd4f8..1e61479c8a7c 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; @@ -910,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: [ @@ -922,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 From 2a809e29406d193e7efd1f43b82ac8016056a8a3 Mon Sep 17 00:00:00 2001 From: auvred <61150013+auvred@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:01:09 +0000 Subject: [PATCH 17/24] test(eslint-plugin): [no-unnecessary-type-parameters] add tests with intrinsic `NoInfer` (#9981) test(eslint-plugin): [no-unnecessary-type-parameters] add tests with intrinsic NoInfer --- .../tests/rules/no-unnecessary-type-parameters.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) 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: [ From 75df60587d045438df448d76cc0ee093c8c31e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Mon, 16 Sep 2024 09:01:55 -0400 Subject: [PATCH 18/24] docs: explicitly indicate blog posts are major version upgrade guides (#9982) --- CHANGELOG.md | 5 +++++ .../blog/2024-07-31-announcing-typescript-eslint-v8.md | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf59d2eafd8e..4de361bad4ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -145,6 +145,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 +826,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 +1471,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/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. From af92611f53d21243656d5588e9e6a5fde1ab4d51 Mon Sep 17 00:00:00 2001 From: Abraham Guo Date: Mon, 16 Sep 2024 08:45:56 -0500 Subject: [PATCH 19/24] feat: add `allow` option for `restrict-template-expressions` (#8556) * WIP * finish logic * add doc section * update snapshot * refactors and renames * simplify type flag testers * rename * write tests * simplify test * ignoredtypenames * restore suppression * rename option * change option type * change to typematchesspecifier * finish * change defaults * add tests * change defaults * add documentation * removed extra quote * add description * add language * fix JS error * fix types from node * extract repeated documentation * update comment clarifying string array * update import path * update snapshot * revert unnecessary formatting change * add test for type from Node * restore comment * push more into var * sync tests * move to shared folder * add shared folder to test * update snapshot * change check for consistency * clarify documentation for string form * clean up * make docs secret, tweak tests * revert * cleanup * finish rename * simplify tests * change URL to Error * add tsconfig withdom * revert docs tsconfig change * fix TS error * simplify docs * update snapshot * update docs * remove unused import * simplify unit tests to only use non-DOM types --- .../rules/restrict-template-expressions.mdx | 19 +- .../src/rules/no-floating-promises.ts | 14 +- .../rules/restrict-template-expressions.ts | 42 +++-- .../restrict-template-expressions.shot | 8 + packages/eslint-plugin/tests/docs.test.ts | 1 + .../restrict-template-expressions.test.ts | 15 ++ .../restrict-template-expressions.shot | 113 ++++++++++++ .../type-utils/src/TypeOrValueSpecifier.ts | 168 ++++++++++-------- packages/type-utils/src/isTypeReadonly.ts | 15 +- .../tests/TypeOrValueSpecifier.test.ts | 19 +- 10 files changed, 292 insertions(+), 122 deletions(-) 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/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts index 5a83a338055b..9c1cc46525c4 100644 --- a/packages/eslint-plugin/src/rules/no-floating-promises.ts +++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts @@ -13,7 +13,7 @@ import { OperatorPrecedence, readonlynessOptionsDefaults, readonlynessOptionsSchema, - typeMatchesSpecifier, + typeMatchesSomeSpecifier, } from '../util'; type Options = [ @@ -239,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, ); } @@ -419,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; 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/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/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/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/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]>([ [ From 9652c02f1675e3e5e262321940b9b7dd61a565db Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:01:51 -0400 Subject: [PATCH 20/24] chore(deps): update dependency monaco-editor to ~0.51.0 (#9907) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- packages/website/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/website/package.json b/packages/website/package.json index a9fc31f238f8..55d3897cfbff 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -62,7 +62,7 @@ "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/yarn.lock b/yarn.lock index ebf5959a84ca..32f2d1fba24a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15146,10 +15146,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 @@ -20260,7 +20260,7 @@ __metadata: 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 From 95a947a15ce63072c43e2884548da805dc607e83 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:02:27 -0400 Subject: [PATCH 21/24] chore(deps): update dependency eslint-plugin-import to v2.30.0 (#9960) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 88 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/yarn.lock b/yarn.lock index 32f2d1fba24a..3fd81ab4f674 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" @@ -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 @@ -9728,15 +9736,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 +9773,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 @@ -12187,12 +12196,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 @@ -15585,7 +15594,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 +15606,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: From 454d37e1c8b5d365e207194acb80916eb743f3e5 Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Mon, 16 Sep 2024 23:39:13 +0700 Subject: [PATCH 22/24] feat(eslint-plugin): [no-misused-promises] check array predicate return (#9955) * feat(eslint-plugin): [no-misused-promises] check array predicate return * refactor * fix review --- .../docs/rules/no-misused-promises.mdx | 5 ++ .../src/rules/no-misused-promises.ts | 23 ++++++++ .../src/rules/no-unnecessary-condition.ts | 23 ++------ packages/eslint-plugin/src/util/index.ts | 1 + .../util/isArrayMethodCallWithPredicate.ts | 43 +++++++++++++++ .../no-misused-promises.shot | 6 +++ .../tests/rules/no-misused-promises.test.ts | 53 +++++++++++++++++++ .../rules/no-unnecessary-condition.test.ts | 2 + 8 files changed, 138 insertions(+), 18 deletions(-) create mode 100644 packages/eslint-plugin/src/util/isArrayMethodCallWithPredicate.ts 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/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 663bd20ce140..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, @@ -461,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 ( 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/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/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 1e61479c8a7c..9fbd0eb7ac6f 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -1300,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 From 036e7e3e3a06a833d2989ab1e11c496d989d5a02 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:39:55 -0400 Subject: [PATCH 23/24] chore(deps): update dependency eslint-plugin-jsx-a11y to v6.10.0 (#9966) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3fd81ab4f674..1ddc8cd54e0e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6843,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 @@ -6861,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 @@ -9838,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 @@ -9858,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 From 343710e0b68868836ae01c0271472adcea4f1676 Mon Sep 17 00:00:00 2001 From: "typescript-eslint[bot]" Date: Mon, 16 Sep 2024 17:16:45 +0000 Subject: [PATCH 24/24] chore(release): publish 8.6.0 --- CHANGELOG.md | 29 +++++++ packages/ast-spec/CHANGELOG.md | 6 ++ packages/ast-spec/package.json | 2 +- packages/eslint-plugin/CHANGELOG.md | 35 ++++++++ packages/eslint-plugin/package.json | 14 ++-- packages/parser/CHANGELOG.md | 6 ++ packages/parser/package.json | 10 +-- .../CHANGELOG.md | 6 ++ .../package.json | 6 +- packages/rule-tester/CHANGELOG.md | 6 ++ packages/rule-tester/package.json | 8 +- packages/scope-manager/CHANGELOG.md | 6 ++ packages/scope-manager/package.json | 8 +- packages/type-utils/CHANGELOG.md | 20 +++++ packages/type-utils/package.json | 8 +- packages/types/CHANGELOG.md | 18 +++++ packages/types/package.json | 2 +- packages/typescript-eslint/CHANGELOG.md | 6 ++ packages/typescript-eslint/package.json | 8 +- packages/typescript-estree/CHANGELOG.md | 23 ++++++ packages/typescript-estree/package.json | 6 +- packages/utils/CHANGELOG.md | 18 +++++ packages/utils/package.json | 8 +- packages/visitor-keys/CHANGELOG.md | 6 ++ packages/visitor-keys/package.json | 4 +- yarn.lock | 80 +++++++++---------- 26 files changed, 267 insertions(+), 82 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4de361bad4ab..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) 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/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/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/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/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/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/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/yarn.lock b/yarn.lock index 1ddc8cd54e0e..91ed28cd5251 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5638,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: @@ -5647,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: "*" @@ -5696,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: "*" @@ -5721,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 @@ -5760,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: "*" @@ -5787,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: "*" @@ -5809,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: @@ -5910,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: "*" @@ -5953,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 @@ -5989,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 @@ -19543,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 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