diff --git a/packages/eslint-plugin/src/configs/strict-type-checked-only.ts b/packages/eslint-plugin/src/configs/strict-type-checked-only.ts index 4188f5f73465..6ab9b2cc0e53 100644 --- a/packages/eslint-plugin/src/configs/strict-type-checked-only.ts +++ b/packages/eslint-plugin/src/configs/strict-type-checked-only.ts @@ -15,7 +15,7 @@ export = { '@typescript-eslint/no-base-to-string': 'error', '@typescript-eslint/no-confusing-void-expression': 'error', '@typescript-eslint/no-duplicate-type-constituents': 'error', - '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: false }], '@typescript-eslint/no-for-in-array': 'error', 'no-implied-eval': 'off', '@typescript-eslint/no-implied-eval': 'error', @@ -43,8 +43,27 @@ export = { '@typescript-eslint/prefer-return-this-type': 'error', 'require-await': 'off', '@typescript-eslint/require-await': 'error', - '@typescript-eslint/restrict-plus-operands': 'error', - '@typescript-eslint/restrict-template-expressions': 'error', + '@typescript-eslint/restrict-plus-operands': [ + 'error', + { + allowAny: false, + allowBoolean: false, + allowNullish: false, + allowNumberAndString: false, + allowRegExp: false, + }, + ], + '@typescript-eslint/restrict-template-expressions': [ + 'error', + { + allowAny: false, + allowBoolean: false, + allowNullish: false, + allowNumber: false, + allowRegExp: false, + allowNever: false, + }, + ], '@typescript-eslint/unbound-method': 'error', }, } satisfies ClassicConfig.Config; diff --git a/packages/eslint-plugin/src/configs/strict-type-checked.ts b/packages/eslint-plugin/src/configs/strict-type-checked.ts index 836a0ec76e7c..de748c677576 100644 --- a/packages/eslint-plugin/src/configs/strict-type-checked.ts +++ b/packages/eslint-plugin/src/configs/strict-type-checked.ts @@ -11,7 +11,10 @@ export = { extends: ['./configs/base', './configs/eslint-recommended'], rules: { '@typescript-eslint/await-thenable': 'error', - '@typescript-eslint/ban-ts-comment': 'error', + '@typescript-eslint/ban-ts-comment': [ + 'error', + { minimumDescriptionLength: 10 }, + ], '@typescript-eslint/ban-types': 'error', 'no-array-constructor': 'off', '@typescript-eslint/no-array-constructor': 'error', @@ -24,7 +27,7 @@ export = { '@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/no-extra-non-null-assertion': 'error', '@typescript-eslint/no-extraneous-class': 'error', - '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: false }], '@typescript-eslint/no-for-in-array': 'error', 'no-implied-eval': 'off', '@typescript-eslint/no-implied-eval': 'error', @@ -71,8 +74,27 @@ export = { '@typescript-eslint/prefer-ts-expect-error': 'error', 'require-await': 'off', '@typescript-eslint/require-await': 'error', - '@typescript-eslint/restrict-plus-operands': 'error', - '@typescript-eslint/restrict-template-expressions': 'error', + '@typescript-eslint/restrict-plus-operands': [ + 'error', + { + allowAny: false, + allowBoolean: false, + allowNullish: false, + allowNumberAndString: false, + allowRegExp: false, + }, + ], + '@typescript-eslint/restrict-template-expressions': [ + 'error', + { + allowAny: false, + allowBoolean: false, + allowNullish: false, + allowNumber: false, + allowRegExp: false, + allowNever: false, + }, + ], '@typescript-eslint/triple-slash-reference': 'error', '@typescript-eslint/unbound-method': 'error', '@typescript-eslint/unified-signatures': 'error', diff --git a/packages/eslint-plugin/src/configs/strict.ts b/packages/eslint-plugin/src/configs/strict.ts index e49b8cbcadc1..dbf57cc2c3f6 100644 --- a/packages/eslint-plugin/src/configs/strict.ts +++ b/packages/eslint-plugin/src/configs/strict.ts @@ -10,7 +10,10 @@ import type { ClassicConfig } from '@typescript-eslint/utils/ts-eslint'; export = { extends: ['./configs/base', './configs/eslint-recommended'], rules: { - '@typescript-eslint/ban-ts-comment': 'error', + '@typescript-eslint/ban-ts-comment': [ + 'error', + { minimumDescriptionLength: 10 }, + ], '@typescript-eslint/ban-types': 'error', 'no-array-constructor': 'off', '@typescript-eslint/no-array-constructor': 'error', diff --git a/packages/eslint-plugin/src/rules/ban-ts-comment.ts b/packages/eslint-plugin/src/rules/ban-ts-comment.ts index 5a5aeef35273..cd90e4cc6580 100644 --- a/packages/eslint-plugin/src/rules/ban-ts-comment.ts +++ b/packages/eslint-plugin/src/rules/ban-ts-comment.ts @@ -32,7 +32,10 @@ export default createRule<[Options], MessageIds>({ docs: { description: 'Disallow `@ts-` comments or require descriptions after directives', - recommended: 'recommended', + recommended: { + recommended: true, + strict: [{ minimumDescriptionLength: 10 }], + }, }, messages: { tsDirectiveComment: diff --git a/packages/eslint-plugin/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts index b3ac65296992..9e439118747f 100644 --- a/packages/eslint-plugin/src/rules/no-floating-promises.ts +++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts @@ -50,7 +50,10 @@ export default createRule({ docs: { description: 'Require Promise-like statements to be handled appropriately', - recommended: 'recommended', + recommended: { + recommended: true, + strict: [{ ignoreVoid: false }], + }, requiresTypeChecking: true, }, hasSuggestions: true, diff --git a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts index 9e17fa486b52..1953c15c70cb 100644 --- a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts +++ b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts @@ -31,7 +31,18 @@ export default createRule({ docs: { description: 'Require both operands of addition to be the same type and be `bigint`, `number`, or `string`', - recommended: 'recommended', + recommended: { + recommended: true, + strict: [ + { + allowAny: false, + allowBoolean: false, + allowNullish: false, + allowNumberAndString: false, + allowRegExp: false, + }, + ], + }, requiresTypeChecking: true, }, messages: { diff --git a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts index 960617ab044c..cc719fe7fb7b 100644 --- a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts +++ b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts @@ -62,7 +62,19 @@ export default createRule({ docs: { description: 'Enforce template literal expressions to be of `string` type', - recommended: 'recommended', + recommended: { + recommended: true, + strict: [ + { + allowAny: false, + allowBoolean: false, + allowNullish: false, + allowNumber: false, + allowRegExp: false, + allowNever: false, + }, + ], + }, requiresTypeChecking: true, }, messages: { diff --git a/packages/eslint-plugin/tests/configs.test.ts b/packages/eslint-plugin/tests/configs.test.ts index 23f8d5ffd35c..db9130e18011 100644 --- a/packages/eslint-plugin/tests/configs.test.ts +++ b/packages/eslint-plugin/tests/configs.test.ts @@ -23,7 +23,9 @@ function entriesToObject(value: [string, T][]): Record { }, {}); } -function filterRules(values: Record): [string, string][] { +function filterRules( + values: Record, +): [string, string | unknown[]][] { return Object.entries(values).filter(([name]) => name.startsWith(RULE_NAME_PREFIX), ); @@ -39,7 +41,7 @@ function filterAndMapRuleConfigs({ excludeDeprecated, typeChecked, recommendations, -}: FilterAndMapRuleConfigsSettings = {}): [string, string][] { +}: FilterAndMapRuleConfigsSettings = {}): [string, unknown][] { let result = Object.entries(rules); if (excludeDeprecated) { @@ -55,16 +57,41 @@ function filterAndMapRuleConfigs({ } if (recommendations) { - result = result.filter(([, rule]) => - recommendations.includes(rule.meta.docs?.recommended), - ); + result = result.filter(([, rule]) => { + switch (typeof rule.meta.docs?.recommended) { + case 'undefined': + return false; + case 'object': + return Object.keys(rule.meta.docs.recommended).some(recommended => + recommendations.includes(recommended as RuleRecommendation), + ); + case 'string': + return recommendations.includes(rule.meta.docs.recommended); + } + }); } - return result.map(([name]) => [`${RULE_NAME_PREFIX}${name}`, 'error']); + const highestRecommendation = recommendations?.filter(Boolean).at(-1); + + return result.map(([name, rule]) => { + const customRecommendation = + highestRecommendation && + typeof rule.meta.docs?.recommended === 'object' && + rule.meta.docs.recommended[ + highestRecommendation as 'recommended' | 'strict' + ]; + + return [ + `${RULE_NAME_PREFIX}${name}`, + customRecommendation && typeof customRecommendation !== 'boolean' + ? ['error', customRecommendation[0]] + : 'error', + ]; + }); } function itHasBaseRulesOverriden( - unfilteredConfigRules: Record, + unfilteredConfigRules: Record, ): void { it('has the base rules overriden by the appropriate extension rules', () => { const ruleNames = new Set(Object.keys(unfilteredConfigRules)); @@ -166,7 +193,7 @@ describe('recommended-type-checked-only.ts', () => { }); describe('strict.ts', () => { - const unfilteredConfigRules: Record = + const unfilteredConfigRules: Record = plugin.configs.strict.rules; it('contains all strict rules, excluding type checked ones', () => { @@ -185,7 +212,7 @@ describe('strict.ts', () => { }); describe('strict-type-checked.ts', () => { - const unfilteredConfigRules: Record = + const unfilteredConfigRules: Record = plugin.configs['strict-type-checked'].rules; it('contains all strict rules', () => { @@ -202,7 +229,7 @@ describe('strict-type-checked.ts', () => { }); describe('strict-type-checked-only.ts', () => { - const unfilteredConfigRules: Record = + const unfilteredConfigRules: Record = plugin.configs['strict-type-checked-only'].rules; it('contains only type-checked strict rules', () => { @@ -221,7 +248,7 @@ describe('strict-type-checked-only.ts', () => { }); describe('stylistic.ts', () => { - const unfilteredConfigRules: Record = + const unfilteredConfigRules: Record = plugin.configs.stylistic.rules; it('contains all stylistic rules, excluding deprecated or type checked ones', () => { diff --git a/packages/repo-tools/src/generate-configs.mts b/packages/repo-tools/src/generate-configs.mts index 870b60c6de07..05ba57b30706 100644 --- a/packages/repo-tools/src/generate-configs.mts +++ b/packages/repo-tools/src/generate-configs.mts @@ -61,7 +61,10 @@ async function main(): Promise { config: PRETTIER_CONFIG_PATH, }); - type LinterConfigRules = Record; + type LinterConfigRules = Record< + string, + ClassicConfig.RuleLevel | [ClassicConfig.RuleLevel, ...unknown[]] + >; interface LinterConfig extends ClassicConfig.Config { extends?: string[] | string; @@ -93,8 +96,13 @@ async function main(): Promise { (a, b) => a[0].localeCompare(b[0]), ); - interface RuleFilter { + type GetRuleOptions = ( + rule: RuleModule, + ) => true | readonly unknown[] | undefined; + + interface ConfigRuleSettings { deprecated?: 'exclude'; + getOptions?: GetRuleOptions | undefined; typeChecked?: 'exclude' | 'include-only'; baseRuleForExtensionRule?: 'exclude'; forcedRuleLevel?: Linter.RuleLevel; @@ -106,7 +114,7 @@ async function main(): Promise { function reducer( config: LinterConfigRules, [key, value]: RuleEntry, - settings: RuleFilter = {}, + settings: ConfigRuleSettings = {}, ): LinterConfigRules { if (settings.deprecated && value.meta.deprecated) { return config; @@ -149,7 +157,14 @@ async function main(): Promise { '=', chalk.red('error'), ); - config[ruleName] = settings.forcedRuleLevel ?? 'error'; + + const ruleLevel = settings.forcedRuleLevel ?? 'error'; + const ruleOptions = settings.getOptions?.(value); + + config[ruleName] = + ruleOptions && ruleOptions !== true + ? [ruleLevel, ...ruleOptions] + : ruleLevel; return config; } @@ -247,20 +262,20 @@ async function main(): Promise { interface ExtendedConfigSettings { name: string; - filters?: RuleFilter; ruleEntries: readonly RuleEntry[]; + settings?: ConfigRuleSettings; } async function writeExtendedConfig({ - filters, name, ruleEntries, + settings, }: ExtendedConfigSettings): Promise { await writeConfig( () => ({ extends: [...CLASSIC_EXTENDS], rules: ruleEntries.reduce( - (config, entry) => reducer(config, entry, filters), + (config, entry) => reducer(config, entry, settings), {}, ), }), @@ -272,20 +287,34 @@ async function main(): Promise { ...recommendations: (RuleRecommendation | undefined)[] ): RuleEntry[] { return allRuleEntries.filter(([, rule]) => - recommendations.includes(rule.meta.docs?.recommended), + typeof rule.meta.docs?.recommended === 'object' + ? Object.keys(rule.meta.docs.recommended).some(level => + recommendations.includes(level as RuleRecommendation), + ) + : recommendations.includes(rule.meta.docs?.recommended), ); } + function createGetOptionsForLevel( + level: 'recommended' | 'strict', + ): GetRuleOptions { + return rule => + typeof rule.meta.docs?.recommended === 'object' + ? rule.meta.docs.recommended[level] + : undefined; + } + await writeExtendedConfig({ name: 'all', - filters: { + settings: { deprecated: 'exclude', }, ruleEntries: allRuleEntries, }); await writeExtendedConfig({ - filters: { + settings: { + getOptions: createGetOptionsForLevel('recommended'), typeChecked: 'exclude', }, name: 'recommended', @@ -295,10 +324,14 @@ async function main(): Promise { await writeExtendedConfig({ name: 'recommended-type-checked', ruleEntries: filterRuleEntriesTo('recommended'), + settings: { + getOptions: createGetOptionsForLevel('recommended'), + }, }); await writeExtendedConfig({ - filters: { + settings: { + getOptions: createGetOptionsForLevel('recommended'), typeChecked: 'include-only', }, name: 'recommended-type-checked-only', @@ -306,7 +339,8 @@ async function main(): Promise { }); await writeExtendedConfig({ - filters: { + settings: { + getOptions: createGetOptionsForLevel('strict'), typeChecked: 'exclude', }, name: 'strict', @@ -314,12 +348,16 @@ async function main(): Promise { }); await writeExtendedConfig({ + settings: { + getOptions: createGetOptionsForLevel('strict'), + }, name: 'strict-type-checked', ruleEntries: filterRuleEntriesTo('recommended', 'strict'), }); await writeExtendedConfig({ - filters: { + settings: { + getOptions: createGetOptionsForLevel('strict'), typeChecked: 'include-only', }, name: 'strict-type-checked-only', @@ -327,7 +365,7 @@ async function main(): Promise { }); await writeExtendedConfig({ - filters: { + settings: { typeChecked: 'exclude', }, name: 'stylistic', @@ -340,7 +378,7 @@ async function main(): Promise { }); await writeExtendedConfig({ - filters: { + settings: { typeChecked: 'include-only', }, name: 'stylistic-type-checked-only', diff --git a/packages/repo-tools/src/postinstall.mts b/packages/repo-tools/src/postinstall.mts index 5facbf0defc9..b89cb17cc867 100644 --- a/packages/repo-tools/src/postinstall.mts +++ b/packages/repo-tools/src/postinstall.mts @@ -20,7 +20,8 @@ if (process.env.SKIP_POSTINSTALL) { process.exit(0); } -void (async function (): Promise { +// eslint-disable-next-line @typescript-eslint/no-floating-promises +(async function (): Promise { // make sure we're running from the workspace root const { default: { workspaceRoot }, diff --git a/packages/rule-tester/src/utils/config-validator.ts b/packages/rule-tester/src/utils/config-validator.ts index ac90b8c21eea..ff616a0e57de 100644 --- a/packages/rule-tester/src/utils/config-validator.ts +++ b/packages/rule-tester/src/utils/config-validator.ts @@ -78,7 +78,8 @@ function validateRuleSchema( const validateRule = ruleValidators.get(rule); if (validateRule) { - void validateRule(localOptions); + // eslint-disable-next-line @typescript-eslint/no-floating-promises + validateRule(localOptions); if (validateRule.errors) { throw new Error( validateRule.errors diff --git a/packages/typescript-eslint/src/configs/strict-type-checked-only.ts b/packages/typescript-eslint/src/configs/strict-type-checked-only.ts index e1f60b92e692..034ac9bf6c6f 100644 --- a/packages/typescript-eslint/src/configs/strict-type-checked-only.ts +++ b/packages/typescript-eslint/src/configs/strict-type-checked-only.ts @@ -23,7 +23,10 @@ export default ( '@typescript-eslint/no-base-to-string': 'error', '@typescript-eslint/no-confusing-void-expression': 'error', '@typescript-eslint/no-duplicate-type-constituents': 'error', - '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-floating-promises': [ + 'error', + { ignoreVoid: false }, + ], '@typescript-eslint/no-for-in-array': 'error', 'no-implied-eval': 'off', '@typescript-eslint/no-implied-eval': 'error', @@ -51,8 +54,27 @@ export default ( '@typescript-eslint/prefer-return-this-type': 'error', 'require-await': 'off', '@typescript-eslint/require-await': 'error', - '@typescript-eslint/restrict-plus-operands': 'error', - '@typescript-eslint/restrict-template-expressions': 'error', + '@typescript-eslint/restrict-plus-operands': [ + 'error', + { + allowAny: false, + allowBoolean: false, + allowNullish: false, + allowNumberAndString: false, + allowRegExp: false, + }, + ], + '@typescript-eslint/restrict-template-expressions': [ + 'error', + { + allowAny: false, + allowBoolean: false, + allowNullish: false, + allowNumber: false, + allowRegExp: false, + allowNever: false, + }, + ], '@typescript-eslint/unbound-method': 'error', }, }, diff --git a/packages/typescript-eslint/src/configs/strict-type-checked.ts b/packages/typescript-eslint/src/configs/strict-type-checked.ts index 91abadd4b563..14d30d005440 100644 --- a/packages/typescript-eslint/src/configs/strict-type-checked.ts +++ b/packages/typescript-eslint/src/configs/strict-type-checked.ts @@ -19,7 +19,10 @@ export default ( { rules: { '@typescript-eslint/await-thenable': 'error', - '@typescript-eslint/ban-ts-comment': 'error', + '@typescript-eslint/ban-ts-comment': [ + 'error', + { minimumDescriptionLength: 10 }, + ], '@typescript-eslint/ban-types': 'error', 'no-array-constructor': 'off', '@typescript-eslint/no-array-constructor': 'error', @@ -32,7 +35,10 @@ export default ( '@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/no-extra-non-null-assertion': 'error', '@typescript-eslint/no-extraneous-class': 'error', - '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-floating-promises': [ + 'error', + { ignoreVoid: false }, + ], '@typescript-eslint/no-for-in-array': 'error', 'no-implied-eval': 'off', '@typescript-eslint/no-implied-eval': 'error', @@ -79,8 +85,27 @@ export default ( '@typescript-eslint/prefer-ts-expect-error': 'error', 'require-await': 'off', '@typescript-eslint/require-await': 'error', - '@typescript-eslint/restrict-plus-operands': 'error', - '@typescript-eslint/restrict-template-expressions': 'error', + '@typescript-eslint/restrict-plus-operands': [ + 'error', + { + allowAny: false, + allowBoolean: false, + allowNullish: false, + allowNumberAndString: false, + allowRegExp: false, + }, + ], + '@typescript-eslint/restrict-template-expressions': [ + 'error', + { + allowAny: false, + allowBoolean: false, + allowNullish: false, + allowNumber: false, + allowRegExp: false, + allowNever: false, + }, + ], '@typescript-eslint/triple-slash-reference': 'error', '@typescript-eslint/unbound-method': 'error', '@typescript-eslint/unified-signatures': 'error', diff --git a/packages/typescript-eslint/src/configs/strict.ts b/packages/typescript-eslint/src/configs/strict.ts index c1eb5e29cf3d..dabfa2f78a92 100644 --- a/packages/typescript-eslint/src/configs/strict.ts +++ b/packages/typescript-eslint/src/configs/strict.ts @@ -18,7 +18,10 @@ export default ( eslintRecommendedConfig(plugin, parser), { rules: { - '@typescript-eslint/ban-ts-comment': 'error', + '@typescript-eslint/ban-ts-comment': [ + 'error', + { minimumDescriptionLength: 10 }, + ], '@typescript-eslint/ban-types': 'error', 'no-array-constructor': 'off', '@typescript-eslint/no-array-constructor': 'error', diff --git a/packages/typescript-eslint/tests/configs.test.ts b/packages/typescript-eslint/tests/configs.test.ts index 28c2f5a337e1..6d8add503b4b 100644 --- a/packages/typescript-eslint/tests/configs.test.ts +++ b/packages/typescript-eslint/tests/configs.test.ts @@ -45,7 +45,7 @@ function filterAndMapRuleConfigs({ excludeDeprecated, typeChecked, recommendations, -}: FilterAndMapRuleConfigsSettings = {}): [string, string][] { +}: FilterAndMapRuleConfigsSettings = {}): [string, unknown][] { let result = Object.entries(rules); if (excludeDeprecated) { @@ -61,12 +61,37 @@ function filterAndMapRuleConfigs({ } if (recommendations) { - result = result.filter(([, rule]) => - recommendations.includes(rule.meta.docs?.recommended), - ); + result = result.filter(([, rule]) => { + switch (typeof rule.meta.docs?.recommended) { + case 'undefined': + return false; + case 'object': + return Object.keys(rule.meta.docs.recommended).some(recommended => + recommendations.includes(recommended as RuleRecommendation), + ); + case 'string': + return recommendations.includes(rule.meta.docs.recommended); + } + }); } - return result.map(([name]) => [`${RULE_NAME_PREFIX}${name}`, 'error']); + const highestRecommendation = recommendations?.filter(Boolean).at(-1); + + return result.map(([name, rule]) => { + const customRecommendation = + highestRecommendation && + typeof rule.meta.docs?.recommended === 'object' && + rule.meta.docs.recommended[ + highestRecommendation as 'recommended' | 'strict' + ]; + + return [ + `${RULE_NAME_PREFIX}${name}`, + customRecommendation && typeof customRecommendation !== 'boolean' + ? ['error', customRecommendation[0]] + : 'error', + ]; + }); } function itHasBaseRulesOverriden( diff --git a/packages/utils/src/eslint-utils/RuleCreator.ts b/packages/utils/src/eslint-utils/RuleCreator.ts index 40ebe7f49120..784585415f05 100644 --- a/packages/utils/src/eslint-utils/RuleCreator.ts +++ b/packages/utils/src/eslint-utils/RuleCreator.ts @@ -10,12 +10,15 @@ import { applyDefault } from './applyDefault'; export type { RuleListener, RuleModule }; // we automatically add the url -export type NamedCreateRuleMetaDocs = Omit; -export type NamedCreateRuleMeta = Omit< - RuleMetaData, - 'docs' -> & { - docs: NamedCreateRuleMetaDocs; +export type NamedCreateRuleMetaDocs = Omit< + RuleMetaDataDocs, + 'url' +>; +export type NamedCreateRuleMeta< + MessageIds extends string, + Options extends readonly unknown[], +> = Omit, 'docs'> & { + docs: NamedCreateRuleMetaDocs; }; export interface RuleCreateAndOptions< @@ -33,14 +36,14 @@ export interface RuleWithMeta< Options extends readonly unknown[], MessageIds extends string, > extends RuleCreateAndOptions { - meta: RuleMetaData; + meta: RuleMetaData; } export interface RuleWithMetaAndName< Options extends readonly unknown[], MessageIds extends string, > extends RuleCreateAndOptions { - meta: NamedCreateRuleMeta; + meta: NamedCreateRuleMeta; name: string; } diff --git a/packages/utils/src/ts-eslint/Rule.ts b/packages/utils/src/ts-eslint/Rule.ts index 3c654cedc64e..c0a3678a00cc 100644 --- a/packages/utils/src/ts-eslint/Rule.ts +++ b/packages/utils/src/ts-eslint/Rule.ts @@ -8,7 +8,14 @@ import type { SourceCode } from './SourceCode'; export type RuleRecommendation = 'recommended' | 'strict' | 'stylistic'; -export interface RuleMetaDataDocs { +export interface RuleRecommendationAcrossConfigs< + Options extends readonly unknown[], +> { + recommended: true; + strict: Partial; +} + +export interface RuleMetaDataDocs { /** * Concise description of the rule */ @@ -18,7 +25,7 @@ export interface RuleMetaDataDocs { * Used by the build tools to generate the recommended and strict configs. * Exclude to not include it as a recommendation. */ - recommended?: RuleRecommendation; + recommended?: RuleRecommendation | RuleRecommendationAcrossConfigs; /** * The URL of the rule's docs */ @@ -36,7 +43,10 @@ export interface RuleMetaDataDocs { extendsBaseRule?: boolean | string; } -export interface RuleMetaData { +export interface RuleMetaData< + MessageIds extends string, + Options extends readonly unknown[], +> { /** * True if the rule is deprecated, false otherwise */ @@ -44,7 +54,7 @@ export interface RuleMetaData { /** * Documentation for the rule, unnecessary for custom rules/plugins */ - docs?: RuleMetaDataDocs; + docs?: RuleMetaDataDocs; /** * The fixer category. Omit if there is no fixer */ @@ -630,7 +640,7 @@ export interface RuleModule< /** * Metadata about the rule */ - meta: RuleMetaData; + meta: RuleMetaData; /** * Function which returns an object with methods that ESLint calls to “visit” diff --git a/packages/website/plugins/generated-rule-docs/utils.ts b/packages/website/plugins/generated-rule-docs/utils.ts index e26ce248dbff..d8a2d61aad1d 100644 --- a/packages/website/plugins/generated-rule-docs/utils.ts +++ b/packages/website/plugins/generated-rule-docs/utils.ts @@ -55,8 +55,8 @@ export function getUrlForRuleTest(ruleName: string): string { throw new Error(`Could not find test file for ${ruleName}.`); } -export type RuleMetaDataWithDocs = RuleMetaData & { - docs: RuleMetaDataDocs; +export type RuleMetaDataWithDocs = RuleMetaData & { + docs: RuleMetaDataDocs; }; export type RuleModuleWithMetaDocs = RuleModule & { diff --git a/packages/website/src/components/RulesTable/index.tsx b/packages/website/src/components/RulesTable/index.tsx index 9a1235079303..dec57b97a50f 100644 --- a/packages/website/src/components/RulesTable/index.tsx +++ b/packages/website/src/components/RulesTable/index.tsx @@ -41,6 +41,8 @@ function RuleRow({ } const { fixable, hasSuggestions, type, deprecated } = rule; const { recommended, requiresTypeChecking, extendsBaseRule } = rule.docs; + const actualRecommended = + typeof recommended === 'object' ? 'recommended' : recommended; const formatting = type === 'layout'; return ( @@ -51,9 +53,9 @@ function RuleRow({
{interpolateCode(rule.docs.description)} - + {(() => { - switch (recommended) { + switch (actualRecommended) { case 'recommended': return RECOMMENDED_CONFIG_EMOJI; case 'strict': diff --git a/packages/website/src/hooks/useClipboard.ts b/packages/website/src/hooks/useClipboard.ts index c97ecf383141..d8191784f8e2 100644 --- a/packages/website/src/hooks/useClipboard.ts +++ b/packages/website/src/hooks/useClipboard.ts @@ -8,7 +8,8 @@ export function useClipboard(code: () => string): useClipboardResult { const [copied, setCopied] = useDebouncedToggle(false); const copy = useCallback(() => { - void navigator.clipboard.writeText(code()).then(() => { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + navigator.clipboard.writeText(code()).then(() => { setCopied(true); }); }, [setCopied, code]); diff --git a/packages/website/src/theme/MDXComponents/RuleAttributes.tsx b/packages/website/src/theme/MDXComponents/RuleAttributes.tsx index 53da1d3fdc42..7950da8b397b 100644 --- a/packages/website/src/theme/MDXComponents/RuleAttributes.tsx +++ b/packages/website/src/theme/MDXComponents/RuleAttributes.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import Link from '@docusaurus/Link'; import type { RuleMetaDataDocs } from '@site/../utils/dist/ts-eslint/Rule'; import { useRulesMeta } from '@site/src/hooks/useRulesMeta'; @@ -20,14 +21,25 @@ const recommendations = { stylistic: [STYLISTIC_CONFIG_EMOJI, 'stylistic'], }; -type RecommendedRuleMetaDataDocs = RuleMetaDataDocs & { recommended: string }; +type MakeRequired = Omit & { + [K in Key]-?: NonNullable; +}; + +type RecommendedRuleMetaDataDocs = + MakeRequired, 'recommended'>; const isRecommendedDocs = ( - docs: RuleMetaDataDocs, -): docs is RecommendedRuleMetaDataDocs => !!docs.recommended; + docs: RuleMetaDataDocs, +): docs is RecommendedRuleMetaDataDocs => !!docs.recommended; -const getRecommendation = (docs: RecommendedRuleMetaDataDocs): string[] => { - const recommendation = recommendations[docs.recommended]; +const getRecommendation = ( + docs: RecommendedRuleMetaDataDocs, +): string[] => { + const recommended = docs.recommended; + const recommendation = + recommendations[ + typeof recommended === 'object' ? 'recommended' : recommended + ]; return docs.requiresTypeChecking ? [recommendation[0], `${recommendation[1]}-type-checked`] 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