Initial commit

This commit is contained in:
Patrick Marsceill
2017-03-09 13:16:08 -05:00
commit b7b0d0d7bf
4147 changed files with 401224 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
# selector-max-specificity
Limit the specificity of selectors.
```css
.foo, #bar.baz span, #hoo { color: pink; }
/** ↑ ↑ ↑
* Each of these selectors */
```
Visit the [Specificity Calculator](https://specificity.keegan.st) for visual representation of selector specificity.
This rule ignores selectors with variable interpolation (`#{$var}`, `@{var}`, `$(var)`).
This rule ignores selectors containing the `:not()` or `:matches()` pseudo-classes.
This rule resolves nested selectors before calculating the specificity of a selector.
## Options
`string`: Maximum specificity allowed.
Format is `"id,class,type"`, as laid out in the [W3C selector spec](https://drafts.csswg.org/selectors/#specificity-rules).
For example, with `"0,2,0"`:
The following patterns are considered warnings:
```css
#foo {}
```
```css
.foo .baz .bar {}
```
```css
.foo .baz {
& .bar {}
}
```
```css
.foo {
color: red;
@nest .baz .bar & {
color: blue;
}
}
```
The following patterns are *not* considered warnings:
```css
div {}
```
```css
.foo div {}
```
```css
.foo div {
& div a {}
}
```
```css
.foo {
& .baz {}
}
```
```css
.foo {
color: red;
@nest .baz & {
color: blue;
}
}
```
+74
View File
@@ -0,0 +1,74 @@
"use strict"
const specificity = require("specificity")
const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule")
const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const resolvedNestedSelector = require("postcss-resolve-nested-selector")
const ruleName = "selector-max-specificity"
const messages = ruleMessages(ruleName, {
expected: (selector, specificity) => `Expected "${selector}" to have a specificity no more than "${specificity}"`,
})
const rule = function (max) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: max,
possible: [
function (max) {
// Check that the max specificity is in the form "a,b,c"
const pattern = new RegExp("^\\d+,\\d+,\\d+$")
return pattern.test(max)
},
],
})
if (!validOptions) {
return
}
const maxSpecificityArray = ("0," + max).split(",").map(parseFloat)
root.walkRules(rule => {
if (!isStandardSyntaxRule(rule)) {
return
}
if (!isStandardSyntaxSelector(rule.selector)) {
return
}
// Using rule.selectors gets us each selector in the eventuality we have a comma separated set
rule.selectors.forEach(selector => {
resolvedNestedSelector(selector, rule).forEach(resolvedSelector => {
// Return early if selector contains a not pseudo-class
if (selector.indexOf(":not(") !== -1) {
return
}
// Return early if selector contains a matches
if (selector.indexOf(":matches(") !== -1) {
return
}
// Check if the selector specificity exceeds the allowed maximum
try {
if (specificity.compare(resolvedSelector, maxSpecificityArray) === 1) {
report({
ruleName,
result,
node: rule,
message: messages.expected(resolvedSelector, max),
word: selector,
})
}
} catch (e) {
result.warn("Cannot parse selector", { node: rule })
}
})
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule