diff --git a/packages/eslint-plugin-template/docs/rules/no-negated-async.md b/packages/eslint-plugin-template/docs/rules/no-negated-async.md index bc6f78b23..b36daced0 100644 --- a/packages/eslint-plugin-template/docs/rules/no-negated-async.md +++ b/packages/eslint-plugin-template/docs/rules/no-negated-async.md @@ -23,6 +23,12 @@ Ensures that async pipe results are not negated
+## Rationale + +Angular's async pipes emit null initially, prior to the observable emitting any values, or the promise resolving. This can cause negations, like \*ngIf="!(myConditional | async)" to thrash the layout and cause expensive side-effects like firing off XHR requests for a component which should not be shown. + +
+ ## Rule Options The rule does not have any configuration options. diff --git a/packages/eslint-plugin-template/src/rules/no-negated-async.ts b/packages/eslint-plugin-template/src/rules/no-negated-async.ts index 1769d2a23..00b977bd8 100644 --- a/packages/eslint-plugin-template/src/rules/no-negated-async.ts +++ b/packages/eslint-plugin-template/src/rules/no-negated-async.ts @@ -65,6 +65,10 @@ export default createESLintRule({ }, }); +export const RULE_DOCS_EXTENSION = { + rationale: `Angular's async pipes emit null initially, prior to the observable emitting any values, or the promise resolving. This can cause negations, like *ngIf="!(myConditional | async)" to thrash the layout and cause expensive side-effects like firing off XHR requests for a component which should not be shown.`, +}; + function getSuggestionsSchema() { return [ { messageId: 'suggestFalseComparison', textToInsert: ' === false' }, diff --git a/tools/scripts/generate-rule-docs.ts b/tools/scripts/generate-rule-docs.ts index 8ffc56727..066b48544 100644 --- a/tools/scripts/generate-rule-docs.ts +++ b/tools/scripts/generate-rule-docs.ts @@ -33,6 +33,7 @@ const testDirs = readdirSync(testDirsDir); meta: { deprecated, replacedBy, type, fixable, schema, hasSuggestions }, defaultOptions, }, + docsExtension, ruleFilePath, testCasesFilePath, } = ruleData; @@ -125,7 +126,18 @@ ${ hasSuggestions ? '- 💡 Provides suggestions on how to fix issues (https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)' : '' -} +}${ + docsExtension?.rationale + ? ` + +
+ +## Rationale + +${docsExtension.rationale} +` + : '' + }
@@ -211,6 +223,10 @@ interface RuleData { ruleConfig: TSESLint.RuleModule & { defaultOptions?: Record[]; }; + // Rules can optionally export extended documentation content (outside of ESLint's concept of "docs") + docsExtension?: { + rationale?: string; + }; valid: ExtractedTestCase[]; invalid: ExtractedTestCase[]; } @@ -230,10 +246,15 @@ async function generateAllRuleData(): Promise { // For rule sources we just import/execute the rule source file for (const ruleFile of ruleFiles) { const ruleFilePath = join(rulesDir, ruleFile.replace('.ts', '')); - const { default: ruleConfig, RULE_NAME } = await import(ruleFilePath); + const { + default: ruleConfig, + RULE_NAME, + RULE_DOCS_EXTENSION, + } = await import(ruleFilePath); ruleData[RULE_NAME] = { ruleConfig, ruleFilePath: ruleFilePath + '.ts', + docsExtension: RULE_DOCS_EXTENSION, } as RuleData; }