From 2f8fe25a144b992818e4b832192c3748bc6e7bc7 Mon Sep 17 00:00:00 2001 From: Pavel Trehubau Date: Sat, 15 Sep 2018 22:18:50 +0300 Subject: [PATCH 1/3] Add autofix to selector-pseudo-element-case --- docs/user-guide/rules.md | 2 +- .../selector-pseudo-element-case/README.md | 2 + .../__tests__/index.js | 54 +++++++++++++++++++ .../selector-pseudo-element-case/index.js | 11 ++-- lib/utils/transformSelector.js | 16 ++++++ 5 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 lib/utils/transformSelector.js diff --git a/docs/user-guide/rules.md b/docs/user-guide/rules.md index 1cfe132c4a..176379cf3e 100644 --- a/docs/user-guide/rules.md +++ b/docs/user-guide/rules.md @@ -302,7 +302,7 @@ Here are all the rules within stylelint, grouped first [by category](../../VISIO - [`selector-descendant-combinator-no-non-space`](../../lib/rules/selector-descendant-combinator-no-non-space/README.md): Disallow non-space characters for descendant combinators of selectors. - [`selector-pseudo-class-case`](../../lib/rules/selector-pseudo-class-case/README.md): Specify lowercase or uppercase for pseudo-class selectors. - [`selector-pseudo-class-parentheses-space-inside`](../../lib/rules/selector-pseudo-class-parentheses-space-inside/README.md): Require a single space or disallow whitespace on the inside of the parentheses within pseudo-class selectors. -- [`selector-pseudo-element-case`](../../lib/rules/selector-pseudo-element-case/README.md): Specify lowercase or uppercase for pseudo-element selectors. +- [`selector-pseudo-element-case`](../../lib/rules/selector-pseudo-element-case/README.md): Specify lowercase or uppercase for pseudo-element selectors (Autofixable). - [`selector-pseudo-element-colon-notation`](../../lib/rules/selector-pseudo-element-colon-notation/README.md): Specify single or double colon notation for applicable pseudo-elements (Autofixable). - [`selector-type-case`](../../lib/rules/selector-type-case/README.md): Specify lowercase or uppercase for type selector. diff --git a/lib/rules/selector-pseudo-element-case/README.md b/lib/rules/selector-pseudo-element-case/README.md index cdd78ed353..983fa09509 100644 --- a/lib/rules/selector-pseudo-element-case/README.md +++ b/lib/rules/selector-pseudo-element-case/README.md @@ -8,6 +8,8 @@ Specify lowercase or uppercase for pseudo-element selectors. * This is pseudo-element selector */ ``` +The `--fix` option on the [command line](../../../docs/user-guide/cli.md#autofixing-errors) can automatically fix all of the problems reported by this rule. + ## Options `string`: `"lower"|"upper"` diff --git a/lib/rules/selector-pseudo-element-case/__tests__/index.js b/lib/rules/selector-pseudo-element-case/__tests__/index.js index 9f1e86a618..e6985fd409 100644 --- a/lib/rules/selector-pseudo-element-case/__tests__/index.js +++ b/lib/rules/selector-pseudo-element-case/__tests__/index.js @@ -6,6 +6,7 @@ const { messages, ruleName } = rule; testRule(rule, { ruleName, config: ["lower"], + fix: true, accept: [ { @@ -92,114 +93,133 @@ testRule(rule, { reject: [ { code: "a:Before { color: pink; }", + fixed: "a:before { color: pink; }", message: messages.expected(":Before", ":before"), line: 1, column: 2 }, { code: "a:bEfOrE { color: pink; }", + fixed: "a:before { color: pink; }", message: messages.expected(":bEfOrE", ":before"), line: 1, column: 2 }, { code: "a:BEFORE { color: pink; }", + fixed: "a:before { color: pink; }", message: messages.expected(":BEFORE", ":before"), line: 1, column: 2 }, { code: "a:After { color: pink; }", + fixed: "a:after { color: pink; }", message: messages.expected(":After", ":after"), line: 1, column: 2 }, { code: "a:First-letter { color: pink; }", + fixed: "a:first-letter { color: pink; }", message: messages.expected(":First-letter", ":first-letter"), line: 1, column: 2 }, { code: "a:First-line { color: pink; }", + fixed: "a:first-line { color: pink; }", message: messages.expected(":First-line", ":first-line"), line: 1, column: 2 }, { code: "a::Before { color: pink; }", + fixed: "a::before { color: pink; }", message: messages.expected("::Before", "::before"), line: 1, column: 2 }, { code: "a::bEfOrE { color: pink; }", + fixed: "a::before { color: pink; }", message: messages.expected("::bEfOrE", "::before"), line: 1, column: 2 }, { code: "a::BEFORE { color: pink; }", + fixed: "a::before { color: pink; }", message: messages.expected("::BEFORE", "::before"), line: 1, column: 2 }, { code: "a::After { color: pink; }", + fixed: "a::after { color: pink; }", message: messages.expected("::After", "::after"), line: 1, column: 2 }, { code: "a::First-letter { color: pink; }", + fixed: "a::first-letter { color: pink; }", message: messages.expected("::First-letter", "::first-letter"), line: 1, column: 2 }, { code: "a::First-line { color: pink; }", + fixed: "a::first-line { color: pink; }", message: messages.expected("::First-line", "::first-line"), line: 1, column: 2 }, { code: "::Selection { }", + fixed: "::selection { }", message: messages.expected("::Selection", "::selection"), line: 1, column: 1 }, { code: "::sElEcTiOn { }", + fixed: "::selection { }", message: messages.expected("::sElEcTiOn", "::selection"), line: 1, column: 1 }, { code: "::SELECTION { }", + fixed: "::selection { }", message: messages.expected("::SELECTION", "::selection"), line: 1, column: 1 }, { code: "a::Spelling-error { color: pink; }", + fixed: "a::spelling-error { color: pink; }", message: messages.expected("::Spelling-error", "::spelling-error"), line: 1, column: 2 }, { code: "a::Grammar-error { color: pink; }", + fixed: "a::grammar-error { color: pink; }", message: messages.expected("::Grammar-error", "::grammar-error"), line: 1, column: 2 }, { code: "li::Marker { font-variant-numeric: tabular-nums; }", + fixed: "li::marker { font-variant-numeric: tabular-nums; }", message: messages.expected("::Marker", "::marker"), line: 1, column: 3 }, { code: "a::Some-pseudo-element { }", + fixed: "a::some-pseudo-element { }", message: messages.expected( "::Some-pseudo-element", "::some-pseudo-element" @@ -209,6 +229,7 @@ testRule(rule, { }, { code: "a::sOmE-pSeUdO-eLeMenT { }", + fixed: "a::some-pseudo-element { }", message: messages.expected( "::sOmE-pSeUdO-eLeMenT", "::some-pseudo-element" @@ -218,6 +239,7 @@ testRule(rule, { }, { code: "a::SOME-PSEUDO-ELEMENT { }", + fixed: "a::some-pseudo-element { }", message: messages.expected( "::SOME-PSEUDO-ELEMENT", "::some-pseudo-element" @@ -227,30 +249,35 @@ testRule(rule, { }, { code: "p:first-child:Before { }", + fixed: "p:first-child:before { }", message: messages.expected(":Before", ":before"), line: 1, column: 14 }, { code: "p:First-child:Before { }", + fixed: "p:First-child:before { }", message: messages.expected(":Before", ":before"), line: 1, column: 14 }, { code: "p:first-child::Before { }", + fixed: "p:first-child::before { }", message: messages.expected("::Before", "::before"), line: 1, column: 14 }, { code: "p:First-child::Before { }", + fixed: "p:First-child::before { }", message: messages.expected("::Before", "::before"), line: 1, column: 14 }, { code: "input::-MOZ-PLACEHOLDER { color: pink; }", + fixed: "input::-moz-placeholder { color: pink; }", message: messages.expected("::-MOZ-PLACEHOLDER", "::-moz-placeholder"), line: 1, column: 6 @@ -280,6 +307,7 @@ testRule(rule, { testRule(rule, { ruleName, config: ["upper"], + fix: true, accept: [ { @@ -350,114 +378,133 @@ testRule(rule, { reject: [ { code: "a:Before { color: pink; }", + fixed: "a:BEFORE { color: pink; }", message: messages.expected(":Before", ":BEFORE"), line: 1, column: 2 }, { code: "a:bEfOrE { color: pink; }", + fixed: "a:BEFORE { color: pink; }", message: messages.expected(":bEfOrE", ":BEFORE"), line: 1, column: 2 }, { code: "a:before { color: pink; }", + fixed: "a:BEFORE { color: pink; }", message: messages.expected(":before", ":BEFORE"), line: 1, column: 2 }, { code: "a:After { color: pink; }", + fixed: "a:AFTER { color: pink; }", message: messages.expected(":After", ":AFTER"), line: 1, column: 2 }, { code: "a:First-letter { color: pink; }", + fixed: "a:FIRST-LETTER { color: pink; }", message: messages.expected(":First-letter", ":FIRST-LETTER"), line: 1, column: 2 }, { code: "a:First-line { color: pink; }", + fixed: "a:FIRST-LINE { color: pink; }", message: messages.expected(":First-line", ":FIRST-LINE"), line: 1, column: 2 }, { code: "a::Before { color: pink; }", + fixed: "a::BEFORE { color: pink; }", message: messages.expected("::Before", "::BEFORE"), line: 1, column: 2 }, { code: "a::bEfOrE { color: pink; }", + fixed: "a::BEFORE { color: pink; }", message: messages.expected("::bEfOrE", "::BEFORE"), line: 1, column: 2 }, { code: "a::before { color: pink; }", + fixed: "a::BEFORE { color: pink; }", message: messages.expected("::before", "::BEFORE"), line: 1, column: 2 }, { code: "a::After { color: pink; }", + fixed: "a::AFTER { color: pink; }", message: messages.expected("::After", "::AFTER"), line: 1, column: 2 }, { code: "a::First-letter { color: pink; }", + fixed: "a::FIRST-LETTER { color: pink; }", message: messages.expected("::First-letter", "::FIRST-LETTER"), line: 1, column: 2 }, { code: "a::First-line { color: pink; }", + fixed: "a::FIRST-LINE { color: pink; }", message: messages.expected("::First-line", "::FIRST-LINE"), line: 1, column: 2 }, { code: "::Selection { }", + fixed: "::SELECTION { }", message: messages.expected("::Selection", "::SELECTION"), line: 1, column: 1 }, { code: "::sElEcTiOn { }", + fixed: "::SELECTION { }", message: messages.expected("::sElEcTiOn", "::SELECTION"), line: 1, column: 1 }, { code: "::selection { }", + fixed: "::SELECTION { }", message: messages.expected("::selection", "::SELECTION"), line: 1, column: 1 }, { code: "a::Spelling-error { color: pink; }", + fixed: "a::SPELLING-ERROR { color: pink; }", message: messages.expected("::Spelling-error", "::SPELLING-ERROR"), line: 1, column: 2 }, { code: "a::Grammar-error { color: pink; }", + fixed: "a::GRAMMAR-ERROR { color: pink; }", message: messages.expected("::Grammar-error", "::GRAMMAR-ERROR"), line: 1, column: 2 }, { code: "li::Marker { font-variant-numeric: tabular-nums; }", + fixed: "li::MARKER { font-variant-numeric: tabular-nums; }", message: messages.expected("::Marker", "::MARKER"), line: 1, column: 3 }, { code: "a::Some-pseudo-element { }", + fixed: "a::SOME-PSEUDO-ELEMENT { }", message: messages.expected( "::Some-pseudo-element", "::SOME-PSEUDO-ELEMENT" @@ -467,6 +514,7 @@ testRule(rule, { }, { code: "a::sOmE-pSeUdO-eLeMenT { }", + fixed: "a::SOME-PSEUDO-ELEMENT { }", message: messages.expected( "::sOmE-pSeUdO-eLeMenT", "::SOME-PSEUDO-ELEMENT" @@ -476,6 +524,7 @@ testRule(rule, { }, { code: "a::some-pseudo-element { }", + fixed: "a::SOME-PSEUDO-ELEMENT { }", message: messages.expected( "::some-pseudo-element", "::SOME-PSEUDO-ELEMENT" @@ -485,30 +534,35 @@ testRule(rule, { }, { code: "p:first-child:Before { }", + fixed: "p:first-child:BEFORE { }", message: messages.expected(":Before", ":BEFORE"), line: 1, column: 14 }, { code: "p:First-child:Before { }", + fixed: "p:First-child:BEFORE { }", message: messages.expected(":Before", ":BEFORE"), line: 1, column: 14 }, { code: "p:first-child::Before { }", + fixed: "p:first-child::BEFORE { }", message: messages.expected("::Before", "::BEFORE"), line: 1, column: 14 }, { code: "p:First-child::Before { }", + fixed: "p:First-child::BEFORE { }", message: messages.expected("::Before", "::BEFORE"), line: 1, column: 14 }, { code: "input::-moz-placeholder { color: pink; }", + fixed: "input::-MOZ-PLACEHOLDER { color: pink; }", message: messages.expected("::-moz-placeholder", "::-MOZ-PLACEHOLDER"), line: 1, column: 6 diff --git a/lib/rules/selector-pseudo-element-case/index.js b/lib/rules/selector-pseudo-element-case/index.js index 8762e238b1..a56e132d6c 100644 --- a/lib/rules/selector-pseudo-element-case/index.js +++ b/lib/rules/selector-pseudo-element-case/index.js @@ -3,9 +3,9 @@ const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule"); const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector"); const keywordSets = require("../../reference/keywordSets"); -const parseSelector = require("../../utils/parseSelector"); const report = require("../../utils/report"); const ruleMessages = require("../../utils/ruleMessages"); +const transformSelector = require("../../utils/transformSelector"); const validateOptions = require("../../utils/validateOptions"); const ruleName = "selector-pseudo-element-case"; @@ -14,7 +14,7 @@ const messages = ruleMessages(ruleName, { expected: (actual, expected) => `Expected "${actual}" to be "${expected}"` }); -const rule = function(expectation) { +const rule = function(expectation, options, context) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, @@ -35,7 +35,7 @@ const rule = function(expectation) { return; } - parseSelector(selector, result, rule, selectorTree => { + transformSelector(result, rule, selectorTree => { selectorTree.walkPseudos(pseudoNode => { const pseudoElement = pseudoNode.value; @@ -61,6 +61,11 @@ const rule = function(expectation) { return; } + if (context.fix) { + pseudoNode.value = expectedPseudoElement; + return; + } + report({ message: messages.expected(pseudoElement, expectedPseudoElement), node: rule, diff --git a/lib/utils/transformSelector.js b/lib/utils/transformSelector.js new file mode 100644 index 0000000000..7104b40ef0 --- /dev/null +++ b/lib/utils/transformSelector.js @@ -0,0 +1,16 @@ +/* @flow */ +"use strict"; + +const selectorParser = require("postcss-selector-parser"); + +module.exports = function( + result /*: Object*/, + node /*: Object*/, + cb /*: Function*/ +) { + try { + return selectorParser(cb).processSync(node, { updateSelector: true }); + } catch (e) { + result.warn("Cannot parse selector", { node, stylelintType: "parseError" }); + } +}; From cd01924f8640e8b96c25aba476a115bac311a03d Mon Sep 17 00:00:00 2001 From: Pavel Trehubau Date: Sat, 18 May 2019 19:30:53 +0300 Subject: [PATCH 2/3] Added few test cases with comments in selectors --- .../__tests__/index.js | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/rules/selector-pseudo-element-case/__tests__/index.js b/lib/rules/selector-pseudo-element-case/__tests__/index.js index e6985fd409..f73f4ce124 100644 --- a/lib/rules/selector-pseudo-element-case/__tests__/index.js +++ b/lib/rules/selector-pseudo-element-case/__tests__/index.js @@ -87,6 +87,9 @@ testRule(rule, { { code: "html { --custom-property-set: {} }", description: "custom property set in selector" + }, + { + code: "html/*comment*/ { }" } ], @@ -281,6 +284,13 @@ testRule(rule, { message: messages.expected("::-MOZ-PLACEHOLDER", "::-moz-placeholder"), line: 1, column: 6 + }, + { + code: "a::bEfOrE/*comment*/ { color: pink; }", + fixed: "a::before/*comment*/ { color: pink; }", + message: messages.expected("::bEfOrE", "::before"), + line: 1, + column: 2 } ] }); @@ -300,6 +310,9 @@ testRule(rule, { }, { code: "a::#{$variable} {}" + }, + { + code: "a::#{$variable}/*comment*/ {}" } ] }); @@ -372,6 +385,9 @@ testRule(rule, { }, { code: "input::-MOZ-PLACEHOLDER { color: pink; }" + }, + { + code: "a:FOCUS/*comment*/ { }" } ], @@ -566,6 +582,13 @@ testRule(rule, { message: messages.expected("::-moz-placeholder", "::-MOZ-PLACEHOLDER"), line: 1, column: 6 + }, + { + code: "::Selection/*comment*/ { }", + fixed: "::SELECTION/*comment*/ { }", + message: messages.expected("::Selection", "::SELECTION"), + line: 1, + column: 1 } ] }); From 991d49558af04dd39ce9aba7dc1109bdd646a768 Mon Sep 17 00:00:00 2001 From: Pavel Trehubau Date: Sat, 18 May 2019 19:37:55 +0300 Subject: [PATCH 3/3] Fixed lint issue --- lib/rules/selector-pseudo-element-case/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/rules/selector-pseudo-element-case/index.js b/lib/rules/selector-pseudo-element-case/index.js index 408eb3ffbf..1ee394ae05 100644 --- a/lib/rules/selector-pseudo-element-case/index.js +++ b/lib/rules/selector-pseudo-element-case/index.js @@ -65,6 +65,7 @@ const rule = function(expectation, options, context) { if (context.fix) { pseudoNode.value = expectedPseudoElement; + return; }