From 4dcf1ba1a0e9952c82cb366000e1104a406633b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Feb 2022 18:40:51 -0800 Subject: [PATCH 01/12] chore: bump rollup from 2.67.2 to 2.67.3 (#4578) Bumps [rollup](https://github.com/rollup/rollup) from 2.67.2 to 2.67.3. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v2.67.2...v2.67.3) --- updated-dependencies: - dependency-name: rollup dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index e86a9f956597..418345426037 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13468,9 +13468,9 @@ rimraf@^2.6.3: glob "^7.1.3" rollup@^2.59.0: - version "2.67.2" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.67.2.tgz#d95e15f60932ad21e05a870bd0aa0b235d056f04" - integrity sha512-hoEiBWwZtf1QdK3jZIq59L0FJj4Fiv4RplCO4pvCRC86qsoFurWB4hKQIjoRf3WvJmk5UZ9b0y5ton+62fC7Tw== + version "2.67.3" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.67.3.tgz#3f04391fc296f807d067c9081d173e0a33dbd37e" + integrity sha512-G/x1vUwbGtP6O5ZM8/sWr8+p7YfZhI18pPqMRtMYMWSbHjKZ/ajHGiM+GWNTlWyOR0EHIdT8LHU+Z4ciIZ1oBw== optionalDependencies: fsevents "~2.3.2" From 0c63f26ad0b72517a59ad8608ccdaa4dbcde7659 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Feb 2022 18:41:05 -0800 Subject: [PATCH 02/12] chore: bump webpack from 5.69.0 to 5.69.1 (#4579) Bumps [webpack](https://github.com/webpack/webpack) from 5.69.0 to 5.69.1. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.69.0...v5.69.1) --- updated-dependencies: - dependency-name: webpack dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 418345426037..ccca33603c21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6887,7 +6887,7 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enhanced-resolve@^5.8.3, enhanced-resolve@^5.9.0: +enhanced-resolve@^5.8.3: version "5.9.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz#49ac24953ac8452ed8fed2ef1340fc8e043667ee" integrity sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA== @@ -15373,9 +15373,9 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.61.0, webpack@^5.64.0: - version "5.69.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.69.0.tgz#c9eb607d4f6c49f1e5755492323a7b055c3450e3" - integrity sha512-E5Fqu89Gu8fR6vejRqu26h8ld/k6/dCVbeGUcuZjc+goQHDfCPU9rER71JmdtBYGmci7Ec2aFEATQ2IVXKy2wg== + version "5.69.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.69.1.tgz#8cfd92c192c6a52c99ab00529b5a0d33aa848dc5" + integrity sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^0.0.51" @@ -15386,7 +15386,7 @@ webpack@^5.61.0, webpack@^5.64.0: acorn-import-assertions "^1.7.6" browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.9.0" + enhanced-resolve "^5.8.3" es-module-lexer "^0.9.0" eslint-scope "5.1.1" events "^3.2.0" From f106e4b95e824ebb68141bce3d3207448d50c860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20De=20Boey?= Date: Thu, 24 Feb 2022 00:37:08 +0100 Subject: [PATCH 03/12] feat(utils): expose `ast-utils`' helpers (#4503) --- packages/utils/src/ast-utils/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/utils/src/ast-utils/index.ts b/packages/utils/src/ast-utils/index.ts index c0ab325299ad..66e528aa3e34 100644 --- a/packages/utils/src/ast-utils/index.ts +++ b/packages/utils/src/ast-utils/index.ts @@ -1,3 +1,4 @@ +export * from './eslint-utils'; +export * from './helpers'; export * from './misc'; export * from './predicates'; -export * from './eslint-utils'; From 63d051eed29dcf71015a23992feac0a8f92717a0 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 23 Feb 2022 18:39:15 -0500 Subject: [PATCH 04/12] feat(eslint-plugin): add `no-redundant-type-constituents` rule (#4378) --- .cspell.json | 5 +- packages/eslint-plugin/README.md | 1 + .../rules/no-redundant-type-constituents.md | 85 ++ packages/eslint-plugin/src/configs/all.ts | 1 + packages/eslint-plugin/src/rules/index.ts | 2 + .../rules/no-redundant-type-constituents.ts | 457 ++++++++++ packages/eslint-plugin/src/util/misc.ts | 21 + .../no-redundant-type-constituents.test.ts | 787 ++++++++++++++++++ packages/type-utils/src/predicates.ts | 19 + 9 files changed, 1376 insertions(+), 2 deletions(-) create mode 100644 packages/eslint-plugin/docs/rules/no-redundant-type-constituents.md create mode 100644 packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts create mode 100644 packages/eslint-plugin/tests/rules/no-redundant-type-constituents.test.ts diff --git a/.cspell.json b/.cspell.json index 898e10932518..4a8c0ad226be 100644 --- a/.cspell.json +++ b/.cspell.json @@ -71,8 +71,8 @@ "IIFE", "IIFEs", "linebreaks", - "markdownlint", "lzstring", + "markdownlint", "necroing", "nocheck", "nullish", @@ -101,14 +101,15 @@ "transpiled", "transpiles", "transpiling", - "tsvfs", "tsconfigs", "tsutils", + "tsvfs", "typedef", "typedefs", "unfixable", "unoptimized", "unprefixed", + "upsert", "Zacher" ], "overrides": [ diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 47012b81b8fc..d10d9f079907 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -136,6 +136,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/no-non-null-asserted-optional-chain`](./docs/rules/no-non-null-asserted-optional-chain.md) | Disallows using a non-null assertion after an optional chain expression | :white_check_mark: | | | | [`@typescript-eslint/no-non-null-assertion`](./docs/rules/no-non-null-assertion.md) | Disallows non-null assertions using the `!` postfix operator | :white_check_mark: | | | | [`@typescript-eslint/no-parameter-properties`](./docs/rules/no-parameter-properties.md) | Disallow the use of parameter properties in class constructors | | | | +| [`@typescript-eslint/no-redundant-type-constituents`](./docs/rules/no-redundant-type-constituents.md) | Disallow members of unions and intersections that do nothing or override type information | | | :thought_balloon: | | [`@typescript-eslint/no-require-imports`](./docs/rules/no-require-imports.md) | Disallows invocation of `require()` | | | | | [`@typescript-eslint/no-this-alias`](./docs/rules/no-this-alias.md) | Disallow aliasing `this` | :white_check_mark: | | | | [`@typescript-eslint/no-type-alias`](./docs/rules/no-type-alias.md) | Disallow the use of type aliases | | | | diff --git a/packages/eslint-plugin/docs/rules/no-redundant-type-constituents.md b/packages/eslint-plugin/docs/rules/no-redundant-type-constituents.md new file mode 100644 index 000000000000..e427c671d3fc --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-redundant-type-constituents.md @@ -0,0 +1,85 @@ +# `no-redundant-type-constituents` + +Disallow members of unions and intersections that do nothing or override type information. + +## Rule Details + +Some types can override some other types ("constituents") in a union or intersection and/or be overridden by some other types. +TypeScript's set theory of types includes cases where a constituent type might be useless in the parent union or intersection. + +Within `|` unions: + +- `any` and `unknown` "override" all other union members +- `never` is dropped from unions in any position except when in a return type position +- primitive types such as `string` "override" any of their literal types such as `""` + +Within `&` intersections: + +- `any` and `never` "override" all other intersection members +- `unknown` is dropped from intersections +- literal types "override" any primitive types in an intersection +- literal types such as `""` "override" any of their primitive types such as `string` + +Examples of code for this rule: + + + +### ❌ Incorrect + +```ts +type UnionAny = any | 'foo'; +type UnionUnknown = unknown | 'foo'; +type UnionNever = never | 'foo'; + +type UnionBooleanLiteral = boolean | false; +type UnionNumberLiteral = number | 1; +type UnionStringLiteral = string | 'foo'; + +type IntersectionAny = any & 'foo'; +type IntersectionUnknown = string & unknown; +type IntersectionNever = string | never; + +type IntersectionBooleanLiteral = boolean & false; +type IntersectionNumberLiteral = number & 1; +type IntersectionStringLiteral = string & 'foo'; +``` + +### ✅ Correct + +```ts +type UnionAny = any; +type UnionUnknown = unknown; +type UnionNever = never; + +type UnionBooleanLiteral = boolean; +type UnionNumberLiteral = number; +type UnionStringLiteral = string; + +type IntersectionAny = any; +type IntersectionUnknown = string; +type IntersectionNever = string; + +type IntersectionBooleanLiteral = false; +type IntersectionNumberLiteral = 1; +type IntersectionStringLiteral = 'foo'; + +type ReturnUnionNever = () => string | never; +``` + +## Limitations + +This rule plays it safe and only works with bottom types, top types, and comparing literal types to primitive types. +It also does not provide an auto-fixer just yet. + +## Further Reading + +- [Union Types](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types) +- [Intersection Types](https://www.typescriptlang.org/docs/handbook/2/objects.html#intersection-types) +- [Bottom Types](https://en.wikipedia.org/wiki/Bottom_type) +- [Top Types](https://en.wikipedia.org/wiki/Top_type) + +## Attributes + +- [ ] ✅ Recommended +- [ ] 🔧 Fixable +- [x] 💭 Requires type information diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts index 538be53ce583..c052ce4eb37d 100644 --- a/packages/eslint-plugin/src/configs/all.ts +++ b/packages/eslint-plugin/src/configs/all.ts @@ -88,6 +88,7 @@ export = { '@typescript-eslint/no-parameter-properties': 'error', 'no-redeclare': 'off', '@typescript-eslint/no-redeclare': 'error', + '@typescript-eslint/no-redundant-type-constituents': 'error', '@typescript-eslint/no-require-imports': 'error', 'no-restricted-imports': 'off', '@typescript-eslint/no-restricted-imports': 'error', diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 83e76f0a23d0..96251c3c22f0 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -60,6 +60,7 @@ import noNonNullAssertedOptionalChain from './no-non-null-asserted-optional-chai import noNonNullAssertion from './no-non-null-assertion'; import noParameterProperties from './no-parameter-properties'; import noRedeclare from './no-redeclare'; +import noRedundantTypeConstituents from './no-redundant-type-constituents'; import noRequireImports from './no-require-imports'; import noRestrictedImports from './no-restricted-imports'; import noShadow from './no-shadow'; @@ -183,6 +184,7 @@ export default { 'no-non-null-assertion': noNonNullAssertion, 'no-parameter-properties': noParameterProperties, 'no-redeclare': noRedeclare, + 'no-redundant-type-constituents': noRedundantTypeConstituents, 'no-require-imports': noRequireImports, 'no-restricted-imports': noRestrictedImports, 'no-shadow': noShadow, diff --git a/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts b/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts new file mode 100644 index 000000000000..1ee8c3c49594 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts @@ -0,0 +1,457 @@ +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/utils'; +import * as tsutils from 'tsutils'; +import * as ts from 'typescript'; +import * as util from '../util'; + +const literalToPrimitiveTypeFlags = { + [ts.TypeFlags.BigIntLiteral]: ts.TypeFlags.BigInt, + [ts.TypeFlags.BooleanLiteral]: ts.TypeFlags.Boolean, + [ts.TypeFlags.NumberLiteral]: ts.TypeFlags.Number, + [ts.TypeFlags.StringLiteral]: ts.TypeFlags.String, + [ts.TypeFlags.TemplateLiteral]: ts.TypeFlags.String, +} as const; + +const literalTypeFlags = [ + ts.TypeFlags.BigIntLiteral, + ts.TypeFlags.BooleanLiteral, + ts.TypeFlags.NumberLiteral, + ts.TypeFlags.StringLiteral, + ts.TypeFlags.TemplateLiteral, +] as const; + +const primitiveTypeFlags = [ + ts.TypeFlags.BigInt, + ts.TypeFlags.Boolean, + ts.TypeFlags.Number, + ts.TypeFlags.String, +] as const; + +const primitiveTypeFlagNames = { + [ts.TypeFlags.BigInt]: 'bigint', + [ts.TypeFlags.Boolean]: 'boolean', + [ts.TypeFlags.Number]: 'number', + [ts.TypeFlags.String]: 'string', +} as const; + +const primitiveTypeFlagTypes = { + bigint: ts.TypeFlags.BigIntLiteral, + boolean: ts.TypeFlags.BooleanLiteral, + number: ts.TypeFlags.NumberLiteral, + string: ts.TypeFlags.StringLiteral, +} as const; + +const keywordNodeTypesToTsTypes = new Map([ + [TSESTree.AST_NODE_TYPES.TSAnyKeyword, ts.TypeFlags.Any], + [TSESTree.AST_NODE_TYPES.TSBigIntKeyword, ts.TypeFlags.BigInt], + [TSESTree.AST_NODE_TYPES.TSBooleanKeyword, ts.TypeFlags.Boolean], + [TSESTree.AST_NODE_TYPES.TSNeverKeyword, ts.TypeFlags.Never], + [TSESTree.AST_NODE_TYPES.TSUnknownKeyword, ts.TypeFlags.Unknown], + [TSESTree.AST_NODE_TYPES.TSNumberKeyword, ts.TypeFlags.Number], + [TSESTree.AST_NODE_TYPES.TSStringKeyword, ts.TypeFlags.String], +]); + +type PrimitiveTypeFlag = typeof primitiveTypeFlags[number]; + +interface TypeFlagsWithName { + typeFlags: ts.TypeFlags; + typeName: string; +} + +interface TypeNodeWithValue { + literalValue: unknown; + typeNode: TSESTree.TypeNode; +} + +function addToMapGroup( + map: Map, + key: Key, + value: Value, +): void { + const existing = map.get(key); + + if (existing) { + existing.push(value); + } else { + map.set(key, [value]); + } +} + +function describeLiteralType(type: ts.Type): string { + if (type.isStringLiteral()) { + return JSON.stringify(type.value); + } + + if (type.isLiteral()) { + return type.value.toString(); + } + + if (util.isTypeAnyType(type)) { + return 'any'; + } + + if (util.isTypeNeverType(type)) { + return 'never'; + } + + if (util.isTypeUnknownType(type)) { + return 'unknown'; + } + + if (util.isTypeTemplateLiteralType(type)) { + return 'template literal type'; + } + + if (util.isTypeBigIntLiteralType(type)) { + return `${type.value.negative ? '-' : ''}${type.value.base10Value}n`; + } + + if (tsutils.isBooleanLiteralType(type, true)) { + return 'true'; + } + + if (tsutils.isBooleanLiteralType(type, false)) { + return 'false'; + } + + return 'literal type'; +} + +function describeLiteralTypeNode(typeNode: TSESTree.TypeNode): string { + switch (typeNode.type) { + case AST_NODE_TYPES.TSAnyKeyword: + return 'any'; + case AST_NODE_TYPES.TSBooleanKeyword: + return 'boolean'; + case AST_NODE_TYPES.TSNeverKeyword: + return 'never'; + case AST_NODE_TYPES.TSNumberKeyword: + return 'number'; + case AST_NODE_TYPES.TSStringKeyword: + return 'string'; + case AST_NODE_TYPES.TSUnknownKeyword: + return 'unknown'; + case AST_NODE_TYPES.TSLiteralType: + switch (typeNode.literal.type) { + case TSESTree.AST_NODE_TYPES.Literal: + switch (typeof typeNode.literal.value) { + case 'bigint': + return `${typeNode.literal.value < 0 ? '-' : ''}${ + typeNode.literal.value + }n`; + case 'string': + return JSON.stringify(typeNode.literal.value); + default: + return `${typeNode.literal.value}`; + } + case TSESTree.AST_NODE_TYPES.TemplateLiteral: + return 'template literal type'; + } + } + + return 'literal type'; +} + +function isNodeInsideReturnType(node: TSESTree.TSUnionType): boolean { + return !!( + node.parent?.type === AST_NODE_TYPES.TSTypeAnnotation && + node.parent.parent && + (util.isFunctionType(node.parent.parent) || + util.isFunction(node.parent.parent)) + ); +} + +/** + * @remarks TypeScript stores boolean types as the union false | true, always. + */ +function unionTypePartsUnlessBoolean(type: ts.Type): ts.Type[] { + return type.isUnion() && + type.types.length === 2 && + tsutils.isBooleanLiteralType(type.types[0], false) && + tsutils.isBooleanLiteralType(type.types[1], true) + ? [type] + : tsutils.unionTypeParts(type); +} + +export default util.createRule({ + name: 'no-redundant-type-constituents', + meta: { + docs: { + description: + 'Disallow members of unions and intersections that do nothing or override type information', + recommended: false, + requiresTypeChecking: true, + }, + messages: { + literalOverridden: `{{literal}} is overridden by {{primitive}} in this union type.`, + primitiveOverridden: `{{primitive}} is overridden by the {{literal}} in this intersection type.`, + overridden: `'{{typeName}}' is overridden by other types in this {{container}} type.`, + overrides: `'{{typeName}}' overrides all other types in this {{container}} type.`, + }, + schema: [], + type: 'suggestion', + }, + defaultOptions: [], + create(context) { + const parserServices = util.getParserServices(context); + const typesCache = new Map(); + + function getTypeNodeTypePartFlags( + typeNode: TSESTree.TypeNode, + ): TypeFlagsWithName[] { + const keywordTypeFlags = keywordNodeTypesToTsTypes.get(typeNode.type); + if (keywordTypeFlags) { + return [ + { + typeFlags: keywordTypeFlags, + typeName: describeLiteralTypeNode(typeNode), + }, + ]; + } + + if ( + typeNode.type === AST_NODE_TYPES.TSLiteralType && + typeNode.literal.type === AST_NODE_TYPES.Literal + ) { + return [ + { + typeFlags: + primitiveTypeFlagTypes[ + typeof typeNode.literal + .value as keyof typeof primitiveTypeFlagTypes + ], + typeName: describeLiteralTypeNode(typeNode), + }, + ]; + } + + if (typeNode.type === AST_NODE_TYPES.TSUnionType) { + return typeNode.types.flatMap(getTypeNodeTypePartFlags); + } + + const tsNode = parserServices.esTreeNodeToTSNodeMap.get(typeNode); + const checker = parserServices.program.getTypeChecker(); + const nodeType = checker.getTypeAtLocation(tsNode); + const typeParts = unionTypePartsUnlessBoolean(nodeType); + + return typeParts.map(typePart => ({ + typeFlags: typePart.flags, + typeName: describeLiteralType(typePart), + })); + } + + function getTypeNodeTypePartFlagsCached( + typeNode: TSESTree.TypeNode, + ): TypeFlagsWithName[] { + const existing = typesCache.get(typeNode); + if (existing) { + return existing; + } + + const created = getTypeNodeTypePartFlags(typeNode); + typesCache.set(typeNode, created); + return created; + } + + return { + 'TSIntersectionType:exit'(node: TSESTree.TSIntersectionType): void { + const seenLiteralTypes = new Map(); + const seenPrimitiveTypes = new Map< + PrimitiveTypeFlag, + TSESTree.TypeNode[] + >(); + + function checkIntersectionBottomAndTopTypes( + { typeFlags, typeName }: TypeFlagsWithName, + typeNode: TSESTree.TypeNode, + ): boolean { + for (const [messageId, checkFlag] of [ + ['overrides', ts.TypeFlags.Any], + ['overrides', ts.TypeFlags.Never], + ['overridden', ts.TypeFlags.Unknown], + ] as const) { + if (typeFlags === checkFlag) { + context.report({ + data: { + container: 'intersection', + typeName, + }, + messageId, + node: typeNode, + }); + return true; + } + } + + return false; + } + + for (const typeNode of node.types) { + const typePartFlags = getTypeNodeTypePartFlagsCached(typeNode); + + for (const typePart of typePartFlags) { + if (checkIntersectionBottomAndTopTypes(typePart, typeNode)) { + continue; + } + + for (const literalTypeFlag of literalTypeFlags) { + if (typePart.typeFlags === literalTypeFlag) { + addToMapGroup( + seenLiteralTypes, + literalToPrimitiveTypeFlags[literalTypeFlag], + typePart.typeName, + ); + break; + } + } + + for (const primitiveTypeFlag of primitiveTypeFlags) { + if (typePart.typeFlags === primitiveTypeFlag) { + addToMapGroup(seenPrimitiveTypes, primitiveTypeFlag, typeNode); + } + } + } + } + + // For each primitive type of all the seen primitive types, + // if there was a literal type seen that overrides it, + // report each of the primitive type's type nodes + for (const [primitiveTypeFlag, typeNodes] of seenPrimitiveTypes) { + const matchedLiteralTypes = seenLiteralTypes.get(primitiveTypeFlag); + if (matchedLiteralTypes) { + for (const typeNode of typeNodes) { + context.report({ + data: { + literal: matchedLiteralTypes.join(' | '), + primitive: primitiveTypeFlagNames[primitiveTypeFlag], + }, + messageId: 'primitiveOverridden', + node: typeNode, + }); + } + } + } + }, + 'TSUnionType:exit'(node: TSESTree.TSUnionType): void { + const seenLiteralTypes = new Map< + PrimitiveTypeFlag, + TypeNodeWithValue[] + >(); + const seenPrimitiveTypes = new Set(); + + function checkUnionBottomAndTopTypes( + { typeFlags, typeName }: TypeFlagsWithName, + typeNode: TSESTree.TypeNode, + ): boolean { + for (const checkFlag of [ + ts.TypeFlags.Any, + ts.TypeFlags.Unknown, + ] as const) { + if (typeFlags === checkFlag) { + context.report({ + data: { + container: 'union', + typeName, + }, + messageId: 'overrides', + node: typeNode, + }); + return true; + } + } + + if ( + typeFlags === ts.TypeFlags.Never && + !isNodeInsideReturnType(node) + ) { + context.report({ + data: { + container: 'union', + typeName: 'never', + }, + messageId: 'overridden', + node: typeNode, + }); + return true; + } + + return false; + } + + for (const typeNode of node.types) { + const typePartFlags = getTypeNodeTypePartFlagsCached(typeNode); + + for (const typePart of typePartFlags) { + if (checkUnionBottomAndTopTypes(typePart, typeNode)) { + continue; + } + + for (const literalTypeFlag of literalTypeFlags) { + if (typePart.typeFlags === literalTypeFlag) { + addToMapGroup( + seenLiteralTypes, + literalToPrimitiveTypeFlags[literalTypeFlag], + { + literalValue: typePart.typeName, + typeNode, + }, + ); + break; + } + } + + for (const primitiveTypeFlag of primitiveTypeFlags) { + if ((typePart.typeFlags & primitiveTypeFlag) !== 0) { + seenPrimitiveTypes.add(primitiveTypeFlag); + } + } + } + } + + interface TypeFlagWithText { + literalValue: unknown; + primitiveTypeFlag: PrimitiveTypeFlag; + } + + const overriddenTypeNodes = new Map< + TSESTree.TypeNode, + TypeFlagWithText[] + >(); + + // For each primitive type of all the seen literal types, + // if there was a primitive type seen that overrides it, + // upsert the literal text and primitive type under the backing type node + for (const [primitiveTypeFlag, typeNodesWithText] of seenLiteralTypes) { + if (seenPrimitiveTypes.has(primitiveTypeFlag)) { + for (const { literalValue, typeNode } of typeNodesWithText) { + addToMapGroup(overriddenTypeNodes, typeNode, { + literalValue, + primitiveTypeFlag, + }); + } + } + } + + // For each type node that had at least one overridden literal, + // group those literals by their primitive type, + // then report each primitive type with all its literals + for (const [typeNode, typeFlagsWithText] of overriddenTypeNodes) { + const grouped = util.arrayGroupByToMap( + typeFlagsWithText, + pair => pair.primitiveTypeFlag, + ); + + for (const [primitiveTypeFlag, pairs] of grouped) { + context.report({ + data: { + literal: pairs.map(pair => pair.literalValue).join(' | '), + primitive: primitiveTypeFlagNames[primitiveTypeFlag], + }, + messageId: 'literalOverridden', + node: typeNode, + }); + } + } + }, + }; + }, +}); diff --git a/packages/eslint-plugin/src/util/misc.ts b/packages/eslint-plugin/src/util/misc.ts index eb62a029b154..8479feb728dd 100644 --- a/packages/eslint-plugin/src/util/misc.ts +++ b/packages/eslint-plugin/src/util/misc.ts @@ -19,6 +19,26 @@ function upperCaseFirst(str: string): string { return str[0].toUpperCase() + str.slice(1); } +function arrayGroupByToMap( + array: T[], + getKey: (item: T) => Key, +): Map { + const groups = new Map(); + + for (const item of array) { + const key = getKey(item); + const existing = groups.get(key); + + if (existing) { + existing.push(item); + } else { + groups.set(key, [item]); + } + } + + return groups; +} + /** Return true if both parameters are equal. */ type Equal = (a: T, b: T) => boolean; @@ -148,6 +168,7 @@ function formatWordList(words: string[]): string { } export { + arrayGroupByToMap, arraysAreEqual, Equal, ExcludeKeys, diff --git a/packages/eslint-plugin/tests/rules/no-redundant-type-constituents.test.ts b/packages/eslint-plugin/tests/rules/no-redundant-type-constituents.test.ts new file mode 100644 index 000000000000..4994459278f7 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-redundant-type-constituents.test.ts @@ -0,0 +1,787 @@ +import rule from '../../src/rules/no-redundant-type-constituents'; +import { RuleTester, getFixturesRootDir } from '../RuleTester'; + +const rootDir = getFixturesRootDir(); +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 2021, + tsconfigRootDir: rootDir, + project: './tsconfig.json', + }, + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('no-redundant-type-constituents', rule, { + valid: [ + ` + type T = any; + type U = T; + `, + ` + type T = never; + type U = T; + `, + ` + type T = 1 | 2; + type U = T | 3; + type V = U; + `, + 'type T = () => never;', + 'type T = () => never | string;', + ` + type B = never; + type T = () => B | string; + `, + ` + type B = string; + type T = () => B | never; + `, + 'type T = () => string | never;', + 'type T = { (): string | never };', + ` + function _(): string | never { + return ''; + } + `, + ` + const _ = (): string | never => { + return ''; + }; + `, + ` + type B = string; + type T = { (): B | never }; + `, + 'type T = { new (): string | never };', + ` + type B = never; + type T = { new (): string | B }; + `, + ` + type B = unknown; + type T = B; + `, + 'type T = bigint;', + ` + type B = bigint; + type T = B; + `, + 'type T = 1n | 2n;', + ` + type B = 1n; + type T = B | 2n; + `, + 'type T = boolean;', + ` + type B = boolean; + type T = B; + `, + 'type T = false | true;', + ` + type B = false; + type T = B | true; + `, + ` + type B = true; + type T = B | false; + `, + 'type T = number;', + ` + type B = number; + type T = B; + `, + 'type T = 1 | 2;', + ` + type B = 1; + type T = B | 2; + `, + 'type T = 1 | false;', + ` + type B = 1; + type T = B | false; + `, + 'type T = string;', + ` + type B = string; + type T = B; + `, + "type T = 'a' | 'b';", + ` + type B = 'b'; + type T = 'a' | B; + `, + ` + type B = 'a'; + type T = B | 'b'; + `, + 'type T = bigint | null;', + ` + type B = bigint; + type T = B | null; + `, + 'type T = boolean | null;', + ` + type B = boolean; + type T = B | null; + `, + 'type T = number | null;', + ` + type B = number; + type T = B | null; + `, + 'type T = string | null;', + ` + type B = string; + type T = B | null; + `, + 'type T = bigint & null;', + ` + type B = bigint; + type T = B & null; + `, + 'type T = boolean & null;', + ` + type B = boolean; + type T = B & null; + `, + 'type T = number & null;', + ` + type B = number; + type T = B & null; + `, + 'type T = string & null;', + ` + type B = string; + type T = B & null; + `, + 'type T = `${string}` & null;', + ` + type B = \`\${string}\`; + type T = B & null; + `, + ], + + invalid: [ + { + code: 'type T = number | any;', + errors: [ + { + column: 19, + data: { + container: 'union', + typeName: 'any', + }, + messageId: 'overrides', + }, + ], + }, + { + code: ` + type B = number; + type T = B | any; + `, + errors: [ + { + column: 22, + data: { + container: 'union', + typeName: 'any', + }, + messageId: 'overrides', + }, + ], + }, + { + code: 'type T = any | number;', + errors: [ + { + column: 10, + data: { + container: 'union', + typeName: 'any', + }, + messageId: 'overrides', + }, + ], + }, + { + code: ` + type B = any; + type T = B | number; + `, + errors: [ + { + column: 18, + data: { + container: 'union', + typeName: 'any', + }, + messageId: 'overrides', + }, + ], + }, + { + code: 'type T = number | never;', + errors: [ + { + column: 19, + data: { + container: 'union', + typeName: 'never', + }, + messageId: 'overridden', + }, + ], + }, + { + code: ` + type B = number; + type T = B | never; + `, + errors: [ + { + column: 22, + data: { + container: 'union', + typeName: 'never', + }, + messageId: 'overridden', + }, + ], + }, + { + code: ` + type B = never; + type T = B | number; + `, + errors: [ + { + column: 18, + data: { + container: 'union', + typeName: 'never', + }, + messageId: 'overridden', + }, + ], + }, + { + code: 'type T = never | number;', + errors: [ + { + column: 10, + data: { + container: 'union', + typeName: 'never', + }, + messageId: 'overridden', + }, + ], + }, + { + code: 'type T = number | unknown;', + errors: [ + { + column: 19, + data: { + container: 'union', + typeName: 'unknown', + }, + messageId: 'overrides', + }, + ], + }, + { + code: 'type T = unknown | number;', + errors: [ + { + column: 10, + data: { + container: 'union', + typeName: 'unknown', + }, + messageId: 'overrides', + }, + ], + }, + { + code: 'type T = number | 0;', + errors: [ + { + column: 19, + data: { + literal: '0', + primitive: 'number', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: 'type T = number | (0 | 1);', + errors: [ + { + column: 20, + data: { + literal: '0 | 1', + primitive: 'number', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: 'type T = (0 | 0) | number;', + errors: [ + { + column: 11, + data: { + literal: '0 | 0', + primitive: 'number', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: ` + type B = 0 | 1; + type T = (2 | B) | number; + `, + errors: [ + { + column: 19, + data: { + literal: '2 | 0 | 1', + primitive: 'number', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: 'type T = (0 | (1 | 2)) | number;', + errors: [ + { + column: 11, + data: { + literal: '0 | 1 | 2', + primitive: 'number', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: 'type T = (0 | 1) | number;', + errors: [ + { + column: 11, + data: { + literal: '0 | 1', + primitive: 'number', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: 'type T = (0 | (0 | 1)) | number;', + errors: [ + { + column: 11, + data: { + literal: '0 | 0 | 1', + primitive: 'number', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: "type T = (2 | 'other' | 3) | number;", + errors: [ + { + column: 11, + data: { + literal: '2 | 3', + primitive: 'number', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: "type T = '' | string;", + errors: [ + { + column: 10, + data: { + literal: '""', + primitive: 'string', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: ` + type B = 'b'; + type T = B | string; + `, + errors: [ + { + column: 18, + data: { + literal: '"b"', + primitive: 'string', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: 'type T = `a${number}c` | string;', + errors: [ + { + column: 10, + data: { + literal: 'template literal type', + primitive: 'string', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: ` + type B = \`a\${number}c\`; + type T = B | string; + `, + errors: [ + { + column: 18, + data: { + literal: 'template literal type', + primitive: 'string', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: 'type T = `${number}` | string;', + errors: [ + { + column: 10, + data: { + literal: 'template literal type', + primitive: 'string', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: 'type T = 0n | bigint;', + errors: [ + { + column: 10, + data: { + literal: '0n', + primitive: 'bigint', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: 'type T = -1n | bigint;', + errors: [ + { + column: 10, + data: { + literal: '-1n', + primitive: 'bigint', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: 'type T = (-1n | 1n) | bigint;', + errors: [ + { + column: 11, + data: { + literal: '-1n | 1n', + primitive: 'bigint', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: ` + type B = boolean; + type T = B | false; + `, + errors: [ + { + column: 22, + data: { + literal: 'false', + primitive: 'boolean', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: 'type T = false | boolean;', + errors: [ + { + column: 10, + data: { + literal: 'false', + primitive: 'boolean', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: 'type T = true | boolean;', + errors: [ + { + column: 10, + data: { + literal: 'true', + primitive: 'boolean', + }, + messageId: 'literalOverridden', + }, + ], + }, + { + code: 'type T = false & boolean;', + errors: [ + { + column: 18, + data: { + literal: 'false', + primitive: 'boolean', + }, + messageId: 'primitiveOverridden', + }, + ], + }, + { + code: ` + type B = false; + type T = B & boolean; + `, + errors: [ + { + column: 22, + data: { + literal: 'false', + primitive: 'boolean', + }, + messageId: 'primitiveOverridden', + }, + ], + }, + { + code: ` + type B = true; + type T = B & boolean; + `, + errors: [ + { + column: 22, + data: { + literal: 'true', + primitive: 'boolean', + }, + messageId: 'primitiveOverridden', + }, + ], + }, + { + code: 'type T = true & boolean;', + errors: [ + { + column: 17, + data: { + literal: 'true', + primitive: 'boolean', + }, + messageId: 'primitiveOverridden', + }, + ], + }, + { + code: 'type T = number & any;', + errors: [ + { + column: 19, + data: { + container: 'intersection', + typeName: 'any', + }, + messageId: 'overrides', + }, + ], + }, + { + code: 'type T = any & number;', + errors: [ + { + column: 10, + data: { + container: 'intersection', + typeName: 'any', + }, + messageId: 'overrides', + }, + ], + }, + { + code: 'type T = number & never;', + errors: [ + { + column: 19, + data: { + container: 'intersection', + typeName: 'never', + }, + messageId: 'overrides', + }, + ], + }, + { + code: ` + type B = never; + type T = B & number; + `, + errors: [ + { + column: 18, + data: { + container: 'intersection', + typeName: 'never', + }, + messageId: 'overrides', + }, + ], + }, + { + code: 'type T = never & number;', + errors: [ + { + column: 10, + data: { + container: 'intersection', + typeName: 'never', + }, + messageId: 'overrides', + }, + ], + }, + { + code: 'type T = number & unknown;', + errors: [ + { + column: 19, + data: { + container: 'intersection', + typeName: 'unknown', + }, + messageId: 'overridden', + }, + ], + }, + { + code: 'type T = unknown & number;', + errors: [ + { + column: 10, + data: { + container: 'intersection', + typeName: 'unknown', + }, + messageId: 'overridden', + }, + ], + }, + { + code: 'type T = number & 0;', + errors: [ + { + column: 10, + data: { + literal: '0', + primitive: 'number', + }, + messageId: 'primitiveOverridden', + }, + ], + }, + { + code: "type T = '' & string;", + errors: [ + { + column: 15, + data: { + literal: '""', + primitive: 'string', + }, + messageId: 'primitiveOverridden', + }, + ], + }, + { + code: ` + type B = 0n; + type T = B & bigint; + `, + errors: [ + { + column: 22, + data: { + literal: '0n', + primitive: 'bigint', + }, + messageId: 'primitiveOverridden', + }, + ], + }, + { + code: 'type T = 0n & bigint;', + errors: [ + { + column: 15, + data: { + literal: '0n', + primitive: 'bigint', + }, + messageId: 'primitiveOverridden', + }, + ], + }, + { + code: 'type T = -1n & bigint;', + errors: [ + { + column: 16, + data: { + literal: '-1n', + primitive: 'bigint', + }, + messageId: 'primitiveOverridden', + }, + ], + }, + ], +}); diff --git a/packages/type-utils/src/predicates.ts b/packages/type-utils/src/predicates.ts index c3ece8aa3c7a..541d236bd7c9 100644 --- a/packages/type-utils/src/predicates.ts +++ b/packages/type-utils/src/predicates.ts @@ -47,6 +47,13 @@ export function isTypeArrayTypeOrUnionOfArrayTypes( return true; } +/** + * @returns true if the type is `never` + */ +export function isTypeNeverType(type: ts.Type): boolean { + return isTypeFlagSet(type, ts.TypeFlags.Never); +} + /** * @returns true if the type is `unknown` */ @@ -168,3 +175,15 @@ export function typeIsOrHasBaseType( return false; } + +export function isTypeBigIntLiteralType( + type: ts.Type, +): type is ts.BigIntLiteralType { + return isTypeFlagSet(type, ts.TypeFlags.BigIntLiteral); +} + +export function isTypeTemplateLiteralType( + type: ts.Type, +): type is ts.TemplateLiteralType { + return isTypeFlagSet(type, ts.TypeFlags.TemplateLiteral); +} From 823b945c8f9e83d0246a2a5d07519f01e1a64518 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 23 Feb 2022 18:40:45 -0500 Subject: [PATCH 05/12] feat(eslint-plugin): add `no-useless-empty-export` rule (#4380) --- packages/eslint-plugin/README.md | 1 + .../docs/rules/no-useless-empty-export.md | 45 +++++++ packages/eslint-plugin/src/configs/all.ts | 1 + packages/eslint-plugin/src/rules/index.ts | 2 + .../src/rules/no-useless-empty-export.ts | 79 +++++++++++ .../rules/no-useless-empty-export.test.ts | 125 ++++++++++++++++++ 6 files changed, 253 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/no-useless-empty-export.md create mode 100644 packages/eslint-plugin/src/rules/no-useless-empty-export.ts create mode 100644 packages/eslint-plugin/tests/rules/no-useless-empty-export.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index d10d9f079907..ec14afa8c908 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -151,6 +151,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int | [`@typescript-eslint/no-unsafe-call`](./docs/rules/no-unsafe-call.md) | Disallows calling an any type value | :white_check_mark: | | :thought_balloon: | | [`@typescript-eslint/no-unsafe-member-access`](./docs/rules/no-unsafe-member-access.md) | Disallows member access on any typed variables | :white_check_mark: | | :thought_balloon: | | [`@typescript-eslint/no-unsafe-return`](./docs/rules/no-unsafe-return.md) | Disallows returning any from a function | :white_check_mark: | | :thought_balloon: | +| [`@typescript-eslint/no-useless-empty-export`](./docs/rules/no-useless-empty-export.md) | Disallow empty exports that don't change anything in a module file | | :wrench: | | | [`@typescript-eslint/no-var-requires`](./docs/rules/no-var-requires.md) | Disallows the use of require statements except in import statements | :white_check_mark: | | | | [`@typescript-eslint/non-nullable-type-assertion-style`](./docs/rules/non-nullable-type-assertion-style.md) | Prefers a non-null assertion over explicit type cast when possible | | :wrench: | :thought_balloon: | | [`@typescript-eslint/prefer-as-const`](./docs/rules/prefer-as-const.md) | Prefer usage of `as const` over literal type | :white_check_mark: | :wrench: | | diff --git a/packages/eslint-plugin/docs/rules/no-useless-empty-export.md b/packages/eslint-plugin/docs/rules/no-useless-empty-export.md new file mode 100644 index 000000000000..0cb24763f125 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-useless-empty-export.md @@ -0,0 +1,45 @@ +# `no-useless-empty-export` + +Disallow empty exports that don't change anything in a module file. + +## Rule Details + +An empty `export {}` statement is sometimes useful in TypeScript code to turn a file that would otherwise be a script file into a module file. +Per the TypeScript Handbook [Modules](https://www.typescriptlang.org/docs/handbook/modules.html) page: + +> In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. +> Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well). + +However, an `export {}` statement does nothing if there are any other top-level import or export statements in a file. + +Examples of code for this rule: + + + +### ❌ Incorrect + +```ts +export const value = 'Hello, world!'; +export {}; +``` + +```ts +import 'some-other-module'; +export {}; +``` + +### ✅ Correct + +```ts +export const value = 'Hello, world!'; +``` + +```ts +import 'some-other-module'; +``` + +## Attributes + +- [ ] ✅ Recommended +- [x] 🔧 Fixable +- [ ] 💭 Requires type information diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts index c052ce4eb37d..8b63b936b4a8 100644 --- a/packages/eslint-plugin/src/configs/all.ts +++ b/packages/eslint-plugin/src/configs/all.ts @@ -115,6 +115,7 @@ export = { '@typescript-eslint/no-unused-vars': 'error', 'no-use-before-define': 'off', '@typescript-eslint/no-use-before-define': 'error', + '@typescript-eslint/no-useless-empty-export': 'error', 'no-useless-constructor': 'off', '@typescript-eslint/no-useless-constructor': 'error', '@typescript-eslint/no-var-requires': 'error', diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 96251c3c22f0..3249559746ae 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -82,6 +82,7 @@ import noUnusedExpressions from './no-unused-expressions'; import noUnusedVars from './no-unused-vars'; import noUseBeforeDefine from './no-use-before-define'; import noUselessConstructor from './no-useless-constructor'; +import noUselessEmptyExport from './no-useless-empty-export'; import noVarRequires from './no-var-requires'; import nonNullableTypeAssertionStyle from './non-nullable-type-assertion-style'; import objectCurlySpacing from './object-curly-spacing'; @@ -206,6 +207,7 @@ export default { 'no-unused-vars': noUnusedVars, 'no-use-before-define': noUseBeforeDefine, 'no-useless-constructor': noUselessConstructor, + 'no-useless-empty-export': noUselessEmptyExport, 'no-var-requires': noVarRequires, 'non-nullable-type-assertion-style': nonNullableTypeAssertionStyle, 'object-curly-spacing': objectCurlySpacing, diff --git a/packages/eslint-plugin/src/rules/no-useless-empty-export.ts b/packages/eslint-plugin/src/rules/no-useless-empty-export.ts new file mode 100644 index 000000000000..c06c47b8f8e3 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-useless-empty-export.ts @@ -0,0 +1,79 @@ +import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils'; +import * as util from '../util'; + +function isEmptyExport( + node: TSESTree.Node, +): node is TSESTree.ExportNamedDeclaration { + return ( + node.type === AST_NODE_TYPES.ExportNamedDeclaration && + node.specifiers.length === 0 && + !node.declaration + ); +} + +const exportOrImportNodeTypes = new Set([ + AST_NODE_TYPES.ExportAllDeclaration, + AST_NODE_TYPES.ExportDefaultDeclaration, + AST_NODE_TYPES.ExportNamedDeclaration, + AST_NODE_TYPES.ExportSpecifier, + AST_NODE_TYPES.ImportDeclaration, + AST_NODE_TYPES.TSExportAssignment, + AST_NODE_TYPES.TSImportEqualsDeclaration, +]); + +export default util.createRule({ + name: 'no-useless-empty-export', + meta: { + docs: { + description: + "Disallow empty exports that don't change anything in a module file", + recommended: false, + suggestion: true, + }, + fixable: 'code', + hasSuggestions: true, + messages: { + uselessExport: 'Empty export does nothing and can be removed.', + }, + schema: [], + type: 'suggestion', + }, + defaultOptions: [], + create(context) { + function checkNode( + node: TSESTree.Program | TSESTree.TSModuleDeclaration, + ): void { + if (!Array.isArray(node.body)) { + return; + } + + let emptyExport: TSESTree.ExportNamedDeclaration | undefined; + let foundOtherExport = false; + + for (const statement of node.body) { + if (isEmptyExport(statement)) { + emptyExport = statement; + + if (foundOtherExport) { + break; + } + } else if (exportOrImportNodeTypes.has(statement.type)) { + foundOtherExport = true; + } + } + + if (emptyExport && foundOtherExport) { + context.report({ + fix: fixer => fixer.remove(emptyExport!), + messageId: 'uselessExport', + node: emptyExport, + }); + } + } + + return { + Program: checkNode, + TSModuleDeclaration: checkNode, + }; + }, +}); diff --git a/packages/eslint-plugin/tests/rules/no-useless-empty-export.test.ts b/packages/eslint-plugin/tests/rules/no-useless-empty-export.test.ts new file mode 100644 index 000000000000..ea13395ec9ed --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-useless-empty-export.test.ts @@ -0,0 +1,125 @@ +/* eslint-disable eslint-comments/no-use */ +// this rule tests the spacing, which prettier will want to fix and break the tests +/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ +/* eslint-enable eslint-comments/no-use */ +import rule from '../../src/rules/no-useless-empty-export'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module', + }, + parser: '@typescript-eslint/parser', +}); + +const error = { + messageId: 'uselessExport', +} as const; + +ruleTester.run('no-useless-empty-export', rule, { + valid: [ + "declare module '_'", + "import {} from '_';", + "import * as _ from '_';", + 'export = {};', + 'export = 3;', + 'export const _ = {};', + ` + const _ = {}; + export default _; + `, + ` + export * from '_'; + export = {}; + `, + ` + export {}; + `, + ], + invalid: [ + { + code: ` +export const _ = {}; +export {}; + `, + errors: [error], + output: ` +export const _ = {}; + + `, + }, + { + code: ` +export * from '_'; +export {}; + `, + errors: [error], + output: ` +export * from '_'; + + `, + }, + { + code: ` +export {}; +export * from '_'; + `, + errors: [error], + output: ` + +export * from '_'; + `, + }, + { + code: ` +const _ = {}; +export default _; +export {}; + `, + errors: [error], + output: ` +const _ = {}; +export default _; + + `, + }, + { + code: ` +export {}; +const _ = {}; +export default _; + `, + errors: [error], + output: ` + +const _ = {}; +export default _; + `, + }, + { + code: ` +const _ = {}; +export { _ }; +export {}; + `, + errors: [error], + output: ` +const _ = {}; +export { _ }; + + `, + }, + { + code: ` +import _ = require('_'); +export {}; + `, + errors: [error], + output: ` +import _ = require('_'); + + `, + }, + ], +}); From 66501d6dd7e97c22c671efaa6d1ba8237907e417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20De=20Boey?= Date: Thu, 24 Feb 2022 06:35:18 +0100 Subject: [PATCH 06/12] feat(utils): extract `isNotTokenOfTypeWithConditions` out of `ast-utils`' `predicates` (#4502) --- packages/utils/src/ast-utils/helpers.ts | 21 +++++++++++++++++++++ packages/utils/src/ast-utils/predicates.ts | 22 +++++++++------------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/packages/utils/src/ast-utils/helpers.ts b/packages/utils/src/ast-utils/helpers.ts index f2a4ac2e2442..baf4daf68101 100644 --- a/packages/utils/src/ast-utils/helpers.ts +++ b/packages/utils/src/ast-utils/helpers.ts @@ -56,3 +56,24 @@ export const isTokenOfTypeWithConditions = < token?.type === tokenType && entries.every(([key, value]) => token[key] === value); }; + +export const isNotTokenOfTypeWithConditions = + < + TokenType extends AST_TOKEN_TYPES, + Conditions extends Partial, + >( + tokenType: TokenType, + conditions: Conditions, + ): (( + token: TSESTree.Token | null | undefined, + ) => token is Exclude< + TSESTree.Token, + TSESTree.Token & { type: TokenType } & Conditions + >) => + ( + token, + ): token is Exclude< + TSESTree.Token, + TSESTree.Token & { type: TokenType } & Conditions + > => + !isTokenOfTypeWithConditions(tokenType, conditions)(token); diff --git a/packages/utils/src/ast-utils/predicates.ts b/packages/utils/src/ast-utils/predicates.ts index 0038f092bbe0..10e50522d672 100644 --- a/packages/utils/src/ast-utils/predicates.ts +++ b/packages/utils/src/ast-utils/predicates.ts @@ -4,6 +4,7 @@ import { isNodeOfType, isNodeOfTypes, isNodeOfTypeWithConditions, + isNotTokenOfTypeWithConditions, isTokenOfTypeWithConditions, } from './helpers'; @@ -12,25 +13,20 @@ const isOptionalChainPunctuator = isTokenOfTypeWithConditions( { value: '?.' }, ); -function isNotOptionalChainPunctuator( - token: TSESTree.Token, -): token is Exclude< - TSESTree.Token, - TSESTree.PunctuatorToken & { value: '?.' } -> { - return !isOptionalChainPunctuator(token); -} +const isNotOptionalChainPunctuator = isNotTokenOfTypeWithConditions( + AST_TOKEN_TYPES.Punctuator, + { value: '?.' }, +); const isNonNullAssertionPunctuator = isTokenOfTypeWithConditions( AST_TOKEN_TYPES.Punctuator, { value: '!' }, ); -function isNotNonNullAssertionPunctuator( - token: TSESTree.Token, -): token is Exclude { - return !isNonNullAssertionPunctuator(token); -} +const isNotNonNullAssertionPunctuator = isNotTokenOfTypeWithConditions( + AST_TOKEN_TYPES.Punctuator, + { value: '!' }, +); /** * Returns true if and only if the node represents: foo?.() or foo.bar?.() From 208b6d02252dff2bf272329d3e4a4a82e56c52c0 Mon Sep 17 00:00:00 2001 From: Domas Trijonis Date: Thu, 24 Feb 2022 16:41:53 +0100 Subject: [PATCH 07/12] feat(eslint-plugin): add extension rule `space-before-blocks` (#1606) (#4184) Co-authored-by: Josh Goldberg --- packages/eslint-plugin/README.md | 1 + .../docs/rules/space-before-blocks.md | 60 ++++ packages/eslint-plugin/src/configs/all.ts | 2 + packages/eslint-plugin/src/rules/index.ts | 2 + .../src/rules/space-before-blocks.ts | 90 ++++++ .../src/util/getESLintCoreRule.ts | 1 + .../tests/rules/space-before-blocks.test.ts | 258 ++++++++++++++++++ .../eslint-plugin/typings/eslint-rules.d.ts | 23 ++ packages/utils/src/ast-utils/misc.ts | 4 +- 9 files changed, 439 insertions(+), 2 deletions(-) create mode 100644 packages/eslint-plugin/docs/rules/space-before-blocks.md create mode 100644 packages/eslint-plugin/src/rules/space-before-blocks.ts create mode 100644 packages/eslint-plugin/tests/rules/space-before-blocks.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index ec14afa8c908..34209e621cbd 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -231,6 +231,7 @@ In these cases, we create what we call an extension rule; a rule within our plug | [`@typescript-eslint/require-await`](./docs/rules/require-await.md) | Disallow async functions which have no `await` expression | :white_check_mark: | | :thought_balloon: | | [`@typescript-eslint/return-await`](./docs/rules/return-await.md) | Enforces consistent returning of awaited values | | :wrench: | :thought_balloon: | | [`@typescript-eslint/semi`](./docs/rules/semi.md) | Require or disallow semicolons instead of ASI | | :wrench: | | +| [`@typescript-eslint/space-before-blocks`](./docs/rules/space-before-blocks.md) | Enforces consistent spacing before blocks | | :wrench: | | | [`@typescript-eslint/space-before-function-paren`](./docs/rules/space-before-function-paren.md) | Enforces consistent spacing before function parenthesis | | :wrench: | | | [`@typescript-eslint/space-infix-ops`](./docs/rules/space-infix-ops.md) | This rule is aimed at ensuring there are spaces around infix operators. | | :wrench: | | diff --git a/packages/eslint-plugin/docs/rules/space-before-blocks.md b/packages/eslint-plugin/docs/rules/space-before-blocks.md new file mode 100644 index 000000000000..52f8a121df69 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/space-before-blocks.md @@ -0,0 +1,60 @@ +# `space-before-blocks` + +Enforces consistent spacing before blocks. + +## Rule Details + +This rule extends the base [`eslint/space-before-blocks`](https://eslint.org/docs/rules/space-before-blocks) rule. +It adds support for interfaces and enums: + +### ❌ Incorrect + +```ts +enum Breakpoint{ + Large, Medium; +} + +interface State{ + currentBreakpoint: Breakpoint; +} +``` + +### ✅ Correct + +```ts +enum Breakpoint { + Large, Medium; +} + +interface State { + currentBreakpoint: Breakpoint; +} +``` + +In case a more specific options object is passed these blocks will follow `classes` configuration option. + +## How to Use + +```jsonc +{ + // note you must disable the base rule as it can report incorrect errors + "space-before-blocks": "off", + "@typescript-eslint/space-before-blocks": ["error"] +} +``` + +## Options + +See [`eslint/space-before-blocks` options](https://eslint.org/docs/rules/space-before-blocks#options). + + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/space-before-blocks.md) + + + +## Attributes + +- [ ] ✅ Recommended +- [x] 🔧 Fixable +- [ ] 💭 Requires type information diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts index 8b63b936b4a8..03188b724648 100644 --- a/packages/eslint-plugin/src/configs/all.ts +++ b/packages/eslint-plugin/src/configs/all.ts @@ -157,6 +157,8 @@ export = { '@typescript-eslint/space-before-function-paren': 'error', 'space-infix-ops': 'off', '@typescript-eslint/space-infix-ops': 'error', + 'space-before-blocks': 'off', + '@typescript-eslint/space-before-blocks': 'error', '@typescript-eslint/strict-boolean-expressions': 'error', '@typescript-eslint/switch-exhaustiveness-check': 'error', '@typescript-eslint/triple-slash-reference': 'error', diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 3249559746ae..c2f90319e386 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -112,6 +112,7 @@ import restrictTemplateExpressions from './restrict-template-expressions'; import returnAwait from './return-await'; import semi from './semi'; import sortTypeUnionIntersectionMembers from './sort-type-union-intersection-members'; +import spaceBeforeBlocks from './space-before-blocks'; import spaceBeforeFunctionParen from './space-before-function-paren'; import spaceInfixOps from './space-infix-ops'; import strictBooleanExpressions from './strict-boolean-expressions'; @@ -237,6 +238,7 @@ export default { 'return-await': returnAwait, semi: semi, 'sort-type-union-intersection-members': sortTypeUnionIntersectionMembers, + 'space-before-blocks': spaceBeforeBlocks, 'space-before-function-paren': spaceBeforeFunctionParen, 'space-infix-ops': spaceInfixOps, 'strict-boolean-expressions': strictBooleanExpressions, diff --git a/packages/eslint-plugin/src/rules/space-before-blocks.ts b/packages/eslint-plugin/src/rules/space-before-blocks.ts new file mode 100644 index 000000000000..5bf827df96c3 --- /dev/null +++ b/packages/eslint-plugin/src/rules/space-before-blocks.ts @@ -0,0 +1,90 @@ +import { TSESTree } from '@typescript-eslint/utils'; +import { getESLintCoreRule } from '../util/getESLintCoreRule'; +import * as util from '../util'; + +const baseRule = getESLintCoreRule('space-before-blocks'); + +export type Options = util.InferOptionsTypeFromRule; +export type MessageIds = util.InferMessageIdsTypeFromRule; + +export default util.createRule({ + name: 'space-before-blocks', + meta: { + type: 'layout', + docs: { + description: 'Enforces consistent spacing before blocks', + recommended: false, + extendsBaseRule: true, + }, + fixable: baseRule.meta.fixable, + hasSuggestions: baseRule.meta.hasSuggestions, + schema: baseRule.meta.schema, + messages: { + // @ts-expect-error -- we report on this messageId so we need to ensure it's there in case ESLint changes in future + unexpectedSpace: 'Unexpected space before opening brace.', + // @ts-expect-error -- we report on this messageId so we need to ensure it's there in case ESLint changes in future + missingSpace: 'Missing space before opening brace.', + ...baseRule.meta.messages, + }, + }, + defaultOptions: ['always'], + create(context) { + const rules = baseRule.create(context); + const config = context.options[0]; + const sourceCode = context.getSourceCode(); + + let requireSpace = true; + + if (typeof config === 'object') { + requireSpace = config.classes === 'always'; + } else if (config === 'never') { + requireSpace = false; + } + + function checkPrecedingSpace( + node: TSESTree.Token | TSESTree.TSInterfaceBody, + ): void { + const precedingToken = sourceCode.getTokenBefore(node); + if (precedingToken && util.isTokenOnSameLine(precedingToken, node)) { + const hasSpace = sourceCode.isSpaceBetweenTokens( + precedingToken, + node as TSESTree.Token, + ); + + if (requireSpace && !hasSpace) { + context.report({ + node, + messageId: 'missingSpace', + fix(fixer) { + return fixer.insertTextBefore(node, ' '); + }, + }); + } else if (!requireSpace && hasSpace) { + context.report({ + node, + messageId: 'unexpectedSpace', + fix(fixer) { + return fixer.removeRange([ + precedingToken.range[1], + node.range[0], + ]); + }, + }); + } + } + } + + function checkSpaceAfterEnum(node: TSESTree.TSEnumDeclaration): void { + const punctuator = sourceCode.getTokenAfter(node.id); + if (punctuator) { + checkPrecedingSpace(punctuator); + } + } + + return { + ...rules, + TSEnumDeclaration: checkSpaceAfterEnum, + TSInterfaceBody: checkPrecedingSpace, + }; + }, +}); diff --git a/packages/eslint-plugin/src/util/getESLintCoreRule.ts b/packages/eslint-plugin/src/util/getESLintCoreRule.ts index f61bdb0c3094..5ba9ae369659 100644 --- a/packages/eslint-plugin/src/util/getESLintCoreRule.ts +++ b/packages/eslint-plugin/src/util/getESLintCoreRule.ts @@ -33,6 +33,7 @@ interface RuleMap { 'prefer-const': typeof import('eslint/lib/rules/prefer-const'); quotes: typeof import('eslint/lib/rules/quotes'); semi: typeof import('eslint/lib/rules/semi'); + 'space-before-blocks': typeof import('eslint/lib/rules/space-before-blocks'); 'space-infix-ops': typeof import('eslint/lib/rules/space-infix-ops'); strict: typeof import('eslint/lib/rules/strict'); } diff --git a/packages/eslint-plugin/tests/rules/space-before-blocks.test.ts b/packages/eslint-plugin/tests/rules/space-before-blocks.test.ts new file mode 100644 index 000000000000..7e1338c6fc5d --- /dev/null +++ b/packages/eslint-plugin/tests/rules/space-before-blocks.test.ts @@ -0,0 +1,258 @@ +/* eslint-disable eslint-comments/no-use */ +// this rule tests spacing, which prettier will want to fix and break the tests +/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ +/* eslint-enable eslint-comments/no-use */ + +import rule from '../../src/rules/space-before-blocks'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('space-before-blocks', rule, { + valid: [ + { + code: ` + enum Test{ + KEY1 = 2, + } + `, + options: ['never'], + }, + { + code: ` + interface Test{ + prop1: number; + } + `, + options: ['never'], + }, + { + code: ` + enum Test { + KEY1 = 2, + } + `, + options: ['always'], + }, + { + code: ` + interface Test { + prop1: number; + } + `, + options: ['always'], + }, + { + code: ` + enum Test{ + KEY1 = 2, + } + `, + options: [{ classes: 'never' }], + }, + { + code: ` + interface Test{ + prop1: number; + } + `, + options: [{ classes: 'never' }], + }, + { + code: ` + enum Test { + KEY1 = 2, + } + `, + options: [{ classes: 'always' }], + }, + { + code: ` + interface Test { + prop1: number; + } + `, + options: [{ classes: 'always' }], + }, + { + code: ` + interface Test{ + prop1: number; + } + `, + options: [{ classes: 'off' }], + }, + ], + invalid: [ + { + code: ` + enum Test{ + A = 2, + B = 1, + } + `, + output: ` + enum Test { + A = 2, + B = 1, + } + `, + errors: [ + { + messageId: 'missingSpace', + column: 18, + line: 2, + }, + ], + options: ['always'], + }, + { + code: ` + interface Test{ + prop1: number; + } + `, + output: ` + interface Test { + prop1: number; + } + `, + errors: [ + { + messageId: 'missingSpace', + column: 23, + line: 2, + }, + ], + options: ['always'], + }, + { + code: ` + enum Test{ + A = 2, + B = 1, + } + `, + output: ` + enum Test { + A = 2, + B = 1, + } + `, + errors: [ + { + messageId: 'missingSpace', + column: 18, + line: 2, + }, + ], + options: [{ classes: 'always' }], + }, + { + code: ` + interface Test{ + prop1: number; + } + `, + output: ` + interface Test { + prop1: number; + } + `, + errors: [ + { + messageId: 'missingSpace', + column: 23, + line: 2, + }, + ], + options: [{ classes: 'always' }], + }, + { + code: ` + enum Test { + A = 2, + B = 1, + } + `, + output: ` + enum Test{ + A = 2, + B = 1, + } + `, + errors: [ + { + messageId: 'unexpectedSpace', + column: 19, + line: 2, + }, + ], + options: ['never'], + }, + { + code: ` + interface Test { + prop1: number; + } + `, + output: ` + interface Test{ + prop1: number; + } + `, + errors: [ + { + messageId: 'unexpectedSpace', + column: 24, + line: 2, + }, + ], + options: ['never'], + }, + { + code: ` + enum Test { + A = 2, + B = 1, + } + `, + output: ` + enum Test{ + A = 2, + B = 1, + } + `, + errors: [ + { + messageId: 'unexpectedSpace', + column: 19, + line: 2, + }, + ], + options: [{ classes: 'never' }], + }, + { + code: ` + interface Test { + prop1: number; + } + `, + output: ` + interface Test{ + prop1: number; + } + `, + errors: [ + { + messageId: 'unexpectedSpace', + column: 24, + line: 2, + }, + ], + options: [{ classes: 'never' }], + }, + ], +}); diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts index d53ef43a747f..fa7d5934b263 100644 --- a/packages/eslint-plugin/typings/eslint-rules.d.ts +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -842,6 +842,29 @@ declare module 'eslint/lib/rules/space-infix-ops' { export = rule; } +declare module 'eslint/lib/rules/space-before-blocks' { + import { TSESLint, TSESTree } from '@typescript-eslint/utils'; + + const rule: TSESLint.RuleModule< + 'missingSpace' | 'unexpectedSpace', + [ + | 'always' + | 'never' + | { + classes?: 'always' | 'never' | 'off'; + functions?: 'always' | 'never' | 'off'; + keywords?: 'always' | 'never' | 'off'; + }, + ], + { + BlockStatement(node: TSESTree.BlockStatement): void; + ClassBody(node: TSESTree.ClassBody): void; + SwitchStatement(node: TSESTree.SwitchStatement): void; + } + >; + export = rule; +} + declare module 'eslint/lib/rules/prefer-const' { import { TSESLint, TSESTree } from '@typescript-eslint/utils'; diff --git a/packages/utils/src/ast-utils/misc.ts b/packages/utils/src/ast-utils/misc.ts index 923424d9f928..72a7898c31c0 100644 --- a/packages/utils/src/ast-utils/misc.ts +++ b/packages/utils/src/ast-utils/misc.ts @@ -6,8 +6,8 @@ const LINEBREAK_MATCHER = /\r\n|[\r\n\u2028\u2029]/; * Determines whether two adjacent tokens are on the same line */ function isTokenOnSameLine( - left: TSESTree.Token, - right: TSESTree.Token, + left: TSESTree.Node | TSESTree.Token, + right: TSESTree.Node | TSESTree.Token, ): boolean { return left.loc.end.line === right.loc.start.line; } From 6afcaea0160a1ccd1c6483ca677c544ca1b8cb4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Balogh?= <34451173+grabofus@users.noreply.github.com> Date: Thu, 24 Feb 2022 16:15:04 +0000 Subject: [PATCH 08/12] feat(eslint-plugin): added member group support to member-ordering rule (#4538) * feat(eslint-plugin): added member group support to member-ordering rule * test(eslint-plugin): added more test cases for member-ordering * test(eslint-plugin): added more test cases for member-ordering Co-authored-by: Josh Goldberg --- .../docs/rules/member-ordering.md | 23 +++ .../src/rules/member-ordering.ts | 49 ++++-- .../tests/rules/member-ordering.test.ts | 156 ++++++++++++++++++ 3 files changed, 215 insertions(+), 13 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/member-ordering.md b/packages/eslint-plugin/docs/rules/member-ordering.md index b8e1e9f711fe..ab54967aa3ab 100644 --- a/packages/eslint-plugin/docs/rules/member-ordering.md +++ b/packages/eslint-plugin/docs/rules/member-ordering.md @@ -292,6 +292,29 @@ The third grouping option is to ignore both scope and accessibility. ] ``` +### Grouping different member types at the same rank + +It is also possible to group different member types at the same rank. + +```jsonc +[ + // Index signature + "signature", + + // Fields + "field", + + // Constructors + "constructor", + + // Getters and Setters at the same rank + ["get", "set"], + + // Methods + "method" +] +``` + ### Default configuration The default configuration looks as follows: diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index ce2edabe058f..92c794caf07f 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -13,12 +13,14 @@ type Order = | 'alphabetically-case-insensitive' | 'as-written'; +type MemberType = string | string[]; + interface SortedOrderConfig { - memberTypes?: string[] | 'never'; + memberTypes?: MemberType[] | 'never'; order: Order; } -type OrderConfig = string[] | SortedOrderConfig | 'never'; +type OrderConfig = MemberType[] | SortedOrderConfig | 'never'; type Member = TSESTree.ClassElement | TSESTree.TypeElement; export type Options = [ @@ -36,14 +38,24 @@ const neverConfig: JSONSchema.JSONSchema4 = { enum: ['never'], }; -const arrayConfig = (memberTypes: string[]): JSONSchema.JSONSchema4 => ({ +const arrayConfig = (memberTypes: MemberType[]): JSONSchema.JSONSchema4 => ({ type: 'array', items: { - enum: memberTypes, + oneOf: [ + { + enum: memberTypes, + }, + { + type: 'array', + items: { + enum: memberTypes, + }, + }, + ], }, }); -const objectConfig = (memberTypes: string[]): JSONSchema.JSONSchema4 => ({ +const objectConfig = (memberTypes: MemberType[]): JSONSchema.JSONSchema4 => ({ type: 'object', properties: { memberTypes: { @@ -339,12 +351,20 @@ function getMemberName( * * @return Index of the matching member type in the order configuration. */ -function getRankOrder(memberGroups: string[], orderConfig: string[]): number { +function getRankOrder( + memberGroups: string[], + orderConfig: MemberType[], +): number { let rank = -1; const stack = memberGroups.slice(); // Get a copy of the member groups while (stack.length > 0 && rank === -1) { - rank = orderConfig.indexOf(stack.shift()!); + const memberGroup = stack.shift()!; + rank = orderConfig.findIndex(memberType => + Array.isArray(memberType) + ? memberType.includes(memberGroup) + : memberType === memberGroup, + ); } return rank; @@ -358,7 +378,7 @@ function getRankOrder(memberGroups: string[], orderConfig: string[]): number { */ function getRank( node: Member, - orderConfig: string[], + orderConfig: MemberType[], supportsModifiers: boolean, ): number { const type = getNodeType(node); @@ -414,7 +434,7 @@ function getRank( } /** - * Gets the lowest possible rank higher than target. + * Gets the lowest possible rank(s) higher than target. * e.g. given the following order: * ... * public-static-method @@ -427,15 +447,16 @@ function getRank( * and considering that a public-instance-method has already been declared, so ranks contains * public-instance-method, then the lowest possible rank for public-static-method is * public-instance-method. + * If a lowest possible rank is a member group, a comma separated list of ranks is returned. * @param ranks the existing ranks in the object. * @param target the target rank. * @param order the current order to be validated. - * @returns the name of the lowest possible rank without dashes (-). + * @returns the name(s) of the lowest possible rank without dashes (-). */ function getLowestRank( ranks: number[], target: number, - order: string[], + order: MemberType[], ): string { let lowest = ranks[ranks.length - 1]; @@ -445,7 +466,9 @@ function getLowestRank( } }); - return order[lowest].replace(/-/g, ' '); + const lowestRank = order[lowest]; + const lowestRanks = Array.isArray(lowestRank) ? lowestRank : [lowestRank]; + return lowestRanks.map(rank => rank.replace(/-/g, ' ')).join(', '); } export default util.createRule({ @@ -523,7 +546,7 @@ export default util.createRule({ */ function checkGroupSort( members: Member[], - groupOrder: string[], + groupOrder: MemberType[], supportsModifiers: boolean, ): Array | null { const previousRanks: number[] = []; diff --git a/packages/eslint-plugin/tests/rules/member-ordering.test.ts b/packages/eslint-plugin/tests/rules/member-ordering.test.ts index c9c1090c7348..c21272ca510a 100644 --- a/packages/eslint-plugin/tests/rules/member-ordering.test.ts +++ b/packages/eslint-plugin/tests/rules/member-ordering.test.ts @@ -1417,6 +1417,74 @@ class Foo { }, ], }, + { + code: ` +class Foo { + A: string; + constructor() {} + get B() {} + set B() {} + get C() {} + set C() {} + D(): void; +} `, + options: [ + { + default: ['field', 'constructor', ['get', 'set'], 'method'], + }, + ], + }, + { + code: ` +class Foo { + A: string; + constructor() {} + B(): void; +} `, + options: [ + { + default: ['field', 'constructor', [], 'method'], + }, + ], + }, + { + code: ` +class Foo { + A: string; + constructor() {} + @Dec() private B: string; + private C(): void; + set D() {} + E(): void; +} `, + options: [ + { + default: [ + 'public-field', + 'constructor', + ['private-decorated-field', 'public-set', 'private-method'], + 'public-method', + ], + }, + ], + }, + { + code: ` +class Foo { + A: string; + constructor() {} + get B() {} + get C() {} + set B() {} + set C() {} + D(): void; +} `, + options: [ + { + default: ['field', 'constructor', ['get'], ['set'], 'method'], + }, + ], + }, ], invalid: [ { @@ -3823,6 +3891,94 @@ class Foo { }, ], }, + { + code: ` +class Foo { + A: string; + get B() {} + constructor() {} + set B() {} + get C() {} + set C() {} + D(): void; +} `, + options: [ + { + default: ['field', 'constructor', ['get', 'set'], 'method'], + }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'constructor', + rank: 'get, set', + }, + line: 5, + column: 5, + }, + ], + }, + { + code: ` +class Foo { + A: string; + private C(): void; + constructor() {} + @Dec() private B: string; + set D() {} + E(): void; +} `, + options: [ + { + default: [ + 'public-field', + 'constructor', + ['private-decorated-field', 'public-set', 'private-method'], + 'public-method', + ], + }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'constructor', + rank: 'private decorated field, public set, private method', + }, + line: 5, + column: 5, + }, + ], + }, + { + code: ` +class Foo { + A: string; + constructor() {} + get B() {} + set B() {} + get C() {} + set C() {} + D(): void; +} `, + options: [ + { + default: ['field', 'constructor', 'get', ['set'], 'method'], + }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'C', + rank: 'set', + }, + line: 7, + column: 5, + }, + ], + }, ], }; From 851bb90216e20b7679efc891dc445e6152d4837f Mon Sep 17 00:00:00 2001 From: islandryu <65934663+islandryu@users.noreply.github.com> Date: Fri, 25 Feb 2022 01:50:41 +0900 Subject: [PATCH 09/12] fix(eslint-plugin): [sort-type-union-intersection-members] Wrap the constructorType in parentheses (#4590) Co-authored-by: Josh Goldberg --- .../src/rules/sort-type-union-intersection-members.ts | 5 ++++- .../rules/sort-type-union-intersection-members.test.ts | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/sort-type-union-intersection-members.ts b/packages/eslint-plugin/src/rules/sort-type-union-intersection-members.ts index fcdaa00152de..fe7f698949c7 100644 --- a/packages/eslint-plugin/src/rules/sort-type-union-intersection-members.ts +++ b/packages/eslint-plugin/src/rules/sort-type-union-intersection-members.ts @@ -85,7 +85,10 @@ function getGroup(node: TSESTree.TypeNode): Group { } function requiresParentheses(node: TSESTree.TypeNode): boolean { - return node.type === AST_NODE_TYPES.TSFunctionType; + return ( + node.type === AST_NODE_TYPES.TSFunctionType || + node.type === AST_NODE_TYPES.TSConstructorType + ); } export type Options = [ diff --git a/packages/eslint-plugin/tests/rules/sort-type-union-intersection-members.test.ts b/packages/eslint-plugin/tests/rules/sort-type-union-intersection-members.test.ts index a5eeb2f2fa48..1d29a45ce610 100644 --- a/packages/eslint-plugin/tests/rules/sort-type-union-intersection-members.test.ts +++ b/packages/eslint-plugin/tests/rules/sort-type-union-intersection-members.test.ts @@ -272,6 +272,15 @@ type T = }, ], }, + { + code: `type Expected = (new (x: number) => boolean) ${operator} string;`, + output: `type Expected = string ${operator} (new (x: number) => boolean);`, + errors: [ + { + messageId: 'notSortedNamed', + }, + ], + }, ]; }; From 052cf51fe663283afe89dc7bf97c947e750df095 Mon Sep 17 00:00:00 2001 From: uhyo Date: Fri, 25 Feb 2022 03:08:53 +0900 Subject: [PATCH 10/12] feat(eslint-plugin): [no-misused-promises] check more places for checksVoidReturn (#4541) * refactor(eslint-plugin): create isVoidReturningFunctionType * feat(eslint-plugin): add checkAssignments and checkVariableDeclaration * test(eslint-plugin): add valid test cases * feat(eslint-plugin): add object property checks * feat(eslint-plugin): add checkReturnStatements * feat(eslint-plugin): add checkJSXAttributes * fix(website): resolve newly introduced lint errors * test(eslint-plugin): worship code coverage * Apply suggestions from code review Co-authored-by: Josh Goldberg * fix(eslint-plugin): rename checker functions to singular * fix(eslint-plugin): align error messages * refactor(eslint-plugin): change MessageId * refactor(eslint-plugin): fix if statements style * refactor(eslint-plugin): no need of getTypeAtLocation * refactor(eslint-plugin): make coverage 100% * refactor(eslint-plugin): update comment Co-authored-by: Josh Goldberg --- .../src/rules/no-misused-promises.ts | 228 +++++++++++++-- .../tests/rules/no-misused-promises.test.ts | 269 +++++++++++++++++- .../src/components/OptionsSelector.tsx | 16 +- 3 files changed, 476 insertions(+), 37 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index 6e42e5474361..7f08402334cc 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -11,7 +11,15 @@ type Options = [ }, ]; -export default util.createRule({ +type MessageId = + | 'conditional' + | 'voidReturnArgument' + | 'voidReturnVariable' + | 'voidReturnProperty' + | 'voidReturnReturnValue' + | 'voidReturnAttribute'; + +export default util.createRule({ name: 'no-misused-promises', meta: { docs: { @@ -20,8 +28,16 @@ export default util.createRule({ requiresTypeChecking: true, }, messages: { - voidReturn: + voidReturnArgument: 'Promise returned in function argument where a void return was expected.', + voidReturnVariable: + 'Promise-returning function provided to variable where a void return was expected.', + voidReturnProperty: + 'Promise-returning function provided to property where a void return was expected.', + voidReturnReturnValue: + 'Promise-returning function provided to return value where a void return was expected.', + voidReturnAttribute: + 'Promise-returning function provided to attribute where a void return was expected.', conditional: 'Expected non-Promise value in a boolean conditional.', }, schema: [ @@ -67,6 +83,11 @@ export default util.createRule({ const voidReturnChecks: TSESLint.RuleListener = { CallExpression: checkArguments, NewExpression: checkArguments, + AssignmentExpression: checkAssignment, + VariableDeclarator: checkVariableDeclaration, + Property: checkProperty, + ReturnStatement: checkReturnStatement, + JSXAttribute: checkJSXAttribute, }; function checkTestConditional(node: { @@ -130,13 +151,168 @@ export default util.createRule({ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(argument); if (returnsThenable(checker, tsNode as ts.Expression)) { context.report({ - messageId: 'voidReturn', + messageId: 'voidReturnArgument', node: argument, }); } } } + function checkAssignment(node: TSESTree.AssignmentExpression): void { + const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); + const varType = checker.getTypeAtLocation(tsNode.left); + if (!isVoidReturningFunctionType(checker, tsNode.left, varType)) { + return; + } + + if (returnsThenable(checker, tsNode.right)) { + context.report({ + messageId: 'voidReturnVariable', + node: node.right, + }); + } + } + + function checkVariableDeclaration(node: TSESTree.VariableDeclarator): void { + const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); + if (tsNode.initializer === undefined || node.init === null) { + return; + } + const varType = checker.getTypeAtLocation(tsNode.name); + if (!isVoidReturningFunctionType(checker, tsNode.initializer, varType)) { + return; + } + + if (returnsThenable(checker, tsNode.initializer)) { + context.report({ + messageId: 'voidReturnVariable', + node: node.init, + }); + } + } + + function checkProperty(node: TSESTree.Property): void { + const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); + if (ts.isPropertyAssignment(tsNode)) { + const contextualType = checker.getContextualType(tsNode.initializer); + if ( + contextualType !== undefined && + isVoidReturningFunctionType( + checker, + tsNode.initializer, + contextualType, + ) && + returnsThenable(checker, tsNode.initializer) + ) { + context.report({ + messageId: 'voidReturnProperty', + node: node.value, + }); + } + } else if (ts.isShorthandPropertyAssignment(tsNode)) { + const contextualType = checker.getContextualType(tsNode.name); + if ( + contextualType !== undefined && + isVoidReturningFunctionType(checker, tsNode.name, contextualType) && + returnsThenable(checker, tsNode.name) + ) { + context.report({ + messageId: 'voidReturnProperty', + node: node.value, + }); + } + } else if (ts.isMethodDeclaration(tsNode)) { + if (ts.isComputedPropertyName(tsNode.name)) { + return; + } + const obj = tsNode.parent; + + // Below condition isn't satisfied unless something goes wrong, + // but is needed for type checking. + // 'node' does not include class method declaration so 'obj' is + // always an object literal expression, but after converting 'node' + // to TypeScript AST, its type includes MethodDeclaration which + // does include the case of class method declaration. + if (!ts.isObjectLiteralExpression(obj)) { + return; + } + + const objType = checker.getContextualType(obj); + if (objType === undefined) { + return; + } + const propertySymbol = checker.getPropertyOfType( + objType, + tsNode.name.text, + ); + if (propertySymbol === undefined) { + return; + } + + const contextualType = checker.getTypeOfSymbolAtLocation( + propertySymbol, + tsNode.name, + ); + + if ( + isVoidReturningFunctionType(checker, tsNode.name, contextualType) && + returnsThenable(checker, tsNode) + ) { + context.report({ + messageId: 'voidReturnProperty', + node: node.value, + }); + } + return; + } + } + + function checkReturnStatement(node: TSESTree.ReturnStatement): void { + const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); + if (tsNode.expression === undefined || node.argument === null) { + return; + } + const contextualType = checker.getContextualType(tsNode.expression); + if ( + contextualType !== undefined && + isVoidReturningFunctionType( + checker, + tsNode.expression, + contextualType, + ) && + returnsThenable(checker, tsNode.expression) + ) { + context.report({ + messageId: 'voidReturnReturnValue', + node: node.argument, + }); + } + } + + function checkJSXAttribute(node: TSESTree.JSXAttribute): void { + const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); + const value = tsNode.initializer; + if ( + node.value === null || + value === undefined || + !ts.isJsxExpression(value) || + value.expression === undefined + ) { + return; + } + const contextualType = checker.getContextualType(value); + if ( + contextualType !== undefined && + isVoidReturningFunctionType(checker, value, contextualType) && + returnsThenable(checker, value.expression) + ) { + context.report({ + messageId: 'voidReturnAttribute', + node: node.value, + }); + } + } + return { ...(checksConditionals ? conditionalChecks : {}), ...(checksVoidReturn ? voidReturnChecks : {}), @@ -219,7 +395,6 @@ function voidFunctionParams( node: ts.CallExpression | ts.NewExpression, ): Set { const voidReturnIndices = new Set(); - const thenableReturnIndices = new Set(); const type = checker.getTypeAtLocation(node.expression); for (const subType of tsutils.unionTypeParts(type)) { @@ -233,36 +408,41 @@ function voidFunctionParams( parameter, node.expression, ); - for (const subType of tsutils.unionTypeParts(type)) { - for (const signature of subType.getCallSignatures()) { - const returnType = signature.getReturnType(); - if (tsutils.isTypeFlagSet(returnType, ts.TypeFlags.Void)) { - voidReturnIndices.add(index); - } else if ( - tsutils.isThenableType(checker, node.expression, returnType) - ) { - thenableReturnIndices.add(index); - } - } + if (isVoidReturningFunctionType(checker, node.expression, type)) { + voidReturnIndices.add(index); } } } } - // If a certain positional argument accepts both thenable and void returns, - // a promise-returning function is valid - for (const thenable of thenableReturnIndices) { - voidReturnIndices.delete(thenable); - } - return voidReturnIndices; } -// Returns true if the expression is a function that returns a thenable -function returnsThenable( +// Returns true if given type is a void-returning function. +function isVoidReturningFunctionType( checker: ts.TypeChecker, - node: ts.Expression, + node: ts.Node, + type: ts.Type, ): boolean { + let hasVoidReturningFunction = false; + let hasThenableReturningFunction = false; + for (const subType of tsutils.unionTypeParts(type)) { + for (const signature of subType.getCallSignatures()) { + const returnType = signature.getReturnType(); + if (tsutils.isTypeFlagSet(returnType, ts.TypeFlags.Void)) { + hasVoidReturningFunction = true; + } else if (tsutils.isThenableType(checker, node, returnType)) { + hasThenableReturningFunction = true; + } + } + } + // If a certain positional argument accepts both thenable and void returns, + // a promise-returning function is valid + return hasVoidReturningFunction && !hasThenableReturningFunction; +} + +// Returns true if the expression is a function that returns a thenable +function returnsThenable(checker: ts.TypeChecker, node: ts.Node): boolean { const type = checker.getApparentType(checker.getTypeAtLocation(node)); for (const subType of tsutils.unionTypeParts(type)) { 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 63e429000c03..961a21d4c892 100644 --- a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts +++ b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts @@ -166,6 +166,92 @@ async function test(p: Promise | undefined) { } } `, + ` +let f; +f = async () => 10; + `, + ` +let f: () => Promise; +f = async () => 10; +const g = async () => 0; +const h: () => Promise = async () => 10; + `, + ` +const obj = { + f: async () => 10, +}; + `, + ` +const f = async () => 123; +const obj = { + f, +}; + `, + ` +const obj = { + async f() { + return 0; + }, +}; + `, + ` +type O = { f: () => Promise; g: () => Promise }; +const g = async () => 0; +const obj: O = { + f: async () => 10, + g, +}; + `, + ` +type O = { f: () => Promise }; +const name = 'f'; +const obj: O = { + async [name]() { + return 10; + }, +}; + `, + ` +const obj: number = { + g() { + return 10; + }, +}; + `, + ` +const obj = { + f: async () => 'foo', + async g() { + return 0; + }, +}; + `, + ` +function f() { + return async () => 0; +} +function g() { + return; +} + `, + { + code: ` +type O = { + bool: boolean; + func: () => Promise; +}; +const Component = (obj: O) => null; + 10} />; + `, + filename: 'react.tsx', + }, + { + code: ` +const Component: any = () => null; + 10} />; + `, + filename: 'react.tsx', + }, ], invalid: [ @@ -265,7 +351,7 @@ if (!Promise.resolve()) { errors: [ { line: 2, - messageId: 'voidReturn', + messageId: 'voidReturnArgument', }, ], }, @@ -279,7 +365,7 @@ new Promise(async (resolve, reject) => { errors: [ { line: 2, - messageId: 'voidReturn', + messageId: 'voidReturnArgument', }, ], }, @@ -296,7 +382,7 @@ fnWithCallback('val', async (err, res) => { errors: [ { line: 6, - messageId: 'voidReturn', + messageId: 'voidReturnArgument', }, ], }, @@ -311,7 +397,7 @@ fnWithCallback('val', (err, res) => Promise.resolve(res)); errors: [ { line: 6, - messageId: 'voidReturn', + messageId: 'voidReturnArgument', }, ], }, @@ -332,7 +418,7 @@ fnWithCallback('val', (err, res) => { errors: [ { line: 6, - messageId: 'voidReturn', + messageId: 'voidReturnArgument', }, ], }, @@ -349,7 +435,7 @@ fnWithCallback?.('val', (err, res) => Promise.resolve(res)); errors: [ { line: 8, - messageId: 'voidReturn', + messageId: 'voidReturnArgument', }, ], }, @@ -372,7 +458,7 @@ fnWithCallback('val', (err, res) => { errors: [ { line: 8, - messageId: 'voidReturn', + messageId: 'voidReturnArgument', }, ], }, @@ -432,5 +518,174 @@ function test(p: Promise | undefined) { }, ], }, + { + code: ` +let f: () => void; +f = async () => { + return 3; +}; + `, + errors: [ + { + line: 3, + messageId: 'voidReturnVariable', + }, + ], + }, + { + code: ` +const f: () => void = async () => { + return 0; +}; +const g = async () => 1, + h: () => void = async () => {}; + `, + errors: [ + { + line: 2, + messageId: 'voidReturnVariable', + }, + { + line: 6, + messageId: 'voidReturnVariable', + }, + ], + }, + { + code: ` +const obj: { + f?: () => void; +} = {}; +obj.f = async () => { + return 0; +}; + `, + errors: [ + { + line: 5, + messageId: 'voidReturnVariable', + }, + ], + }, + { + code: ` +type O = { f: () => void }; +const obj: O = { + f: async () => 'foo', +}; + `, + errors: [ + { + line: 4, + messageId: 'voidReturnProperty', + }, + ], + }, + { + code: ` +type O = { f: () => void }; +const f = async () => 0; +const obj: O = { + f, +}; + `, + errors: [ + { + line: 5, + messageId: 'voidReturnProperty', + }, + ], + }, + { + code: ` +type O = { f: () => void }; +const obj: O = { + async f() { + return 0; + }, +}; + `, + errors: [ + { + line: 4, + messageId: 'voidReturnProperty', + }, + ], + }, + { + code: ` +type O = { f: () => void; g: () => void; h: () => void }; +function f(): O { + const h = async () => 0; + return { + async f() { + return 123; + }, + g: async () => 0, + h, + }; +} + `, + errors: [ + { + line: 6, + messageId: 'voidReturnProperty', + }, + { + line: 9, + messageId: 'voidReturnProperty', + }, + { + line: 10, + messageId: 'voidReturnProperty', + }, + ], + }, + { + code: ` +function f(): () => void { + return async () => 0; +} + `, + errors: [ + { + line: 3, + messageId: 'voidReturnReturnValue', + }, + ], + }, + { + code: ` +type O = { + func: () => void; +}; +const Component = (obj: O) => null; + 0} />; + `, + filename: 'react.tsx', + errors: [ + { + line: 6, + messageId: 'voidReturnAttribute', + }, + ], + }, + { + code: ` +type O = { + func: () => void; +}; +const g = async () => 'foo'; +const Component = (obj: O) => null; +; + `, + filename: 'react.tsx', + errors: [ + { + line: 7, + messageId: 'voidReturnAttribute', + }, + ], + }, ], }); diff --git a/packages/website/src/components/OptionsSelector.tsx b/packages/website/src/components/OptionsSelector.tsx index 83715fc3ae5c..86c842965073 100644 --- a/packages/website/src/components/OptionsSelector.tsx +++ b/packages/website/src/components/OptionsSelector.tsx @@ -74,17 +74,21 @@ function OptionsSelector({ [setState], ); - const copyLinkToClipboard = useCallback(async () => { - await navigator.clipboard.writeText(document.location.toString()); - setCopyLink(true); + const copyLinkToClipboard = useCallback(() => { + void navigator.clipboard + .writeText(document.location.toString()) + .then(() => { + setCopyLink(true); + }); }, []); - const copyMarkdownToClipboard = useCallback(async () => { + const copyMarkdownToClipboard = useCallback(() => { if (isLoading) { return; } - await navigator.clipboard.writeText(createMarkdown(state)); - setCopyMarkdown(true); + void navigator.clipboard.writeText(createMarkdown(state)).then(() => { + setCopyMarkdown(true); + }); }, [state, isLoading]); const openIssue = useCallback(() => { From 4aefecc063d83b12ecac4fc3485ba3b6b6ca8e89 Mon Sep 17 00:00:00 2001 From: xuchaobei Date: Sat, 26 Feb 2022 01:09:45 +0800 Subject: [PATCH 11/12] Update README.md (#4592) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3535cd83c38..64c2d50e9aeb 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ All of the packages are published with the same version number to make it easier We publish a canary release on every successful merge to `main`, so **you never need to wait for a new stable version to make use of any updates**. -Additionally, we promote the to the `latest` tag on NPM once per week, **on Mondays at 1 pm Eastern**. +Additionally, we promote to the `latest` tag on NPM once per week, **on Mondays at 1 pm Eastern**. The latest version under the `latest` tag is: From 9d47a8b94577540ae445e5ec17373da25423eeb8 Mon Sep 17 00:00:00 2001 From: James Henry Date: Mon, 28 Feb 2022 18:02:54 +0000 Subject: [PATCH 12/12] chore: publish v5.13.0 --- CHANGELOG.md | 22 ++++++++++++++++++++ lerna.json | 2 +- packages/ast-spec/CHANGELOG.md | 8 +++++++ packages/ast-spec/package.json | 2 +- packages/eslint-plugin-internal/CHANGELOG.md | 8 +++++++ packages/eslint-plugin-internal/package.json | 6 +++--- packages/eslint-plugin-tslint/CHANGELOG.md | 8 +++++++ packages/eslint-plugin-tslint/package.json | 6 +++--- packages/eslint-plugin/CHANGELOG.md | 20 ++++++++++++++++++ packages/eslint-plugin/package.json | 8 +++---- packages/experimental-utils/CHANGELOG.md | 8 +++++++ packages/experimental-utils/package.json | 4 ++-- packages/parser/CHANGELOG.md | 8 +++++++ packages/parser/package.json | 8 +++---- packages/scope-manager/CHANGELOG.md | 8 +++++++ packages/scope-manager/package.json | 8 +++---- packages/shared-fixtures/CHANGELOG.md | 8 +++++++ packages/shared-fixtures/package.json | 2 +- packages/type-utils/CHANGELOG.md | 11 ++++++++++ packages/type-utils/package.json | 6 +++--- packages/types/CHANGELOG.md | 8 +++++++ packages/types/package.json | 2 +- packages/typescript-estree/CHANGELOG.md | 8 +++++++ packages/typescript-estree/package.json | 8 +++---- packages/utils/CHANGELOG.md | 13 ++++++++++++ packages/utils/package.json | 8 +++---- packages/visitor-keys/CHANGELOG.md | 8 +++++++ packages/visitor-keys/package.json | 4 ++-- packages/website-eslint/CHANGELOG.md | 8 +++++++ packages/website-eslint/package.json | 16 +++++++------- packages/website/CHANGELOG.md | 11 ++++++++++ packages/website/package.json | 4 ++-- 32 files changed, 212 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c2a68154c59..227285069704 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,28 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + + +### Bug Fixes + +* **eslint-plugin:** [sort-type-union-intersection-members] Wrap the constructorType in parentheses ([#4590](https://github.com/typescript-eslint/typescript-eslint/issues/4590)) ([851bb90](https://github.com/typescript-eslint/typescript-eslint/commit/851bb90216e20b7679efc891dc445e6152d4837f)) + + +### Features + +* **eslint-plugin:** [no-misused-promises] check more places for checksVoidReturn ([#4541](https://github.com/typescript-eslint/typescript-eslint/issues/4541)) ([052cf51](https://github.com/typescript-eslint/typescript-eslint/commit/052cf51fe663283afe89dc7bf97c947e750df095)) +* **eslint-plugin:** add `no-redundant-type-constituents` rule ([#4378](https://github.com/typescript-eslint/typescript-eslint/issues/4378)) ([63d051e](https://github.com/typescript-eslint/typescript-eslint/commit/63d051eed29dcf71015a23992feac0a8f92717a0)) +* **eslint-plugin:** add `no-useless-empty-export` rule ([#4380](https://github.com/typescript-eslint/typescript-eslint/issues/4380)) ([823b945](https://github.com/typescript-eslint/typescript-eslint/commit/823b945c8f9e83d0246a2a5d07519f01e1a64518)) +* **eslint-plugin:** add extension rule `space-before-blocks` ([#1606](https://github.com/typescript-eslint/typescript-eslint/issues/1606)) ([#4184](https://github.com/typescript-eslint/typescript-eslint/issues/4184)) ([208b6d0](https://github.com/typescript-eslint/typescript-eslint/commit/208b6d02252dff2bf272329d3e4a4a82e56c52c0)) +* **eslint-plugin:** added member group support to member-ordering rule ([#4538](https://github.com/typescript-eslint/typescript-eslint/issues/4538)) ([6afcaea](https://github.com/typescript-eslint/typescript-eslint/commit/6afcaea0160a1ccd1c6483ca677c544ca1b8cb4f)) +* **utils:** expose `ast-utils`' helpers ([#4503](https://github.com/typescript-eslint/typescript-eslint/issues/4503)) ([f106e4b](https://github.com/typescript-eslint/typescript-eslint/commit/f106e4b95e824ebb68141bce3d3207448d50c860)) +* **utils:** extract `isNotTokenOfTypeWithConditions` out of `ast-utils`' `predicates` ([#4502](https://github.com/typescript-eslint/typescript-eslint/issues/4502)) ([66501d6](https://github.com/typescript-eslint/typescript-eslint/commit/66501d6dd7e97c22c671efaa6d1ba8237907e417)) + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) diff --git a/lerna.json b/lerna.json index 7adf338d75cc..64055f7146da 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "5.12.1", + "version": "5.13.0", "npmClient": "yarn", "useWorkspaces": true, "stream": true diff --git a/packages/ast-spec/CHANGELOG.md b/packages/ast-spec/CHANGELOG.md index a34608c74db3..6c09876810e8 100644 --- a/packages/ast-spec/CHANGELOG.md +++ b/packages/ast-spec/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + +**Note:** Version bump only for package @typescript-eslint/ast-spec + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) **Note:** Version bump only for package @typescript-eslint/ast-spec diff --git a/packages/ast-spec/package.json b/packages/ast-spec/package.json index 230d6657ee1c..6e5f0c3db6b4 100644 --- a/packages/ast-spec/package.json +++ b/packages/ast-spec/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/ast-spec", - "version": "5.12.1", + "version": "5.13.0", "description": "TypeScript-ESTree AST spec", "private": true, "keywords": [ diff --git a/packages/eslint-plugin-internal/CHANGELOG.md b/packages/eslint-plugin-internal/CHANGELOG.md index 39e4b11f2610..4cb6c2804ff5 100644 --- a/packages/eslint-plugin-internal/CHANGELOG.md +++ b/packages/eslint-plugin-internal/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) **Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index b27e1afce240..353fae18f2ef 100644 --- a/packages/eslint-plugin-internal/package.json +++ b/packages/eslint-plugin-internal/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-internal", - "version": "5.12.1", + "version": "5.13.0", "private": true, "main": "dist/index.js", "scripts": { @@ -14,8 +14,8 @@ }, "dependencies": { "@types/prettier": "*", - "@typescript-eslint/scope-manager": "5.12.1", - "@typescript-eslint/utils": "5.12.1", + "@typescript-eslint/scope-manager": "5.13.0", + "@typescript-eslint/utils": "5.13.0", "prettier": "*" } } diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md index d1b858a2d95c..768a35dc57c6 100644 --- a/packages/eslint-plugin-tslint/CHANGELOG.md +++ b/packages/eslint-plugin-tslint/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) **Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index 4cd996d123b7..c98cbbbc654d 100644 --- a/packages/eslint-plugin-tslint/package.json +++ b/packages/eslint-plugin-tslint/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-tslint", - "version": "5.12.1", + "version": "5.13.0", "main": "dist/index.js", "typings": "src/index.ts", "description": "TSLint wrapper plugin for ESLint", @@ -38,7 +38,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/utils": "5.12.1", + "@typescript-eslint/utils": "5.13.0", "lodash": "^4.17.21" }, "peerDependencies": { @@ -48,6 +48,6 @@ }, "devDependencies": { "@types/lodash": "*", - "@typescript-eslint/parser": "5.12.1" + "@typescript-eslint/parser": "5.13.0" } } diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 7bba47fd7bce..947c63d50256 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + + +### Bug Fixes + +* **eslint-plugin:** [sort-type-union-intersection-members] Wrap the constructorType in parentheses ([#4590](https://github.com/typescript-eslint/typescript-eslint/issues/4590)) ([851bb90](https://github.com/typescript-eslint/typescript-eslint/commit/851bb90216e20b7679efc891dc445e6152d4837f)) + + +### Features + +* **eslint-plugin:** [no-misused-promises] check more places for checksVoidReturn ([#4541](https://github.com/typescript-eslint/typescript-eslint/issues/4541)) ([052cf51](https://github.com/typescript-eslint/typescript-eslint/commit/052cf51fe663283afe89dc7bf97c947e750df095)) +* **eslint-plugin:** add `no-redundant-type-constituents` rule ([#4378](https://github.com/typescript-eslint/typescript-eslint/issues/4378)) ([63d051e](https://github.com/typescript-eslint/typescript-eslint/commit/63d051eed29dcf71015a23992feac0a8f92717a0)) +* **eslint-plugin:** add `no-useless-empty-export` rule ([#4380](https://github.com/typescript-eslint/typescript-eslint/issues/4380)) ([823b945](https://github.com/typescript-eslint/typescript-eslint/commit/823b945c8f9e83d0246a2a5d07519f01e1a64518)) +* **eslint-plugin:** add extension rule `space-before-blocks` ([#1606](https://github.com/typescript-eslint/typescript-eslint/issues/1606)) ([#4184](https://github.com/typescript-eslint/typescript-eslint/issues/4184)) ([208b6d0](https://github.com/typescript-eslint/typescript-eslint/commit/208b6d02252dff2bf272329d3e4a4a82e56c52c0)) +* **eslint-plugin:** added member group support to member-ordering rule ([#4538](https://github.com/typescript-eslint/typescript-eslint/issues/4538)) ([6afcaea](https://github.com/typescript-eslint/typescript-eslint/commit/6afcaea0160a1ccd1c6483ca677c544ca1b8cb4f)) + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 69d7a5529ea7..2ed93a352529 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin", - "version": "5.12.1", + "version": "5.13.0", "description": "TypeScript plugin for ESLint", "keywords": [ "eslint", @@ -44,9 +44,9 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/scope-manager": "5.12.1", - "@typescript-eslint/type-utils": "5.12.1", - "@typescript-eslint/utils": "5.12.1", + "@typescript-eslint/scope-manager": "5.13.0", + "@typescript-eslint/type-utils": "5.13.0", + "@typescript-eslint/utils": "5.13.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md index 6a2cbe78f1ac..a2431353a456 100644 --- a/packages/experimental-utils/CHANGELOG.md +++ b/packages/experimental-utils/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + +**Note:** Version bump only for package @typescript-eslint/experimental-utils + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) **Note:** Version bump only for package @typescript-eslint/experimental-utils diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index 1bc0685e05a8..2bf8d3f2cfbc 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/experimental-utils", - "version": "5.12.1", + "version": "5.13.0", "description": "(Experimental) Utilities for working with TypeScript + ESLint together", "keywords": [ "eslint", @@ -38,7 +38,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/utils": "5.12.1" + "@typescript-eslint/utils": "5.13.0" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md index 1f0b280b4506..eb904406d62b 100644 --- a/packages/parser/CHANGELOG.md +++ b/packages/parser/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + +**Note:** Version bump only for package @typescript-eslint/parser + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) **Note:** Version bump only for package @typescript-eslint/parser diff --git a/packages/parser/package.json b/packages/parser/package.json index c9f6f962a510..bbe3659fc921 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/parser", - "version": "5.12.1", + "version": "5.13.0", "description": "An ESLint custom parser which leverages TypeScript ESTree", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -44,9 +44,9 @@ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "dependencies": { - "@typescript-eslint/scope-manager": "5.12.1", - "@typescript-eslint/types": "5.12.1", - "@typescript-eslint/typescript-estree": "5.12.1", + "@typescript-eslint/scope-manager": "5.13.0", + "@typescript-eslint/types": "5.13.0", + "@typescript-eslint/typescript-estree": "5.13.0", "debug": "^4.3.2" }, "devDependencies": { diff --git a/packages/scope-manager/CHANGELOG.md b/packages/scope-manager/CHANGELOG.md index e0dd15ec5814..faa953a077a6 100644 --- a/packages/scope-manager/CHANGELOG.md +++ b/packages/scope-manager/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + +**Note:** Version bump only for package @typescript-eslint/scope-manager + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) **Note:** Version bump only for package @typescript-eslint/scope-manager diff --git a/packages/scope-manager/package.json b/packages/scope-manager/package.json index b4ddedb62332..c010b60160d7 100644 --- a/packages/scope-manager/package.json +++ b/packages/scope-manager/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/scope-manager", - "version": "5.12.1", + "version": "5.13.0", "description": "TypeScript scope analyser for ESLint", "keywords": [ "eslint", @@ -39,12 +39,12 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/types": "5.12.1", - "@typescript-eslint/visitor-keys": "5.12.1" + "@typescript-eslint/types": "5.13.0", + "@typescript-eslint/visitor-keys": "5.13.0" }, "devDependencies": { "@types/glob": "*", - "@typescript-eslint/typescript-estree": "5.12.1", + "@typescript-eslint/typescript-estree": "5.13.0", "glob": "*", "jest-specific-snapshot": "*", "make-dir": "*", diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md index f935e72e5ba3..66d60cd5e848 100644 --- a/packages/shared-fixtures/CHANGELOG.md +++ b/packages/shared-fixtures/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + +**Note:** Version bump only for package @typescript-eslint/shared-fixtures + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) **Note:** Version bump only for package @typescript-eslint/shared-fixtures diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json index 762ad6131868..b350512e48bb 100644 --- a/packages/shared-fixtures/package.json +++ b/packages/shared-fixtures/package.json @@ -1,5 +1,5 @@ { "name": "@typescript-eslint/shared-fixtures", - "version": "5.12.1", + "version": "5.13.0", "private": true } diff --git a/packages/type-utils/CHANGELOG.md b/packages/type-utils/CHANGELOG.md index 56e63599e4cd..7fee288a635f 100644 --- a/packages/type-utils/CHANGELOG.md +++ b/packages/type-utils/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + + +### Features + +* **eslint-plugin:** add `no-redundant-type-constituents` rule ([#4378](https://github.com/typescript-eslint/typescript-eslint/issues/4378)) ([63d051e](https://github.com/typescript-eslint/typescript-eslint/commit/63d051eed29dcf71015a23992feac0a8f92717a0)) + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) diff --git a/packages/type-utils/package.json b/packages/type-utils/package.json index 1f5685e67955..16a23faf7702 100644 --- a/packages/type-utils/package.json +++ b/packages/type-utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/type-utils", - "version": "5.12.1", + "version": "5.13.0", "description": "Type utilities for working with TypeScript + ESLint together", "keywords": [ "eslint", @@ -39,12 +39,12 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/utils": "5.12.1", + "@typescript-eslint/utils": "5.13.0", "debug": "^4.3.2", "tsutils": "^3.21.0" }, "devDependencies": { - "@typescript-eslint/parser": "5.12.1", + "@typescript-eslint/parser": "5.13.0", "typescript": "*" }, "peerDependencies": { diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 90dfc70926e4..26062862c481 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + +**Note:** Version bump only for package @typescript-eslint/types + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) **Note:** Version bump only for package @typescript-eslint/types diff --git a/packages/types/package.json b/packages/types/package.json index 206ebf9545ed..a0193e6067aa 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/types", - "version": "5.12.1", + "version": "5.13.0", "description": "Types for the TypeScript-ESTree AST spec", "keywords": [ "eslint", diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md index 4d7ad425b84b..6919033b0411 100644 --- a/packages/typescript-estree/CHANGELOG.md +++ b/packages/typescript-estree/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + +**Note:** Version bump only for package @typescript-eslint/typescript-estree + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) **Note:** Version bump only for package @typescript-eslint/typescript-estree diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index fa26a4ef6892..d4ec037e5df9 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/typescript-estree", - "version": "5.12.1", + "version": "5.13.0", "description": "A parser that converts TypeScript source code into an ESTree compatible form", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -41,8 +41,8 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/types": "5.12.1", - "@typescript-eslint/visitor-keys": "5.12.1", + "@typescript-eslint/types": "5.13.0", + "@typescript-eslint/visitor-keys": "5.13.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -59,7 +59,7 @@ "@types/is-glob": "*", "@types/semver": "*", "@types/tmp": "*", - "@typescript-eslint/shared-fixtures": "5.12.1", + "@typescript-eslint/shared-fixtures": "5.13.0", "glob": "*", "jest-specific-snapshot": "*", "make-dir": "*", diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index f1c92f415df4..a699c9f1aad6 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + + +### Features + +* **eslint-plugin:** add extension rule `space-before-blocks` ([#1606](https://github.com/typescript-eslint/typescript-eslint/issues/1606)) ([#4184](https://github.com/typescript-eslint/typescript-eslint/issues/4184)) ([208b6d0](https://github.com/typescript-eslint/typescript-eslint/commit/208b6d02252dff2bf272329d3e4a4a82e56c52c0)) +* **utils:** expose `ast-utils`' helpers ([#4503](https://github.com/typescript-eslint/typescript-eslint/issues/4503)) ([f106e4b](https://github.com/typescript-eslint/typescript-eslint/commit/f106e4b95e824ebb68141bce3d3207448d50c860)) +* **utils:** extract `isNotTokenOfTypeWithConditions` out of `ast-utils`' `predicates` ([#4502](https://github.com/typescript-eslint/typescript-eslint/issues/4502)) ([66501d6](https://github.com/typescript-eslint/typescript-eslint/commit/66501d6dd7e97c22c671efaa6d1ba8237907e417)) + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) **Note:** Version bump only for package @typescript-eslint/utils diff --git a/packages/utils/package.json b/packages/utils/package.json index d3fafa8278fd..03d6e47493e1 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/utils", - "version": "5.12.1", + "version": "5.13.0", "description": "Utilities for working with TypeScript + ESLint together", "keywords": [ "eslint", @@ -40,9 +40,9 @@ }, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.12.1", - "@typescript-eslint/types": "5.12.1", - "@typescript-eslint/typescript-estree": "5.12.1", + "@typescript-eslint/scope-manager": "5.13.0", + "@typescript-eslint/types": "5.13.0", + "@typescript-eslint/typescript-estree": "5.13.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, diff --git a/packages/visitor-keys/CHANGELOG.md b/packages/visitor-keys/CHANGELOG.md index 88312dcfe057..5e0ff65320fc 100644 --- a/packages/visitor-keys/CHANGELOG.md +++ b/packages/visitor-keys/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + +**Note:** Version bump only for package @typescript-eslint/visitor-keys + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) **Note:** Version bump only for package @typescript-eslint/visitor-keys diff --git a/packages/visitor-keys/package.json b/packages/visitor-keys/package.json index 0fc64f259de9..798598914661 100644 --- a/packages/visitor-keys/package.json +++ b/packages/visitor-keys/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/visitor-keys", - "version": "5.12.1", + "version": "5.13.0", "description": "Visitor keys used to help traverse the TypeScript-ESTree AST", "keywords": [ "eslint", @@ -38,7 +38,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/types": "5.12.1", + "@typescript-eslint/types": "5.13.0", "eslint-visitor-keys": "^3.0.0" }, "devDependencies": { diff --git a/packages/website-eslint/CHANGELOG.md b/packages/website-eslint/CHANGELOG.md index 6268851171ce..928b86d944d3 100644 --- a/packages/website-eslint/CHANGELOG.md +++ b/packages/website-eslint/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + +**Note:** Version bump only for package @typescript-eslint/website-eslint + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) **Note:** Version bump only for package @typescript-eslint/website-eslint diff --git a/packages/website-eslint/package.json b/packages/website-eslint/package.json index 45ec14a00ffe..5d1eb3dfa376 100644 --- a/packages/website-eslint/package.json +++ b/packages/website-eslint/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/website-eslint", - "version": "5.12.1", + "version": "5.13.0", "private": true, "description": "ESLint which works in browsers.", "engines": { @@ -16,19 +16,19 @@ "format": "prettier --write \"./**/*.{ts,js,json,md}\" --ignore-path ../../.prettierignore" }, "dependencies": { - "@typescript-eslint/types": "5.12.1", - "@typescript-eslint/utils": "5.12.1" + "@typescript-eslint/types": "5.13.0", + "@typescript-eslint/utils": "5.13.0" }, "devDependencies": { "@rollup/plugin-commonjs": "^21.0.1", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.0.6", "@rollup/pluginutils": "^4.1.1", - "@typescript-eslint/eslint-plugin": "5.12.1", - "@typescript-eslint/parser": "5.12.1", - "@typescript-eslint/scope-manager": "5.12.1", - "@typescript-eslint/typescript-estree": "5.12.1", - "@typescript-eslint/visitor-keys": "5.12.1", + "@typescript-eslint/eslint-plugin": "5.13.0", + "@typescript-eslint/parser": "5.13.0", + "@typescript-eslint/scope-manager": "5.13.0", + "@typescript-eslint/typescript-estree": "5.13.0", + "@typescript-eslint/visitor-keys": "5.13.0", "eslint": "*", "rollup": "^2.59.0", "semver": "^7.3.5" diff --git a/packages/website/CHANGELOG.md b/packages/website/CHANGELOG.md index d29faabc6c4d..85539eb69c32 100644 --- a/packages/website/CHANGELOG.md +++ b/packages/website/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28) + + +### Features + +* **eslint-plugin:** [no-misused-promises] check more places for checksVoidReturn ([#4541](https://github.com/typescript-eslint/typescript-eslint/issues/4541)) ([052cf51](https://github.com/typescript-eslint/typescript-eslint/commit/052cf51fe663283afe89dc7bf97c947e750df095)) + + + + + ## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21) **Note:** Version bump only for package website diff --git a/packages/website/package.json b/packages/website/package.json index 50f187c41011..36f082444338 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -1,6 +1,6 @@ { "name": "website", - "version": "5.12.1", + "version": "5.13.0", "private": true, "scripts": { "build": "docusaurus build", @@ -22,7 +22,7 @@ "@docusaurus/theme-common": "^2.0.0-beta.15", "@docusaurus/theme-search-algolia": "^2.0.0-beta.15", "@mdx-js/react": "1.6.22", - "@typescript-eslint/website-eslint": "5.12.1", + "@typescript-eslint/website-eslint": "5.13.0", "clsx": "^1.1.1", "eslint": "*", "json5": "^2.2.0", 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