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

View File

@@ -0,0 +1,129 @@
# declaration-block-no-duplicate-properties
Disallow duplicate properties within declaration blocks.
```css
a { color: pink; color: orange; }
/** ↑ ↑
* These duplicated properties */
```
This rule ignores variables (`$sass`, `@less`, `--custom-property`).
## Options
### `true`
The following patterns are considered warnings:
```css
a { color: pink; color: orange; }
```
```css
a { color: pink; background: orange; color: orange }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a { color: pink; background: orange; }
```
## Optional secondary options
### `ignore: ["consecutive-duplicates"]`
Ignore consecutive duplicated properties.
They can prove to be useful fallbacks for older browsers.
The following patterns are considered warnings:
```css
p {
font-size: 16px;
font-weight: 400;
font-size: 1rem;
}
```
The following patterns are *not* considered warnings:
```css
p {
font-size: 16px;
font-size: 1rem;
font-weight: 400;
}
```
### `ignore: ["consecutive-duplicates-with-different-values"]`
Ignore consecutive duplicated properties with different values.
Including duplicate properties (fallbacks) is useful to deal with older browsers support for CSS properties. E.g. using 'px' units when 'rem' isn't available.
The following patterns are considered warnings:
```css
/* properties with the same value */
p {
font-size: 16px;
font-size: 16px;
font-weight: 400;
}
```
```css
/* nonconsecutive duplicates */
p {
font-size: 16px;
font-weight: 400;
font-size: 1rem;
}
```
The following patterns are *not* considered warnings:
```css
p {
font-size: 16px;
font-size: 1rem;
font-weight: 400;
}
```
### `ignoreProperties: ["/regex/", "non-regex"]`
Ignore duplicates of specific properties.
Given:
```js
["color", "/background\-/"]
```
The following patterns are considered warnings:
```css
a { color: pink; background: orange; background: white; }
```
```css
a { background: orange; color: pink; background: white; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; color: orange; background-color: orange; background-color: white; }
```
```css
a { color: pink; background-color: orange; color: orange; background-color: white; }
```

View File

@@ -0,0 +1,135 @@
"use strict"
const _ = require("lodash")
const isCustomProperty = require("../../utils/isCustomProperty")
const isStandardSyntaxProperty = require("../../utils/isStandardSyntaxProperty")
const optionsMatches = require("../../utils/optionsMatches")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "declaration-block-no-duplicate-properties"
const messages = ruleMessages(ruleName, {
rejected: property => `Unexpected duplicate "${property}"`,
})
const rule = function (on, options) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual: on }, {
actual: options,
possible: {
ignore: [
"consecutive-duplicates",
"consecutive-duplicates-with-different-values",
],
ignoreProperties: [_.isString],
},
optional: true,
})
if (!validOptions) {
return
}
// In order to accommodate nested blocks (postcss-nested),
// we need to run a shallow loop (instead of eachDecl() or eachRule(),
// which loop recursively) and allow each nested block to accumulate
// its own list of properties -- so that a property in a nested rule
// does not conflict with the same property in the parent rule
root.each(node => {
if (
node.type === "rule"
|| node.type === "atrule"
) {
checkRulesInNode(node)
}
})
function checkRulesInNode(node) {
const decls = []
const values = []
node.each(child => {
if (
child.nodes
&& child.nodes.length
) {
checkRulesInNode(child)
}
if (child.type !== "decl") {
return
}
const prop = child.prop
const value = child.value
if (!isStandardSyntaxProperty(prop)) {
return
}
if (isCustomProperty(prop)) {
return
}
// Return early if the property is to be ignored
if (optionsMatches(options, "ignoreProperties", prop)) {
return
}
// Ignore the src property as commonly duplicated in at-fontface
if (prop.toLowerCase() === "src") {
return
}
const indexDuplicate = decls.indexOf(prop.toLowerCase())
if (indexDuplicate !== -1) {
if (optionsMatches(options, "ignore", "consecutive-duplicates-with-different-values")) {
// if duplicates are not consecutive
if (indexDuplicate !== decls.length - 1) {
report({
message: messages.rejected(prop),
node: child,
result,
ruleName,
})
return
}
// if values of consecutive duplicates are equal
if (value === values[indexDuplicate]) {
report({
message: messages.rejected(value),
node: child,
result,
ruleName,
})
return
}
return
}
if (
optionsMatches(options, "ignore", "consecutive-duplicates")
&& indexDuplicate === decls.length - 1
) {
return
}
report({
message: messages.rejected(prop),
node: child,
result,
ruleName,
})
}
decls.push(prop.toLowerCase())
values.push(value.toLowerCase())
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule