diff --git a/docs/user-guide/rules.md b/docs/user-guide/rules.md index 5585463909..26469c9981 100644 --- a/docs/user-guide/rules.md +++ b/docs/user-guide/rules.md @@ -304,7 +304,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 (Autofixable). - [`selector-pseudo-class-case`](../../lib/rules/selector-pseudo-class-case/README.md): Specify lowercase or uppercase for pseudo-class selectors (Autofixable). - [`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 (Autofixable). -- [`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 selectors (Autofixable). diff --git a/lib/rules/selector-pseudo-element-case/README.md b/lib/rules/selector-pseudo-element-case/README.md index af6c17ff86..9cf9621429 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 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..f73f4ce124 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: [ { @@ -86,120 +87,142 @@ testRule(rule, { { code: "html { --custom-property-set: {} }", description: "custom property set in selector" + }, + { + code: "html/*comment*/ { }" } ], 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 +232,7 @@ testRule(rule, { }, { code: "a::sOmE-pSeUdO-eLeMenT { }", + fixed: "a::some-pseudo-element { }", message: messages.expected( "::sOmE-pSeUdO-eLeMenT", "::some-pseudo-element" @@ -218,6 +242,7 @@ testRule(rule, { }, { code: "a::SOME-PSEUDO-ELEMENT { }", + fixed: "a::some-pseudo-element { }", message: messages.expected( "::SOME-PSEUDO-ELEMENT", "::some-pseudo-element" @@ -227,33 +252,45 @@ 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 + }, + { + code: "a::bEfOrE/*comment*/ { color: pink; }", + fixed: "a::before/*comment*/ { color: pink; }", + message: messages.expected("::bEfOrE", "::before"), + line: 1, + column: 2 } ] }); @@ -273,6 +310,9 @@ testRule(rule, { }, { code: "a::#{$variable} {}" + }, + { + code: "a::#{$variable}/*comment*/ {}" } ] }); @@ -280,6 +320,7 @@ testRule(rule, { testRule(rule, { ruleName, config: ["upper"], + fix: true, accept: [ { @@ -344,120 +385,142 @@ testRule(rule, { }, { code: "input::-MOZ-PLACEHOLDER { color: pink; }" + }, + { + code: "a:FOCUS/*comment*/ { }" } ], 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 +530,7 @@ testRule(rule, { }, { code: "a::sOmE-pSeUdO-eLeMenT { }", + fixed: "a::SOME-PSEUDO-ELEMENT { }", message: messages.expected( "::sOmE-pSeUdO-eLeMenT", "::SOME-PSEUDO-ELEMENT" @@ -476,6 +540,7 @@ testRule(rule, { }, { code: "a::some-pseudo-element { }", + fixed: "a::SOME-PSEUDO-ELEMENT { }", message: messages.expected( "::some-pseudo-element", "::SOME-PSEUDO-ELEMENT" @@ -485,33 +550,45 @@ 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 + }, + { + code: "::Selection/*comment*/ { }", + fixed: "::SELECTION/*comment*/ { }", + message: messages.expected("::Selection", "::SELECTION"), + line: 1, + column: 1 } ] }); diff --git a/lib/rules/selector-pseudo-element-case/index.js b/lib/rules/selector-pseudo-element-case/index.js index 06c1f5a7cb..1ee394ae05 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, @@ -37,7 +37,7 @@ const rule = function(expectation) { return; } - parseSelector(selector, result, rule, selectorTree => { + transformSelector(result, rule, selectorTree => { selectorTree.walkPseudos(pseudoNode => { const pseudoElement = pseudoNode.value; @@ -63,6 +63,12 @@ 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" }); + } +};