From 11c066ccc583f808c3af640ce8ab52caa7923fca Mon Sep 17 00:00:00 2001 From: Bastien Chanez Date: Sat, 12 Oct 2024 10:37:47 +0200 Subject: [PATCH 1/6] test(no-important): add tests for rule !important is used --- src/rules/rrd/noImportant.test.ts | 85 +++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/rules/rrd/noImportant.test.ts diff --git a/src/rules/rrd/noImportant.test.ts b/src/rules/rrd/noImportant.test.ts new file mode 100644 index 00000000..1764d786 --- /dev/null +++ b/src/rules/rrd/noImportant.test.ts @@ -0,0 +1,85 @@ +import type { SFCTemplateBlock } from '@vue/compiler-sfc' +import { describe, expect, it } from 'vitest' +import { checkNoImportant, reportNoImportant } from './noImportant' + +describe('importantUsed', () => { + it('should not report templates when there is no use of !important', () => { + const template = { + content: ` + + + `, + } as SFCTemplateBlock + const filename = 'no-important.vue' + checkNoImportant(template, filename) + const result = reportNoImportant() + expect(result.length).toBe(0) + expect(result).toStrictEqual([]) + }) + + it('should not report css files when there is no use of !important', () => { + const template = { + content: ` + div { + color: blue; + } + `, + } as SFCTemplateBlock + const filename = 'no-important.css' + checkNoImportant(template, filename) + const result = reportNoImportant() + expect(result.length).toBe(0) + expect(result).toStrictEqual([]) + }) + + it('should report templates when there is use of !important', () => { + const template = { + content: ` + + + `, + } as SFCTemplateBlock + const filename = 'no-important.vue' + checkNoImportant(template, filename) + const result = reportNoImportant() + expect(result.length).toBe(1) + expect(result).toStrictEqual([{ + file: filename, + rule: `rdd ~ no !important`, + description: `👉 Avoid !important as it complicates CSS management and disrupts natural cascading.`, + message: `Found !important 🚨`, + }]) + }) + + it('should report css files when there is use of !important', () => { + const template = { + content: ` + div { + color: blue !important; + } + `, + } as SFCTemplateBlock + const filename = 'no-important.css' + checkNoImportant(template, filename) + const result = reportNoImportant() + expect(result.length).toBe(1) + expect(result).toStrictEqual([{ + file: filename, + rule: `rdd ~ no !important`, + description: `👉 Avoid !important as it complicates CSS management and disrupts natural cascading.`, + message: `Found !important 🚨`, + }]) + }) +}) From 651854d00b7b0a7ceacdc5ec86da7f2e6a6c7655 Mon Sep 17 00:00:00 2001 From: Bastien Chanez Date: Sat, 12 Oct 2024 10:52:53 +0200 Subject: [PATCH 2/6] test(no-important): correct tests --- src/rules/rrd/noImportant.test.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/rules/rrd/noImportant.test.ts b/src/rules/rrd/noImportant.test.ts index 1764d786..ba38535b 100644 --- a/src/rules/rrd/noImportant.test.ts +++ b/src/rules/rrd/noImportant.test.ts @@ -54,12 +54,17 @@ describe('importantUsed', () => { const filename = 'no-important.vue' checkNoImportant(template, filename) const result = reportNoImportant() - expect(result.length).toBe(1) + expect(result.length).toBe(2) expect(result).toStrictEqual([{ file: filename, rule: `rdd ~ no !important`, description: `👉 Avoid !important as it complicates CSS management and disrupts natural cascading.`, - message: `Found !important 🚨`, + message: `line #${2} Found !important 🚨`, + }, { + file: filename, + rule: `rdd ~ no !important`, + description: `👉 Avoid !important as it complicates CSS management and disrupts natural cascading.`, + message: `line #${6} Found !important 🚨`, }]) }) @@ -79,7 +84,7 @@ describe('importantUsed', () => { file: filename, rule: `rdd ~ no !important`, description: `👉 Avoid !important as it complicates CSS management and disrupts natural cascading.`, - message: `Found !important 🚨`, + message: `line #${2} Found !important 🚨`, }]) }) }) From 664828da4017a4db87af6b7af896df02adde559b Mon Sep 17 00:00:00 2001 From: Bastien Chanez Date: Sat, 12 Oct 2024 11:16:08 +0200 Subject: [PATCH 3/6] docs(no-important): add documentation for the no !important rule --- docs/.vitepress/config.ts | 1 + docs/rules/rrd/index.md | 1 + docs/rules/rrd/no-important.md | 65 ++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 docs/rules/rrd/no-important.md diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index ede64f86..86e18cd8 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -83,6 +83,7 @@ export default defineConfig({ { text: 'If Without Curly Braces', link: '/rules/rrd/if-without-curly-braces' }, { text: 'Magic Numbers', link: '/rules/rrd/magic-numbers' }, { text: 'Nested Ternary', link: '/rules/rrd/nested-ternary' }, + { text: 'No !important', link: '/rules/rrd/no-important' }, { text: 'No Direct Dom Access', link: '/rules/rrd/no-direct-dom-access' }, { text: 'No Inline Styles', link: '/rules/rrd/no-inline-styles' }, { text: 'No Props Destructure', link: '/rules/rrd/no-prop-destructure' }, diff --git a/docs/rules/rrd/index.md b/docs/rules/rrd/index.md index aec754e8..7972cf5b 100644 --- a/docs/rules/rrd/index.md +++ b/docs/rules/rrd/index.md @@ -17,6 +17,7 @@ These ruleset is the most opinionated with rules that are not part of the _offic - [If Without Curly Braces](./if-without-curly-braces.md) - [Magic Numbers](./magic-numbers.md) - [Nested Ternary](./nested-ternary.md) +- [No !Important](./no-important.md) - [No Direct Dom Access](./no-direct-dom-access.md) - [No Inline Styles](./no-inline-styles.md) - [No Prop Destructing](./no-prop-destructure.md) diff --git a/docs/rules/rrd/no-important.md b/docs/rules/rrd/no-important.md new file mode 100644 index 00000000..6c4e0115 --- /dev/null +++ b/docs/rules/rrd/no-important.md @@ -0,0 +1,65 @@ +# No Direct Dom Access + +Checks if a component's styles are using the `!important` declaration to enforce styles. + +## ❓ Why it's good to follow this rule? + +Direct DOM manipulation in Vue components can lead to several issues: + +Using `!important` in CSS can lead to several issues: + +1. It complicates the cascade of styles, making it harder to understand which styles are being applied. +2. It increases the specificity of rules, leading to maintenance challenges when updating styles. +3. It can make debugging more difficult, as the usual cascade and inheritance rules of CSS are bypassed. +4. It promotes poor coding practices by encouraging developers to rely on `!important` rather than properly managing specificity. + +## 😱 Examples of code for which this rule will throw a warning + +::: warning +The following code contains the use of `!important`, which is discouraged: +::: + + +```vue + + + +``` + +## 🤩 How to fix it? + +::: tip +Refactor your CSS to avoid using `!important` by managing specificity through proper selectors. Here's how to fix the previous examples: +::: + +```vue + + + +``` + +In this fixed example: + +1. We removed the `!important` declaration from the `.button` class. +2. We created a more specific selector (`.special-button`) to override styles when necessary. +3. By relying on proper specificity and structure, we maintain clearer and more manageable CSS. + +This approach enhances maintainability, readability, and performance while ensuring that styles are applied correctly without unnecessary overrides. \ No newline at end of file From 7dbcf4b17c3212bc7915d018f3a8e1bfa7cbafd0 Mon Sep 17 00:00:00 2001 From: Bastien Chanez Date: Sat, 12 Oct 2024 11:16:48 +0200 Subject: [PATCH 4/6] feat(no-important): add rule --- src/rules/rrd/index.ts | 1 + src/rules/rrd/noImportant.ts | 44 ++++++++++++++++++++++++++++++++++++ src/rules/rules.ts | 1 + src/rulesCheck.ts | 3 ++- src/rulesReport.ts | 3 ++- 5 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 src/rules/rrd/noImportant.ts diff --git a/src/rules/rrd/index.ts b/src/rules/rrd/index.ts index 000454df..38b7f70c 100644 --- a/src/rules/rrd/index.ts +++ b/src/rules/rrd/index.ts @@ -14,6 +14,7 @@ export * from './ifWithoutCurlyBraces' export * from './magicNumbers' export * from './nestedTernary' export * from './noDirectDomAccess' +export * from './noImportant' export * from './noInlineStyles' export * from './noPropDestructure' export * from './noSkippedTests' diff --git a/src/rules/rrd/noImportant.ts b/src/rules/rrd/noImportant.ts new file mode 100644 index 00000000..a0d1d2e5 --- /dev/null +++ b/src/rules/rrd/noImportant.ts @@ -0,0 +1,44 @@ +import type { SFCTemplateBlock } from '@vue/compiler-sfc' +import type { FileCheckResult, Offense } from '../../types' +import getLineNumber from '../getLineNumber' + +const results: FileCheckResult[] = [] + +const resetResults = () => (results.length = 0) + +const checkNoImportant = (template: SFCTemplateBlock | null, filePath: string) => { + if (!template) { + return + } + + const regex = /!important/g + + const matches = [...template.content.matchAll(regex)] + let from = 0 + matches.forEach((match) => { + const lineNumber = getLineNumber(template.content.trim(), match[0], from) + results.push({ filePath, message: `line #${lineNumber} Found !important` }) + from = lineNumber + }) +} + +const reportNoImportant = () => { + const offenses: Offense[] = [] + + if (results.length > 0) { + results.forEach((result) => { + offenses.push({ + file: result.filePath, + rule: `rdd ~ no !important`, + description: `👉 Avoid !important as it complicates CSS management and disrupts natural cascading.`, + message: `${result.message} 🚨`, + }) + }) + } + + resetResults() + + return offenses +} + +export { checkNoImportant, reportNoImportant } diff --git a/src/rules/rules.ts b/src/rules/rules.ts index dd793d48..0c21ddd5 100644 --- a/src/rules/rules.ts +++ b/src/rules/rules.ts @@ -43,6 +43,7 @@ export const RULES = { 'magicNumbers', 'nestedTernary', 'noDirectDomAccess', + 'noImportant', 'noInlineStyles', 'noPropDestructure', 'noSkippedTests', diff --git a/src/rulesCheck.ts b/src/rulesCheck.ts index 1020d505..4ec34d23 100644 --- a/src/rulesCheck.ts +++ b/src/rulesCheck.ts @@ -1,7 +1,7 @@ import type { SFCDescriptor } from '@vue/compiler-sfc' import type { OverrideConfig } from './types/Override' import { getHasServer, getIsNuxt } from './context' -import { checkAmountOfComments, checkBigVif, checkBigVshow, checkComplicatedConditions, checkComputedSideEffects, checkCyclomaticComplexity, checkDeepIndentation, checkElseCondition, checkFunctionSize, checkHtmlImageElements, checkHtmlLink, checkHugeFiles, checkIfWithoutCurlyBraces, checkMagicNumbers, checkNestedTernary, checkNoDirectDomAccess, checkNoInlineStyles, checkNoPropDestructure, checkNoSkippedTests, checkNoTsLang, checkNoVarDeclaration, checkParameterCount, checkPlainScript, checkPropsDrilling, checkScriptLength, checkShortVariableName, checkTooManyProps, checkVForExpression, checkVForWithIndexKey, checkZeroLengthComparison } from './rules/rrd' +import { checkAmountOfComments, checkBigVif, checkBigVshow, checkComplicatedConditions, checkComputedSideEffects, checkCyclomaticComplexity, checkDeepIndentation, checkElseCondition, checkFunctionSize, checkHtmlImageElements, checkHtmlLink, checkHugeFiles, checkIfWithoutCurlyBraces, checkMagicNumbers, checkNestedTernary, checkNoDirectDomAccess, checkNoImportant, checkNoInlineStyles, checkNoPropDestructure, checkNoSkippedTests, checkNoTsLang, checkNoVarDeclaration, checkParameterCount, checkPlainScript, checkPropsDrilling, checkScriptLength, checkShortVariableName, checkTooManyProps, checkVForExpression, checkVForWithIndexKey, checkZeroLengthComparison } from './rules/rrd' import { checkApiWithoutMethod, checkRateLimiter } from './rules/security' import { checkElementSelectorsWithScoped, checkImplicitParentChildCommunication } from './rules/vue-caution' import { checkGlobalStyle, checkSimpleProp, checkSingleNameComponent, checkVforNoKey, checkVifWithVfor } from './rules/vue-essential' @@ -60,6 +60,7 @@ export const checkRules = (descriptor: SFCDescriptor, filePath: string, apply: s magicNumbers: () => checkMagicNumbers(script, filePath), nestedTernary: () => checkNestedTernary(script, filePath), noDirectDomAccess: () => checkNoDirectDomAccess(script, filePath), + noImportant: () => checkNoImportant(descriptor.template, filePath), noInlineStyles: () => checkNoInlineStyles(descriptor.template, filePath), noPropDestructure: () => checkNoPropDestructure(script, filePath), noSkippedTests: () => checkNoSkippedTests(script, filePath), diff --git a/src/rulesReport.ts b/src/rulesReport.ts index f36f68e9..9c695a91 100644 --- a/src/rulesReport.ts +++ b/src/rulesReport.ts @@ -1,7 +1,7 @@ import type { GroupBy, Health, Offense, OffensesGrouped, OutputLevel, ReportFunction, SortBy } from './types' import type { OverrideConfig } from './types/Override' import type { ReportOutput } from './types/ReportOutput' -import { reportAmountOfComments, reportBigVif, reportBigVshow, reportComplicatedConditions, reportComputedSideEffects, reportCyclomaticComplexity, reportDeepIndentation, reportElseCondition, reportFunctionSize, reportHtmlImageElements, reportHtmlLink, reportHugeFiles, reportIfWithoutCurlyBraces, reportMagicNumbers, reportNestedTernary, reportNoDirectDomAccess, reportNoInlineStyles, reportNoPropDestructure, reportNoSkippedTests, reportNoTsLang, reportNoVarDeclaration, reportParameterCount, reportPlainScript, reportPropsDrilling, reportScriptLength, reportShortVariableName, reportTooManyProps, reportVForExpression, reportVForWithIndexKey, reportZeroLengthComparison } from './rules/rrd' +import { reportAmountOfComments, reportBigVif, reportBigVshow, reportComplicatedConditions, reportComputedSideEffects, reportCyclomaticComplexity, reportDeepIndentation, reportElseCondition, reportFunctionSize, reportHtmlImageElements, reportHtmlLink, reportHugeFiles, reportIfWithoutCurlyBraces, reportMagicNumbers, reportNestedTernary, reportNoDirectDomAccess, reportNoImportant, reportNoInlineStyles, reportNoPropDestructure, reportNoSkippedTests, reportNoTsLang, reportNoVarDeclaration, reportParameterCount, reportPlainScript, reportPropsDrilling, reportScriptLength, reportShortVariableName, reportTooManyProps, reportVForExpression, reportVForWithIndexKey, reportZeroLengthComparison } from './rules/rrd' import { reportApiWithoutMethod, reportRateLimiter } from './rules/security' import { reportElementSelectorsWithScoped, reportImplicitParentChildCommunication } from './rules/vue-caution' import { reportGlobalStyle, reportSimpleProp, reportSingleNameComponent, reportVforNoKey, reportVifWithVfor } from './rules/vue-essential' @@ -75,6 +75,7 @@ export const reportRules = (groupBy: GroupBy, sortBy: SortBy, level: OutputLevel processOffenses(reportMagicNumbers) processOffenses(reportNestedTernary) processOffenses(reportNoDirectDomAccess) + processOffenses(reportNoImportant) processOffenses(reportNoInlineStyles) processOffenses(reportNoPropDestructure) processOffenses(reportNoSkippedTests) From 566890f0465436bc755354c9a40e1fde911ac13f Mon Sep 17 00:00:00 2001 From: rrd Date: Mon, 14 Oct 2024 15:23:24 +0200 Subject: [PATCH 5/6] Update docs/rules/rrd/no-important.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Peña Avila <57303087+David-Pena@users.noreply.github.com> --- docs/rules/rrd/no-important.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/rrd/no-important.md b/docs/rules/rrd/no-important.md index 6c4e0115..468d3bac 100644 --- a/docs/rules/rrd/no-important.md +++ b/docs/rules/rrd/no-important.md @@ -25,7 +25,7 @@ The following code contains the use of `!important`, which is discouraged:
Click Me
-