From 889087bc72b98a253fa4d081fe354eba53e6faad Mon Sep 17 00:00:00 2001 From: thecalamiity Date: Tue, 29 Jul 2025 13:10:58 +0300 Subject: [PATCH 1/2] feat: add suggestion to no-important rule to remove !important flag --- src/rules/no-important.js | 26 ++++- tests/rules/no-important.test.js | 177 +++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+), 3 deletions(-) diff --git a/src/rules/no-important.js b/src/rules/no-important.js index 42071dcd..7968a77b 100644 --- a/src/rules/no-important.js +++ b/src/rules/no-important.js @@ -16,10 +16,16 @@ import { findOffsets } from "../util.js"; /** * @import { CSSRuleDefinition } from "../types.js" - * @typedef {"unexpectedImportant"} NoImportantMessageIds + * @typedef {"unexpectedImportant" | "removeImportant"} NoImportantMessageIds * @typedef {CSSRuleDefinition<{ RuleOptions: [], MessageIds: NoImportantMessageIds }>} NoImportantRuleDefinition */ +//----------------------------------------------------------------------------- +// Helpers +//----------------------------------------------------------------------------- + +const importantPattern = /!(\s|\/\*.*?\*\/)*important/iu; + //----------------------------------------------------------------------------- // Rule Definition //----------------------------------------------------------------------------- @@ -29,6 +35,8 @@ export default { meta: { type: "problem", + hasSuggestions: true, + docs: { description: "Disallow !important flags", recommended: true, @@ -37,12 +45,11 @@ export default { messages: { unexpectedImportant: "Unexpected !important flag found.", + removeImportant: "Remove !important flag.", }, }, create(context) { - const importantPattern = /!(\s|\/\*.*?\*\/)*important/iu; - return { Declaration(node) { if (node.important) { @@ -86,6 +93,19 @@ export default { }, }, messageId: "unexpectedImportant", + suggest: [ + { + messageId: "removeImportant", + fix(fixer) { + const start = + node.loc.start.offset + + importantMatch.index; + const end = + start + importantMatch[0].length; + return fixer.removeRange([start, end]); + }, + }, + ], }); } }, diff --git a/tests/rules/no-important.test.js b/tests/rules/no-important.test.js index 7761aaae..56341058 100644 --- a/tests/rules/no-important.test.js +++ b/tests/rules/no-important.test.js @@ -58,6 +58,12 @@ ruleTester.run("no-important", rule, { column: 16, endLine: 1, endColumn: 26, + suggestions: [ + { + messageId: "removeImportant", + output: "a { color: red ; }", + }, + ], }, ], }, @@ -70,6 +76,12 @@ ruleTester.run("no-important", rule, { column: 14, endLine: 1, endColumn: 24, + suggestions: [ + { + messageId: "removeImportant", + output: "a { color:red; }", + }, + ], }, ], }, @@ -82,6 +94,12 @@ ruleTester.run("no-important", rule, { column: 34, endLine: 1, endColumn: 45, + suggestions: [ + { + messageId: "removeImportant", + output: "a { padding: 10px 20px 30px 40px ; }", + }, + ], }, ], }, @@ -94,6 +112,12 @@ ruleTester.run("no-important", rule, { column: 15, endLine: 1, endColumn: 25, + suggestions: [ + { + messageId: "removeImportant", + output: "a { border: 0 ; }", + }, + ], }, ], }, @@ -106,6 +130,12 @@ ruleTester.run("no-important", rule, { column: 16, endLine: 1, endColumn: 26, + suggestions: [ + { + messageId: "removeImportant", + output: "a { color: red ; margin: 0 ! important; }", + }, + ], }, { messageId: "unexpectedImportant", @@ -113,6 +143,12 @@ ruleTester.run("no-important", rule, { column: 38, endLine: 1, endColumn: 49, + suggestions: [ + { + messageId: "removeImportant", + output: "a { color: red !important; margin: 0 ; }", + }, + ], }, ], }, @@ -130,6 +166,17 @@ ruleTester.run("no-important", rule, { column: 3, endLine: 3, endColumn: 13, + suggestions: [ + { + messageId: "removeImportant", + output: dedent` + a { + color: red + ; + } + `, + }, + ], }, ], }, @@ -147,6 +194,17 @@ ruleTester.run("no-important", rule, { column: 3, endLine: 3, endColumn: 14, + suggestions: [ + { + messageId: "removeImportant", + output: dedent` + a { + color: red + ; + } + `, + }, + ], }, ], }, @@ -159,6 +217,12 @@ ruleTester.run("no-important", rule, { column: 30, endLine: 1, endColumn: 40, + suggestions: [ + { + messageId: "removeImportant", + output: "a { color: red /* comment */ ; }", + }, + ], }, ], }, @@ -176,6 +240,17 @@ ruleTester.run("no-important", rule, { column: 3, endLine: 3, endColumn: 13, + suggestions: [ + { + messageId: "removeImportant", + output: dedent` + a { + color: red /* comment */ + ; + } + `, + }, + ], }, ], }, @@ -188,6 +263,12 @@ ruleTester.run("no-important", rule, { column: 16, endLine: 1, endColumn: 39, + suggestions: [ + { + messageId: "removeImportant", + output: "a { color: red ; }", + }, + ], }, ], }, @@ -200,6 +281,12 @@ ruleTester.run("no-important", rule, { column: 16, endLine: 1, endColumn: 41, + suggestions: [ + { + messageId: "removeImportant", + output: "a { color: red ; }", + }, + ], }, ], }, @@ -217,6 +304,17 @@ ruleTester.run("no-important", rule, { column: 3, endLine: 3, endColumn: 26, + suggestions: [ + { + messageId: "removeImportant", + output: dedent` + a { + color: red + ; + } + `, + }, + ], }, ], }, @@ -234,6 +332,17 @@ ruleTester.run("no-important", rule, { column: 3, endLine: 3, endColumn: 28, + suggestions: [ + { + messageId: "removeImportant", + output: dedent` + a { + color: red + ; + } + `, + }, + ], }, ], }, @@ -253,6 +362,19 @@ ruleTester.run("no-important", rule, { column: 3, endLine: 3, endColumn: 13, + suggestions: [ + { + messageId: "removeImportant", + output: dedent` + a { + color: red + ; + margin: 0 + ! important; + } + `, + }, + ], }, { messageId: "unexpectedImportant", @@ -260,6 +382,19 @@ ruleTester.run("no-important", rule, { column: 3, endLine: 5, endColumn: 14, + suggestions: [ + { + messageId: "removeImportant", + output: dedent` + a { + color: red + !important; + margin: 0 + ; + } + `, + }, + ], }, ], }, @@ -272,6 +407,12 @@ ruleTester.run("no-important", rule, { column: 43, endLine: 1, endColumn: 53, + suggestions: [ + { + messageId: "removeImportant", + output: "@keyframes important { from { margin: 1px ; } }", + }, + ], }, ], }, @@ -284,6 +425,12 @@ ruleTester.run("no-important", rule, { column: 51, endLine: 1, endColumn: 61, + suggestions: [ + { + messageId: "removeImportant", + output: "@-webkit-keyframes important { from { margin: 1px ; } }", + }, + ], }, ], }, @@ -296,6 +443,12 @@ ruleTester.run("no-important", rule, { column: 51, endLine: 1, endColumn: 61, + suggestions: [ + { + messageId: "removeImportant", + output: "@-WEBKIT-KEYFRAMES important { from { margin: 1px ; } }", + }, + ], }, ], }, @@ -308,6 +461,12 @@ ruleTester.run("no-important", rule, { column: 42, endLine: 1, endColumn: 52, + suggestions: [ + { + messageId: "removeImportant", + output: "@keyframes important { from { margin: 1px; } }", + }, + ], }, ], }, @@ -320,6 +479,12 @@ ruleTester.run("no-important", rule, { column: 43, endLine: 1, endColumn: 54, + suggestions: [ + { + messageId: "removeImportant", + output: "@keyframes important { from { margin: 1px ; } }", + }, + ], }, ], }, @@ -332,6 +497,12 @@ ruleTester.run("no-important", rule, { column: 43, endLine: 1, endColumn: 53, + suggestions: [ + { + messageId: "removeImportant", + output: "@kEyFrAmEs important { from { margin: 1px ; } }", + }, + ], }, ], }, @@ -344,6 +515,12 @@ ruleTester.run("no-important", rule, { column: 43, endLine: 1, endColumn: 53, + suggestions: [ + { + messageId: "removeImportant", + output: "@KEYFRAMES important { from { margin: 1px ; } }", + }, + ], }, ], }, From e55adb4565b0a6fd381c379fcb1bc6979a2d65a8 Mon Sep 17 00:00:00 2001 From: thecalamiity Date: Wed, 30 Jul 2025 13:13:51 +0300 Subject: [PATCH 2/2] trim whitespace before !important --- src/rules/no-important.js | 21 ++++++++-- tests/rules/no-important.test.js | 67 +++++++++++++++++++------------- 2 files changed, 57 insertions(+), 31 deletions(-) diff --git a/src/rules/no-important.js b/src/rules/no-important.js index 7968a77b..d4b79f1c 100644 --- a/src/rules/no-important.js +++ b/src/rules/no-important.js @@ -25,6 +25,7 @@ import { findOffsets } from "../util.js"; //----------------------------------------------------------------------------- const importantPattern = /!(\s|\/\*.*?\*\/)*important/iu; +const trailingWhitespacePattern = /\s*$/u; //----------------------------------------------------------------------------- // Rule Definition @@ -97,11 +98,25 @@ export default { { messageId: "removeImportant", fix(fixer) { + const importantStart = importantMatch.index; + const importantEnd = + importantStart + + importantMatch[0].length; + + // Find any trailing whitespace before the !important + const valuePart = declarationText.slice( + 0, + importantStart, + ); + const whitespaceEnd = valuePart.search( + trailingWhitespacePattern, + ); + const start = - node.loc.start.offset + - importantMatch.index; + node.loc.start.offset + whitespaceEnd; const end = - start + importantMatch[0].length; + node.loc.start.offset + importantEnd; + return fixer.removeRange([start, end]); }, }, diff --git a/tests/rules/no-important.test.js b/tests/rules/no-important.test.js index 56341058..d89875a5 100644 --- a/tests/rules/no-important.test.js +++ b/tests/rules/no-important.test.js @@ -61,7 +61,25 @@ ruleTester.run("no-important", rule, { suggestions: [ { messageId: "removeImportant", - output: "a { color: red ; }", + output: "a { color: red; }", + }, + ], + }, + ], + }, + { + code: "a { color: red !important }", + errors: [ + { + messageId: "unexpectedImportant", + line: 1, + column: 16, + endLine: 1, + endColumn: 26, + suggestions: [ + { + messageId: "removeImportant", + output: "a { color: red }", }, ], }, @@ -97,7 +115,7 @@ ruleTester.run("no-important", rule, { suggestions: [ { messageId: "removeImportant", - output: "a { padding: 10px 20px 30px 40px ; }", + output: "a { padding: 10px 20px 30px 40px; }", }, ], }, @@ -115,7 +133,7 @@ ruleTester.run("no-important", rule, { suggestions: [ { messageId: "removeImportant", - output: "a { border: 0 ; }", + output: "a { border: 0; }", }, ], }, @@ -133,7 +151,7 @@ ruleTester.run("no-important", rule, { suggestions: [ { messageId: "removeImportant", - output: "a { color: red ; margin: 0 ! important; }", + output: "a { color: red; margin: 0 ! important; }", }, ], }, @@ -146,7 +164,7 @@ ruleTester.run("no-important", rule, { suggestions: [ { messageId: "removeImportant", - output: "a { color: red !important; margin: 0 ; }", + output: "a { color: red !important; margin: 0; }", }, ], }, @@ -171,8 +189,7 @@ ruleTester.run("no-important", rule, { messageId: "removeImportant", output: dedent` a { - color: red - ; + color: red; } `, }, @@ -199,8 +216,7 @@ ruleTester.run("no-important", rule, { messageId: "removeImportant", output: dedent` a { - color: red - ; + color: red; } `, }, @@ -220,7 +236,7 @@ ruleTester.run("no-important", rule, { suggestions: [ { messageId: "removeImportant", - output: "a { color: red /* comment */ ; }", + output: "a { color: red /* comment */; }", }, ], }, @@ -245,8 +261,7 @@ ruleTester.run("no-important", rule, { messageId: "removeImportant", output: dedent` a { - color: red /* comment */ - ; + color: red /* comment */; } `, }, @@ -266,7 +281,7 @@ ruleTester.run("no-important", rule, { suggestions: [ { messageId: "removeImportant", - output: "a { color: red ; }", + output: "a { color: red; }", }, ], }, @@ -284,7 +299,7 @@ ruleTester.run("no-important", rule, { suggestions: [ { messageId: "removeImportant", - output: "a { color: red ; }", + output: "a { color: red; }", }, ], }, @@ -309,8 +324,7 @@ ruleTester.run("no-important", rule, { messageId: "removeImportant", output: dedent` a { - color: red - ; + color: red; } `, }, @@ -337,8 +351,7 @@ ruleTester.run("no-important", rule, { messageId: "removeImportant", output: dedent` a { - color: red - ; + color: red; } `, }, @@ -367,8 +380,7 @@ ruleTester.run("no-important", rule, { messageId: "removeImportant", output: dedent` a { - color: red - ; + color: red; margin: 0 ! important; } @@ -389,8 +401,7 @@ ruleTester.run("no-important", rule, { a { color: red !important; - margin: 0 - ; + margin: 0; } `, }, @@ -410,7 +421,7 @@ ruleTester.run("no-important", rule, { suggestions: [ { messageId: "removeImportant", - output: "@keyframes important { from { margin: 1px ; } }", + output: "@keyframes important { from { margin: 1px; } }", }, ], }, @@ -428,7 +439,7 @@ ruleTester.run("no-important", rule, { suggestions: [ { messageId: "removeImportant", - output: "@-webkit-keyframes important { from { margin: 1px ; } }", + output: "@-webkit-keyframes important { from { margin: 1px; } }", }, ], }, @@ -446,7 +457,7 @@ ruleTester.run("no-important", rule, { suggestions: [ { messageId: "removeImportant", - output: "@-WEBKIT-KEYFRAMES important { from { margin: 1px ; } }", + output: "@-WEBKIT-KEYFRAMES important { from { margin: 1px; } }", }, ], }, @@ -482,7 +493,7 @@ ruleTester.run("no-important", rule, { suggestions: [ { messageId: "removeImportant", - output: "@keyframes important { from { margin: 1px ; } }", + output: "@keyframes important { from { margin: 1px; } }", }, ], }, @@ -500,7 +511,7 @@ ruleTester.run("no-important", rule, { suggestions: [ { messageId: "removeImportant", - output: "@kEyFrAmEs important { from { margin: 1px ; } }", + output: "@kEyFrAmEs important { from { margin: 1px; } }", }, ], }, @@ -518,7 +529,7 @@ ruleTester.run("no-important", rule, { suggestions: [ { messageId: "removeImportant", - output: "@KEYFRAMES important { from { margin: 1px ; } }", + output: "@KEYFRAMES important { from { margin: 1px; } }", }, ], },