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,45 @@
# at-rule-blacklist
Specify a blacklist of disallowed at-rules.
```css
@keyframes name {}
/** ↑
* At-rules like this */
```
## Options
`array|string`: `["array", "of", "unprefixed", "at-rules"]|"at-rule"`
Given:
```js
["extend", "keyframes"]
```
The following patterns are considered warnings:
```css
a { @extend placeholder; }
```
```css
@keyframes name {
from { top: 10px; }
to { top: 20px; }
}
```
```css
@-moz-keyframes name {
from { top: 10px; }
to { top: 20px; }
}
```
The following patterns are *not* considered warnings:
```css
@import "path/to/file.css";
```

View File

@@ -0,0 +1,48 @@
"use strict"
const _ = require("lodash")
const postcss = require("postcss")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "at-rule-blacklist"
const messages = ruleMessages(ruleName, {
rejected: name => `Unexpected at-rule "${name}"`,
})
const rule = function (blacklistInput) {
// To allow for just a string as a parameter (not only arrays of strings)
const blacklist = [].concat(blacklistInput)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: blacklist,
possible: [_.isString],
})
if (!validOptions) {
return
}
root.walkAtRules(atRule => {
const name = atRule.name
if (blacklist.indexOf(postcss.vendor.unprefixed(name).toLowerCase()) === -1) {
return
}
report({
message: messages.rejected(name),
node: atRule,
result,
ruleName,
})
})
}
}
rule.primaryOptionArray = true
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,353 @@
# at-rule-empty-line-before
Require or disallow an empty line before at-rules.
```css
a {}
/* ← */
@media {} /* ↑ */
/** ↑
* This line */
```
If the at-rule is the very first node in a stylesheet then it is ignored.
## Options
`string`: `"always"|"never"`
### `"always"`
There *must always* be an empty line before at-rules.
The following patterns are considered warnings:
```css
a {} @media {}
```
```css
a {}
@media {}
```
The following patterns are *not* considered warnings:
```css
a {}
@media {}
```
### `"never"`
There *must never* be an empty line before at-rules.
The following patterns are considered warnings:
```css
a {}
@media {}
```
The following patterns are *not* considered warnings:
```css
a {} @media {}
```
```css
a {}
@media {}
```
## Optional secondary options
### `except: ["after-same-name", "inside-block", "blockless-after-same-name-blockless", "blockless-after-blockless", "first-nested"]`
#### `"after-same-name"`
Reverse the primary option for at-rules that follow another at-rule with the same name.
This means that you can group your at-rules by name.
For example, with `"always"`:
The following patterns are *not* considered warnings:
```css
@charset "UTF-8";
@import url(x.css);
@import url(y.css);
@media (min-width: 100px) {}
@media (min-width: 200px) {}
```
```css
a {
@extends .foo;
@extends .bar;
@include x;
@include y {}
}
```
#### `"inside-block"`
***Note: This option was previously called `all-nested`.***
Reverse the primary option for at-rules that are nested.
For example, with `"always"`:
The following patterns are considered warnings:
```css
a {
@extend foo;
color: pink;
}
b {
color: pink;
@extend foo;
}
```
The following patterns are *not* considered warnings:
```css
a {
@extend foo;
color: pink;
}
b {
color: pink;
@extend foo;
}
```
#### `"blockless-after-same-name-blockless"`
Reverse the primary option for blockless at-rules that follow another blockless at-rule with the same name.
This means that you can group your blockless at-rules by name.
For example, with `"always"`:
The following patterns are *not* considered warnings:
```css
@charset "UTF-8";
@import url(x.css);
@import url(y.css);
```
```css
a {
@extends .foo;
@extends .bar;
@include loop;
@include doo;
}
```
#### `"blockless-after-blockless"`
***Note: This option was previously called `blockless-group`.***
Reverse the primary option for at-rules within a blockless group.
For example, with `"always"`:
The following patterns are considered warnings:
```css
@import url(x.css);
@import url(y.css);
@media print {}
```
The following patterns are *not* considered warnings:
```css
@import url(x.css);
@import url(y.css);
@media print {}
```
#### `"first-nested"`
Reverse the primary option for at-rules that are nested and the first child of their parent node.
For example, with `"always"`:
The following patterns are considered warnings:
```css
a {
@extend foo;
color: pink;
}
b {
color: pink;
@extend foo;
}
```
The following patterns are *not* considered warnings:
```css
a {
@extend foo;
color: pink;
}
b {
color: pink;
@extend foo;
}
```
### `ignore: ["after-comment", "inside-block", "blockless-after-same-name-blockless", "blockless-after-blockless"]`
#### `"after-comment"`
Ignore at-rules that come after a comment.
The following patterns are *not* considered warnings:
```css
/* comment */
@media {}
```
```css
/* comment */
@media {}
```
#### `"inside-block"`
***Note: This option was previously called `all-nested`.***
Ignore at-rules that are inside a declaration block.
For example, with `"always"`:
The following patterns are *not* considered warnings:
```css
a {
@extend foo;
color: pink;
}
a {
@extend foo;
color: pink;
}
b {
color: pink;
@extend foo;
}
b {
color: pink;
@extend foo;
}
```
#### `"blockless-after-same-name-blockless"`
Ignore blockless at-rules that follow another blockless at-rule with the same name.
This means that you can group your blockless at-rules by name.
For example, with `"always"`:
The following patterns are *not* considered warnings:
```css
@charset "UTF-8";
@import url(x.css);
@import url(y.css);
```
```css
a {
@extends .foo;
@extends .bar;
@include loop;
@include doo;
}
```
#### `"blockless-after-blockless"`
***Note: This option was previously called `blockless-group`.***
Ignore blockless at-rules that follow another blockless at-rule.
For example, with `"always"`:
The following patterns are *not* considered warnings:
```css
@import url(x.css);
@import url(y.css);
@media print {}
```
```css
@import url(x.css);
@import url(y.css);
@media print {}
```
### `ignoreAtRules: ["array", "of", "at-rules"]`
Ignore specified at-rules.
For example, with `"always"`.
Given:
```js
["import"]
```
The following patterns are *not* considered warnings:
```css
@charset "UTF-8";
@import {}
```

View File

@@ -0,0 +1,203 @@
"use strict"
const _ = require("lodash")
const hasBlock = require("../../utils/hasBlock")
const hasEmptyLine = require("../../utils/hasEmptyLine")
const optionsMatches = require("../../utils/optionsMatches")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "at-rule-empty-line-before"
const messages = ruleMessages(ruleName, {
expected: "Expected empty line before at-rule",
rejected: "Unexpected empty line before at-rule",
})
const rule = function (expectation, options) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"never",
],
}, {
actual: options,
possible: {
except: [
"after-same-name",
"all-nested",
"inside-block",
"blockless-after-same-name-blockless",
"blockless-group",
"blockless-after-blockless",
"first-nested",
],
ignore: [
"after-comment",
"all-nested",
"inside-block",
"blockless-after-same-name-blockless",
"blockless-group",
"blockless-after-blockless",
],
ignoreAtRules: [_.isString],
},
optional: true,
})
if (!validOptions) {
return
}
if (
optionsMatches(options, "ignore", "all-nested")
|| optionsMatches(options, "except", "all-nested")
) {
result.warn((
"'at-rule-empty-line-before\'s' \"all-nested\" option has been deprecated and in 8.0 will be removed. " +
"Instead use the \"inside-block\" option."
), {
stylelintType: "deprecation",
stylelintReference: "https://stylelint.io/user-guide/rules/at-rule-empty-line-before/",
})
}
if (
optionsMatches(options, "ignore", "blockless-group")
|| optionsMatches(options, "except", "blockless-group")
) {
result.warn((
"'at-rule-empty-line-before\'s' \"blockless-group\" option has been deprecated and in 8.0 will be removed. " +
"Instead use the \"blockless-after-blockless\" option."
), {
stylelintType: "deprecation",
stylelintReference: "https://stylelint.io/user-guide/rules/at-rule-empty-line-before/",
})
}
root.walkAtRules(atRule => {
// Ignore the first node
if (atRule === root.first) {
return
}
// Return early if at-rule is to be ignored
if (optionsMatches(options, "ignoreAtRules", atRule.name)) {
return
}
// Optionally ignore the expectation if the node is blockless
if (
optionsMatches(options, "ignore", "blockless-group")
&& !hasBlock(atRule)
|| optionsMatches(options, "ignore", "blockless-after-blockless")
&& !hasBlock(atRule)
) {
return
}
const isNested = atRule.parent !== root
const previousNode = atRule.prev()
// Optionally ignore the expection if the node is blockless
// and following another blockless at-rule with the same name
if (
optionsMatches(options, "ignore", "blockless-after-same-name-blockless")
&& isBlocklessAfterSameNameBlockless()
) {
return
}
// Optionally ignore the expectation if the node is inside a block
if (
optionsMatches(options, "ignore", "all-nested")
&& isNested
|| optionsMatches(options, "ignore", "inside-block")
&& isNested
) {
return
}
// Optionally ignore the expectation if a comment precedes this node
if (
optionsMatches(options, "ignore", "after-comment")
&& isAfterComment()
) {
return
}
const hasEmptyLineBefore = hasEmptyLine(atRule.raws.before)
let expectEmptyLineBefore = expectation === "always"
? true
: false
// Optionally reverse the expectation if any exceptions apply
if (
optionsMatches(options, "except", "after-same-name")
&& isAfterSameName()
|| optionsMatches(options, "except", "all-nested")
&& isNested
|| optionsMatches(options, "except", "inside-block")
&& isNested
|| optionsMatches(options, "except", "first-nested")
&& isFirstNested()
|| optionsMatches(options, "except", "blockless-group")
&& isBlocklessAfterBlockless()
|| optionsMatches(options, "except", "blockless-after-blockless")
&& isBlocklessAfterBlockless()
|| optionsMatches(options, "except", "blockless-after-same-name-blockless")
&& isBlocklessAfterSameNameBlockless()
) {
expectEmptyLineBefore = !expectEmptyLineBefore
}
// Return if the expectation is met
if (expectEmptyLineBefore === hasEmptyLineBefore) {
return
}
const message = expectEmptyLineBefore
? messages.expected
: messages.rejected
report({ message, node: atRule, result, ruleName })
function isAfterComment() {
return previousNode
&& previousNode.type === "comment"
}
function isBlocklessAfterBlockless() {
return previousNode
&& previousNode.type === "atrule"
&& !hasBlock(previousNode)
&& !hasBlock(atRule)
}
function isBlocklessAfterSameNameBlockless() {
return !hasBlock(atRule)
&& previousNode
&& !hasBlock(previousNode)
&& previousNode.type === "atrule"
&& previousNode.name == atRule.name
}
function isAfterSameName() {
return previousNode
&& previousNode.type === "atrule"
&& previousNode.name === atRule.name
}
function isFirstNested() {
return isNested
&& atRule === atRule.parent.first
}
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,91 @@
# at-rule-name-case
Specify lowercase or uppercase for at-rules names.
```css
@media (min-width: 10px) {}
/** ↑
* These at-rule names */
```
Only lowercase at-rule names are valid in SCSS.
## Options
`string`: `"lower"|"upper"`
### `"lower"`
The following patterns are considered warnings:
```css
@Charset 'UTF-8';
```
```css
@cHarSeT 'UTF-8';
```
```css
@CHARSET 'UTF-8';
```
```css
@Media (min-width: 50em) {}
```
```css
@mEdIa (min-width: 50em) {}
```
```css
@MEDIA (min-width: 50em) {}
```
The following patterns are *not* considered warnings:
```css
@charset 'UTF-8';
```
```css
@media (min-width: 50em) {}
```
### `"upper"`
The following patterns are considered warnings:
```css
@Charset 'UTF-8';
```
```css
@cHarSeT 'UTF-8';
```
```css
@charset 'UTF-8';
```
```css
@Media (min-width: 50em) {}
```
```css
@mEdIa (min-width: 50em) {}
```
```css
@media (min-width: 50em) {}
```
The following patterns are *not* considered warnings:
```css
@CHARSET 'UTF-8';
```
```css
@MEDIA (min-width: 50em) {}
```

View File

@@ -0,0 +1,49 @@
"use strict"
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "at-rule-name-case"
const messages = ruleMessages(ruleName, {
expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`,
})
const rule = function (expectation) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"lower",
"upper",
],
})
if (!validOptions) {
return
}
root.walkAtRules(atRule => {
const name = atRule.name
const expectedName = expectation === "lower"
? name.toLowerCase()
: name.toUpperCase()
if (name === expectedName) {
return
}
report({
message: messages.expected(name, expectedName),
node: atRule,
ruleName,
result,
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,94 @@
# at-rule-name-newline-after
Require a newline after at-rule names.
```css
@media
/*↑*/ (max-width: 600px) {}
/** ↑
* The newline after this at-rule name */
```
## Options
`string`: `"always"|"always-multi-line"`
### `"always"`
There *must always* be a newline after at-rule names.
The following patterns are considered warnings:
```css
@charset "UTF-8";
```
```css
@media (min-width: 700px) and
(orientation: landscape) {}
```
The following patterns are *not* considered warnings:
```css
@charset
"UTF-8";
```
```css
@import
"x.css" screen and
(orientation:landscape);
```
```css
@media
(min-width: 700px) and (orientation: landscape) {}
```
```css
@media
(min-width: 700px) and
(orientation: landscape) {}
```
### `"always-multi-line"`
There *must always* be a newline after at-rule names in at-rules with multi-line parameters.
The following patterns are considered warnings:
```css
@import "x.css" screen and
(orientation:landscape);
```
```css
@media (min-width: 700px) and
(orientation: landscape) {}
```
The following patterns are *not* considered warnings:
```css
@charset "UTF-8";
```
```css
@charset
"UTF-8";
```
```css
@import "x.css" screen and (orientation:landscape);
```
```css
@media (min-width: 700px) and (orientation: landscape) {}
```
```css
@media
(min-width: 700px) and
(orientation: landscape) {}
```

View File

@@ -0,0 +1,39 @@
"use strict"
const atRuleNameSpaceChecker = require("../atRuleNameSpaceChecker")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "at-rule-name-newline-after"
const messages = ruleMessages(ruleName, {
expectedAfter: name => `Expected newline after at-rule name \"${name}\"`,
})
const rule = function (expectation) {
const checker = whitespaceChecker("newline", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"always-multi-line",
],
})
if (!validOptions) {
return
}
atRuleNameSpaceChecker({
root,
result,
locationChecker: checker.afterOneOnly,
checkedRuleName: ruleName,
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,98 @@
# at-rule-name-space-after
Require a single space after at-rule names.
```css
@media (max-width: 600px) {}
/** ↑
* The space after at-rule names */
```
## Options
`string`: `"always"|"always-single-line"`
### `"always"`
There *must always* be a single space after at-rule names.
The following patterns are considered warnings:
```css
@charset"UTF-8";
```
```css
@media(min-width: 700px) {}
```
```css
@media (min-width: 700px) {}
```
```css
@media
(min-width: 700px) {}
```
The following patterns are *not* considered warnings:
```css
@charset "UTF-8";
```
```css
@import url("x.css");
```
```css
@media (min-width: 700px) {}
```
### `"always-single-line"`
There *must always* be a single space after at-rule names in single-line declaration blocks.
The following patterns are considered warnings:
```css
@charset"UTF-8";
```
```css
@media(min-width: 700px) {}
```
```css
@media (min-width: 700px) {}
```
The following patterns are *not* considered warnings:
```css
@charset "UTF-8";
```
```css
@import url("x.css");
```
```css
@media (min-width: 700px) {}
```
```css
@media
(min-width: 700px) {}
```
```css
@media(min-width: 700px) and
(orientation: portrait) {}
```
```css
@media
(min-width: 700px) and
(orientation: portrait) {}
```

View File

@@ -0,0 +1,39 @@
"use strict"
const atRuleNameSpaceChecker = require("../atRuleNameSpaceChecker")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "at-rule-name-space-after"
const messages = ruleMessages(ruleName, {
expectedAfter: name => `Expected single space after at-rule name \"${name}\"`,
})
const rule = function (expectation) {
const checker = whitespaceChecker("space", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"always-single-line",
],
})
if (!validOptions) {
return
}
atRuleNameSpaceChecker({
root,
result,
locationChecker: checker.after,
checkedRuleName: ruleName,
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,65 @@
# at-rule-no-unknown
Disallow unknown at-rules.
```css
@unknown (max-width: 960px) {}
/** ↑
* At-rules like this */
```
This rule considers at-rules defined in the CSS Specifications, up to and including Editor's Drafts, to be known.
## Options
### `true`
The following patterns are considered warnings:
```css
@unknown {}
```
The following patterns are *not* considered warnings:
```css
@charset "UTF-8";
```
```css
@CHARSET "UTF-8";
```
```css
@media (max-width: 960px) {}
```
```css
@font-feature-values Font One {
@styleset {}
}
```
## Optional secondary options
### `ignoreAtRules: ["/regex/", "string"]`
Given:
```js
["/^my-/", "custom"]
```
The following patterns are *not* considered warnings:
```css
@my-at-rule "x.css";
```
```css
@my-other-at-rule {}
```
```css
@custom {}
```

View File

@@ -0,0 +1,58 @@
"use strict"
const _ = require("lodash")
const keywordSets = require("../../reference/keywordSets")
const optionsMatches = require("../../utils/optionsMatches")
const postcss = require("postcss")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "at-rule-no-unknown"
const messages = ruleMessages(ruleName, {
rejected: atRule => `Unexpected unknown at-rule "${atRule}"`,
})
const rule = function (actual, options) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual }, {
actual: options,
possible: {
ignoreAtRules: [_.isString],
},
optional: true,
})
if (!validOptions) {
return
}
root.walkAtRules(atRule => {
const name = atRule.name
// Return early if at-rule is to be ignored
if (optionsMatches(options, "ignoreAtRules", atRule.name)) {
return
}
if (
postcss.vendor.prefix(name)
|| keywordSets.atRules.has(name.toLowerCase())
) {
return
}
report({
message: messages.rejected(`@${name}`),
node: atRule,
ruleName,
result,
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,33 @@
# at-rule-no-vendor-prefix
Disallow vendor prefixes for at-rules.
```css
@-webkit-keyframes { 0% { top: 0; } }
/** ↑
* These prefixes */
```
## Options
### `true`
The following patterns are considered warnings:
```css
@-webkit-keyframes { 0% { top: 0; } }
```
```css
@-ms-viewport { orientation: landscape; }
```
The following patterns are *not* considered warnings:
```css
@keyframes { 0% { top: 0; } }
```
```css
@viewport { orientation: landscape; }
```

View File

@@ -0,0 +1,49 @@
"use strict"
const isAutoprefixable = require("../../utils/isAutoprefixable")
const isStandardSyntaxAtRule = require("../../utils/isStandardSyntaxAtRule")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "at-rule-no-vendor-prefix"
const messages = ruleMessages(ruleName, {
rejected: p => `Unexpected vendor-prefixed at-rule "@${p}"`,
})
const rule = function (actual) {
return function (root, result) {
const validOptions = validateOptions(result, ruleName, { actual })
if (!validOptions) {
return
}
root.walkAtRules(atRule => {
if (!isStandardSyntaxAtRule(atRule)) {
return
}
const name = atRule.name
if (name[0] !== "-") {
return
}
if (!isAutoprefixable.atRuleName(name)) {
return
}
report({
message: messages.rejected(name),
node: atRule,
result,
ruleName,
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,54 @@
# at-rule-semicolon-newline-after
Require a newline after the semicolon of at-rules.
```css
@import url("x.css");
@import url("y.css");
/** ↑
* The newline after these semicolons */
```
This rule allows an end-of-line comment followed by a newline. For example:
```css
@import url("x.css"); /* end-of-line comment */
a {}
```
## Options
`string`: `"always"`
### `"always"`
There *must always* be a newline after the semicolon.
The following patterns are considered warnings:
```css
@import url("x.css"); @import url("y.css");
```
```css
@import url("x.css"); a {}
```
The following patterns are *not* considered warnings:
```css
@import url("x.css");
@import url("y.css");
```
```css
@import url("x.css"); /* end-of-line comment */
a {}
```
```css
@import url("x.css");
a {}
```

View File

@@ -0,0 +1,63 @@
"use strict"
const hasBlock = require("../../utils/hasBlock")
const nextNonCommentNode = require("../../utils/nextNonCommentNode")
const rawNodeString = require("../../utils/rawNodeString")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "at-rule-semicolon-newline-after"
const messages = ruleMessages(ruleName, {
expectedAfter: () => "Expected newline after \";\"",
})
const rule = function (actual) {
const checker = whitespaceChecker("newline", actual, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual,
possible: ["always"],
})
if (!validOptions) {
return
}
root.walkAtRules(atRule => {
const nextNode = atRule.next()
if (!nextNode) {
return
}
if (hasBlock(atRule)) {
return
}
// Allow an end-of-line comment
const nodeToCheck = nextNonCommentNode(nextNode)
if (!nodeToCheck) {
return
}
checker.afterOneOnly({
source: rawNodeString(nodeToCheck),
index: -1,
err: msg => {
report({
message: msg,
node: atRule,
index: atRule.toString().length + 1,
result,
ruleName,
})
},
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,57 @@
# at-rule-whitelist
Specify a whitelist of allowed at-rules.
```css
@keyframes name {}
/** ↑
* At-rules like this */
```
## Options
`array|string`: `["array", "of", "unprefixed", "at-rules"]|"at-rule"`
Given:
```js
["extend", "keyframes"]
```
The following patterns are considered warnings:
```css
@import "path/to/file.css";
```
```css
@media screen and (max-width: 1024px) {
a { display: none; }
}
```
The following patterns are *not* considered warnings:
```css
a { @extend placeholder; }
```
```css
@keyframes name {
from { top: 10px; }
to { top: 20px; }
}
```
```css
@KEYFRAMES name {
from { top: 10px; }
to { top: 20px; }
}
```
```css
@-moz-keyframes name {
from { top: 10px; }
to { top: 20px; }
}

View File

@@ -0,0 +1,48 @@
"use strict"
const _ = require("lodash")
const postcss = require("postcss")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "at-rule-whitelist"
const messages = ruleMessages(ruleName, {
rejected: name => `Unexpected at-rule "${name}"`,
})
const rule = function (whitelistInput) {
// To allow for just a string as a parameter (not only arrays of strings)
const whitelist = [].concat(whitelistInput)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: whitelist,
possible: [_.isString],
})
if (!validOptions) {
return
}
root.walkAtRules(atRule => {
const name = atRule.name
if (whitelist.indexOf(postcss.vendor.unprefixed(name).toLowerCase()) !== -1) {
return
}
report({
message: messages.rejected(name),
node: atRule,
result,
ruleName,
})
})
}
}
rule.primaryOptionArray = true
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,29 @@
"use strict"
const isStandardSyntaxAtRule = require("../utils/isStandardSyntaxAtRule")
const report = require("../utils/report")
module.exports = function (options) {
options.root.walkAtRules(atRule => {
if (!isStandardSyntaxAtRule(atRule)) {
return
}
checkColon(`@${atRule.name}${(atRule.raws.afterName || "")}${atRule.params}`, atRule.name.length, atRule)
})
function checkColon(source, index, node) {
options.locationChecker({
source,
index,
err: m => report({
message: m,
node,
index,
result: options.result,
ruleName: options.checkedRuleName,
}),
errTarget: `@${node.name}`,
})
}
}

View File

@@ -0,0 +1,62 @@
# block-closing-brace-empty-line-before
Require or disallow an empty line before the closing brace of blocks.
```css
a {
color: pink;
/* ← */
} /* ↑ */
/** ↑
* This line */
```
## Options
`string`: `"always-multi-line"|"never"`
### `always-multi-line`
The following patterns are considered warnings:
```css
a {
color: pink;
}
```
The following patterns are *not* considered warnings:
```css
a {
color: pink;
}
```
```css
a { color: pink; }
```
### `never`
The following patterns are considered warnings:
```css
a {
color: pink;
}
```
The following patterns are *not* considered warnings:
```css
a {
color: pink;
}
```
```css
a { color: pink; }
```

View File

@@ -0,0 +1,89 @@
"use strict"
const blockString = require("../../utils/blockString")
const hasBlock = require("../../utils/hasBlock")
const hasEmptyBlock = require("../../utils/hasEmptyBlock")
const hasEmptyLine = require("../../utils/hasEmptyLine")
const isSingleLineString = require("../../utils/isSingleLineString")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "block-closing-brace-empty-line-before"
const messages = ruleMessages(ruleName, {
expected: "Expected empty line before closing brace",
rejected: "Unexpected empty line before closing brace",
})
const rule = function (expectation) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always-multi-line",
"never",
],
})
if (!validOptions) {
return
}
// Check both kinds of statements: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
function check(statement) {
// Return early if blockless or has empty block
if (
!hasBlock(statement)
|| hasEmptyBlock(statement)
) {
return
}
// Get whitespace after ""}", ignoring extra semicolon
const before = (statement.raws.after || "").replace(/;+/, "")
if (before === undefined) {
return
}
// Calculate index
const statementString = statement.toString()
let index = statementString.length - 1
if (statementString[index - 1] === "\r") {
index -= 1
}
// Set expectation
const expectEmptyLineBefore = expectation === "always-multi-line"
&& !isSingleLineString(blockString(statement))
? true
: false
// Check for at least one empty line
const hasEmptyLineBefore = hasEmptyLine(before)
// Return if the expectation is met
if (expectEmptyLineBefore === hasEmptyLineBefore) {
return
}
const message = expectEmptyLineBefore
? messages.expected
: messages.rejected
report({
message,
result,
ruleName,
node: statement,
index,
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,174 @@
# block-closing-brace-newline-after
Require a newline or disallow whitespace after the closing brace of blocks.
```css
a { color: pink; }
a { color: red; }
/** ↑
* The newline after this brace */
```
This rule allows an end-of-line comment separated from the closing brace by spaces, as long as the comment contains no newlines. For example,
```css
a {
color: pink;
} /* end-of-line comment */
```
This rule allows a trailing semicolon after the closing brace of a block. For example,
```css
:root {
--toolbar-theme: {
background-color: hsl(120, 70%, 95%);
};
/* ↑
* This semicolon */
}
```
## Options
`string`: `"always"|"always-single-line"|"never-single-line"|"always-multi-line"|"never-multi-line"`
### `"always"`
There *must always* be a newline after the closing brace.
The following patterns are considered warnings:
```css
a { color: pink; }b { color: red; }
```
```css
a { color: pink;
} b { color: red; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
b { color: red; }
```
### `"always-single-line"`
There *must always* be a newline after the closing brace in single-line blocks.
The following patterns are considered warnings:
```css
a { color: pink; } b { color: red; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink;
} b { color: red; }
```
```css
a { color: pink; }
b { color: red; }
```
### `"never-single-line"`
There *must never* be whitespace after the closing brace in single-line blocks.
The following patterns are considered warnings:
```css
a { color: pink; } b { color: red; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }b { color: red; }
```
```css
a { color: pink;
} b { color: red; }
```
### `"always-multi-line"`
There *must always* be a newline after the closing brace in multi-line blocks.
The following patterns are considered warnings:
```css
a { color: pink;
}b { color: red; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }b { color: red; }
```
```css
a { color: pink;
}
b { color: red; }
```
### `"never-multi-line"`
There *must never* be whitespace after the closing brace in multi-line blocks.
The following patterns are considered warnings:
```css
a { color: pink;
} b { color: red; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; } b { color: red; }
```
```css
a { color: pink;
}b { color: red; }
```
## Optional secondary options
### `ignoreAtRules: ["/regex/", "non-regex"]`
Ignore specified at-rules.
For example, with `"always"` or `"always-multi-line"`.
Given:
```js
["if", "else"]
```
The following patterns are *not* considered warnings:
```css
@if ($var) {
color: pink;
} @else if ($var2) {
color: red;
} @else {
color: blue;
}
```
```css
@if ($var) { color: pink; } @else { color: blue; }
```

View File

@@ -0,0 +1,110 @@
"use strict"
const _ = require("lodash")
const blockString = require("../../utils/blockString")
const hasBlock = require("../../utils/hasBlock")
const optionsMatches = require("../../utils/optionsMatches")
const rawNodeString = require("../../utils/rawNodeString")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "block-closing-brace-newline-after"
const messages = ruleMessages(ruleName, {
expectedAfter: () => "Expected newline after \"}\"",
expectedAfterSingleLine: () => "Expected newline after \"}\" of a single-line block",
rejectedAfterSingleLine: () => "Unexpected whitespace after \"}\" of a single-line block",
expectedAfterMultiLine: () => "Expected newline after \"}\" of a multi-line block",
rejectedAfterMultiLine: () => "Unexpected whitespace after \"}\" of a multi-line block",
})
const rule = function (expectation, options) {
const checker = whitespaceChecker("newline", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"always-single-line",
"never-single-line",
"always-multi-line",
"never-multi-line",
],
}, {
actual: options,
possible: {
ignoreAtRules: [_.isString],
},
optional: true,
})
if (!validOptions) {
return
}
// Check both kinds of statements: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
function check(statement) {
if (!hasBlock(statement)) {
return
}
if (optionsMatches(options, "ignoreAtRules", statement.name)) {
return
}
const nextNode = statement.next()
if (!nextNode) {
return
}
// Allow an end-of-line comment x spaces after the brace
const nextNodeIsSingleLineComment = nextNode.type === "comment"
&& !/[^ ]/.test((nextNode.raws.before || ""))
&& nextNode.toString().indexOf("\n") === -1
const nodeToCheck = nextNodeIsSingleLineComment
? nextNode.next()
: nextNode
if (!nodeToCheck) {
return
}
let reportIndex = statement.toString().length
let source = rawNodeString(nodeToCheck)
// Skip a semicolon at the beginning, if any
if (
source
&& source[0] === ";"
) {
source = source.slice(1)
reportIndex++
}
// Only check one after, because there might be other
// spaces handled by the indentation rule
checker.afterOneOnly({
source,
index: -1,
lineCheckStr: blockString(statement),
err: msg => {
report({
message: msg,
node: statement,
index: reportIndex,
result,
ruleName,
})
},
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,81 @@
# block-closing-brace-newline-before
Require a newline or disallow whitespace before the closing brace of blocks.
```css
a { color: pink;
}
/** ↑
* The newline before this brace */
```
## Options
`string`: `"always"|"always-multi-line"|"never-multi-line"`
### `"always"`
There *must always* be a newline before the closing brace.
The following patterns are considered warnings:
```css
a { color: pink;}
```
The following patterns are *not* considered warnings:
```css
a { color: pink;
}
```
```css
a {
color: pink;
}
```
### `"always-multi-line"`
There *must always* be a newline before the closing brace in multi-line blocks.
The following patterns are considered warnings:
```css
a {
color: pink;}
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a { color: pink;
}
```
### `"never-multi-line"`
There *must never* be whitespace before the closing brace in multi-line blocks.
The following patterns are considered warnings:
```css
a {
color: pink; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a {
color: pink;}
```

View File

@@ -0,0 +1,103 @@
"use strict"
const _ = require("lodash")
const blockString = require("../../utils/blockString")
const hasBlock = require("../../utils/hasBlock")
const hasEmptyBlock = require("../../utils/hasEmptyBlock")
const isSingleLineString = require("../../utils/isSingleLineString")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "block-closing-brace-newline-before"
const messages = ruleMessages(ruleName, {
expectedBefore: "Expected newline before \"}\"",
expectedBeforeMultiLine: "Expected newline before \"}\" of a multi-line block",
rejectedBeforeMultiLine: "Unexpected whitespace before \"}\" of a multi-line block",
})
const rule = function (expectation) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"always-multi-line",
"never-multi-line",
],
})
if (!validOptions) {
return
}
// Check both kinds of statements: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
function check(statement) {
// Return early if blockless or has empty block
if (
!hasBlock(statement)
|| hasEmptyBlock(statement)
) {
return
}
// Ignore extra semicolon
const after = (statement.raws.after || "").replace(/;+/, "")
if (after === undefined) {
return
}
const blockIsMultiLine = !isSingleLineString(blockString(statement))
const statementString = statement.toString()
let index = statementString.length - 2
if (statementString[index - 1] === "\r") {
index -= 1
}
// We're really just checking whether a
// newline *starts* the block's final space -- between
// the last declaration and the closing brace. We can
// ignore any other whitespace between them, because that
// will be checked by the indentation rule.
if (
!_.startsWith(after, "\n")
&& !_.startsWith(after, "\r\n")
) {
if (expectation === "always") {
complain(messages.expectedBefore)
} else if (
blockIsMultiLine
&& expectation === "always-multi-line"
) {
complain(messages.expectedBeforeMultiLine)
}
}
if (
after !== ""
&& blockIsMultiLine
&& expectation === "never-multi-line"
) {
complain(messages.rejectedBeforeMultiLine)
}
function complain(message) {
report({
message,
result,
ruleName,
node: statement,
index,
})
}
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,158 @@
# block-closing-brace-space-after
Require a single space or disallow whitespace after the closing brace of blocks.
```css
a { color: pink; }
/** ↑
* The space after this brace */
```
This rule allows a trailing semicolon after the closing brace of a block. For example,
```css
:root {
--toolbar-theme: {
background-color: hsl(120, 70%, 95%);
};
/* ↑
* This semicolon */
}
```
## Options
`string`: `"always"|"never"|"always-single-line"|"never-single-line"|"always-multi-line"|"never-multi-line"`
### `"always"`
There *must always* be a single space after the closing brace.
The following patterns are considered warnings:
```css
a { color: pink; }b { color: red; }
```
```css
a { color: pink; }
b { color: red; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; } b { color: red; }
```
### `"never"`
There *must never* be whitespace after the closing brace.
The following patterns are considered warnings:
```css
a { color: pink; } b { color: red; }
```
```css
a { color: pink; }
b { color: red; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }b { color: red; }
```
```css
a { color: pink;
}b { color: red; }
```
### `"always-single-line"`
There *must always* be a single space after the closing brace in single-line blocks.
The following patterns are considered warnings:
```css
a { color: pink; }b { color: red; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; } b { color: red; }
```
```css
a { color: pink;
}b { color: red; }
```
### `"never-single-line"`
There *must never* be whitespace after the closing brace in single-line blocks.
The following patterns are considered warnings:
```css
a { color: pink; } b { color: red; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }b { color: red; }
```
```css
a { color: pink;
} b { color: red; }
```
### `"always-multi-line"`
There *must always* be a single space after the closing brace in multi-line blocks.
The following patterns are considered warnings:
```css
a { color: pink;
}b { color: red; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }b { color: red; }
```
```css
a { color: pink;
} b { color: red; }
```
### `"never-multi-line"`
There *must never* be whitespace after the closing brace in multi-line blocks.
The following patterns are considered warnings:
```css
a { color: pink;
} b { color: red; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; } b { color: red; }
```
```css
a { color: pink;
}b { color: red; }
```

View File

@@ -0,0 +1,86 @@
"use strict"
const blockString = require("../../utils/blockString")
const hasBlock = require("../../utils/hasBlock")
const rawNodeString = require("../../utils/rawNodeString")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "block-closing-brace-space-after"
const messages = ruleMessages(ruleName, {
expectedAfter: () => "Expected single space after \"}\"",
rejectedAfter: () => "Unexpected whitespace after \"}\"",
expectedAfterSingleLine: () => "Expected single space after \"}\" of a single-line block",
rejectedAfterSingleLine: () => "Unexpected whitespace after \"}\" of a single-line block",
expectedAfterMultiLine: () => "Expected single space after \"}\" of a multi-line block",
rejectedAfterMultiLine: () => "Unexpected whitespace after \"}\" of a multi-line block",
})
const rule = function (expectation) {
const checker = whitespaceChecker("space", expectation, messages)
return function (root, result) {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"never",
"always-single-line",
"never-single-line",
"always-multi-line",
"never-multi-line",
],
})
if (!validOptions) {
return
}
// Check both kinds of statements: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
function check(statement) {
const nextNode = statement.next()
if (!nextNode) {
return
}
if (!hasBlock(statement)) {
return
}
let reportIndex = statement.toString().length
let source = rawNodeString(nextNode)
// Skip a semicolon at the beginning, if any
if (
source
&& source[0] === ";"
) {
source = source.slice(1)
reportIndex++
}
checker.after({
source,
index: -1,
lineCheckStr: blockString(statement),
err: msg => {
report({
message: msg,
node: statement,
index: reportIndex,
result,
ruleName,
})
},
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,151 @@
# block-closing-brace-space-before
Require a single space or disallow whitespace before the closing brace of blocks.
```css
a { color: pink; }
/** ↑
* The space before this brace */
```
## Options
`string`: `"always"|"never"|"always-single-line"|"never-single-line"|"always-multi-line"|"never-multi-line"`
### `"always"`
There *must always* be a single space before the closing brace.
The following patterns are considered warnings:
```css
a { color: pink;}
```
```css
a
{ color: pink;}
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a {
color: pink; }
```
### `"never"`
There *must never* be whitespace before the closing brace.
The following patterns are considered warnings:
```css
a { color: pink; }
```
```css
a
{ color: pink; }
```
The following patterns are *not* considered warnings:
```css
a{ color: pink;}
```
```css
a{
color: pink;}
```
### `"always-single-line"`
There *must always* be a single space before the closing brace in single-line blocks.
The following patterns are considered warnings:
```css
a { color: pink;}
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a {
color: pink;}
```
### `"never-single-line"`
There *must never* be whitespace before the closing brace in single-line blocks.
The following patterns are considered warnings:
```css
a { color: pink; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink;}
```
```css
a {
color: pink; }
```
### `"always-multi-line"`
There *must always* be a single space before the closing brace in multi-line blocks.
The following patterns are considered warnings:
```css
a {
color: pink;}
```
The following patterns are *not* considered warnings:
```css
a { color: pink;}
```
```css
a {
color: pink; }
```
### `"never-multi-line"`
There *must never* be whitespace before the closing brace in multi-line blocks.
The following patterns are considered warnings:
```css
a {
color: pink; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a {
color: pink;}
```

View File

@@ -0,0 +1,81 @@
"use strict"
const blockString = require("../../utils/blockString")
const hasBlock = require("../../utils/hasBlock")
const hasEmptyBlock = require("../../utils/hasEmptyBlock")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "block-closing-brace-space-before"
const messages = ruleMessages(ruleName, {
expectedBefore: () => "Expected single space before \"}\"",
rejectedBefore: () => "Unexpected whitespace before \"}\"",
expectedBeforeSingleLine: () => "Expected single space before \"}\" of a single-line block",
rejectedBeforeSingleLine: () => "Unexpected whitespace before \"}\" of a single-line block",
expectedBeforeMultiLine: () => "Expected single space before \"}\" of a multi-line block",
rejectedBeforeMultiLine: () => "Unexpected whitespace before \"}\" of a multi-line block",
})
const rule = function (expectation) {
const checker = whitespaceChecker("space", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual : expectation,
possible : [
"always",
"never",
"always-single-line",
"never-single-line",
"always-multi-line",
"never-multi-line",
],
})
if (!validOptions) {
return
}
// Check both kinds of statement: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
function check(statement) {
// Return early if blockless or has empty block
if (
!hasBlock(statement)
|| hasEmptyBlock(statement)
) {
return
}
const source = blockString(statement)
const statementString = statement.toString()
let index = statementString.length - 2
if (statementString[index - 1] === "\r") {
index -= 1
}
checker.before({
source,
index: source.length - 1,
err: msg => {
report({
message: msg,
node: statement,
index,
result,
ruleName,
})
},
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,37 @@
# block-no-empty
Disallow empty blocks.
```css
a { }
/** ↑
* Blocks like this */
```
## Options
### `true`
The following patterns are considered warnings:
```css
a {}
```
```css
a { }
```
```css
@media print { a {} }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
@media print { a { color: pink; } }
```

View File

@@ -0,0 +1,51 @@
"use strict"
const beforeBlockString = require("../../utils/beforeBlockString")
const hasEmptyBlock = require("../../utils/hasEmptyBlock")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "block-no-empty"
const messages = ruleMessages(ruleName, {
rejected: "Unexpected empty block",
})
const rule = function (actual) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual })
if (!validOptions) {
return
}
// Check both kinds of statements: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
function check(statement) {
if (!hasEmptyBlock(statement)) {
return
}
let index = beforeBlockString(statement, { noRawBefore: true }).length
// For empty blocks when using SugarSS parser
if (statement.raws.between === undefined) {
index--
}
report({
message: messages.rejected,
node: statement,
index,
result,
ruleName,
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,78 @@
# block-no-single-line
***Deprecated: instead use the [block-opening-brace-newline-after](../block-opening-brace-newline-after/README.md#always) and [block-closing-brace-newline-before](../block-closing-brace-newline-before/README.md#always) rules with the option `"always"`. See [the FAQs for an example](../../../docs/user-guide/faq.md#how-do-i-disallow-single-line-blocks).***
Disallow single-line blocks.
```css
a { color: pink; top: 0; }
/** ↑ ↑
* Declaration blocks like this */
```
## Options
### `true`
The following patterns are considered warnings:
```css
a { color: pink; }
```
```css
a,
b { color: pink; }
```
```css
a { color: pink; top: 1px; }
```
```css
@media print { a { color: pink; } }
```
```css
@media print {
a { color: pink; }
}
```
```css
a {
color: red;
@media print { color: pink; }
}
```
The following patterns are *not* considered warnings:
```css
a {
color: pink;
}
```
```css
a, b {
color: pink;
}
```
```css
@media print {
a {
color: pink;
}
}
```
```css
a {
color: red;
@media print {
color: pink;
}
}
```

View File

@@ -0,0 +1,61 @@
"use strict"
const beforeBlockString = require("../../utils/beforeBlockString")
const blockString = require("../../utils/blockString")
const hasBlock = require("../../utils/hasBlock")
const hasEmptyBlock = require("../../utils/hasEmptyBlock")
const isSingleLineString = require("../../utils/isSingleLineString")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "block-no-single-line"
const messages = ruleMessages(ruleName, {
rejected: "Unexpected single-line block",
})
const rule = function (actual) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual })
if (!validOptions) {
return
}
result.warn((
"'block-no-single-line' has been deprecated and in 8.0 will be removed. " +
"Instead use 'block-opening-brace-newline-after' and 'block-closing-brace-newline-before' with the \"always\" option."
), {
stylelintType: "deprecation",
stylelintReference: "https://stylelint.io/user-guide/rules/block-no-single-line/",
})
// Check both kinds of statements: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
function check(statement) {
if (
!hasBlock(statement)
|| hasEmptyBlock(statement)
) {
return
}
if (!isSingleLineString(blockString(statement))) {
return
}
report({
message: messages.rejected,
node: statement,
index: beforeBlockString(statement, { noRawBefore: true }).length,
result,
ruleName,
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,109 @@
# block-opening-brace-newline-after
Require a newline after the opening brace of blocks.
```css
a {
color: pink; }
/** ↑
* The newline after this brace */
```
This rule allows an end-of-line comment followed by a newline. For example,
```css
a { /* end-of-line comment */
color: pink;
}
```
Refer to [the FAQ](../../../docs/user-guide/faq.md#how-do-i-disallow-single-line-blocks) for more information on using this rule with [`block-opening-brace-newline-before`](../block-opening-brace-newline-before/README.md) to disallow single-line rules.
## Options
`string`: `"always"|"always-multi-line"|"never-multi-line"`
### `"always"`
There *must always* be a newline after the opening brace.
The following patterns are considered warnings:
```css
a{ color: pink; }
```
```css
a{ color: pink;
}
```
```css
a{ /* end-of-line comment
with a newline */
color: pink;
}
```
The following patterns are *not* considered warnings:
```css
a {
color: pink; }
```
```css
a
{
color: pink; }
```
```css
a { /* end-of-line comment */
color: pink;
}
```
### `"always-multi-line"`
There *must always* be a newline after the opening brace in multi-line blocks.
The following patterns are considered warnings:
```css
a{color: pink;
}
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a {
color: pink; }
```
### `"never-multi-line"`
There *must never* be whitespace after the opening brace in multi-line blocks.
The following patterns are considered warnings:
```css
a { color: pink;
}
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a {color: pink;
}
```

View File

@@ -0,0 +1,77 @@
"use strict"
const beforeBlockString = require("../../utils/beforeBlockString")
const blockString = require("../../utils/blockString")
const hasBlock = require("../../utils/hasBlock")
const hasEmptyBlock = require("../../utils/hasEmptyBlock")
const nextNonCommentNode = require("../../utils/nextNonCommentNode")
const rawNodeString = require("../../utils/rawNodeString")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "block-opening-brace-newline-after"
const messages = ruleMessages(ruleName, {
expectedAfter: () => "Expected newline after \"{\"",
expectedAfterMultiLine: () => "Expected newline after \"{\" of a multi-line block",
rejectedAfterMultiLine: () => "Unexpected whitespace after \"{\" of a multi-line block",
})
const rule = function (expectation) {
const checker = whitespaceChecker("newline", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"always-multi-line",
"never-multi-line",
],
})
if (!validOptions) {
return
}
// Check both kinds of statement: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
function check(statement) {
// Return early if blockless or has an empty block
if (
!hasBlock(statement)
|| hasEmptyBlock(statement)
) {
return
}
// Allow an end-of-line comment
const nodeToCheck = nextNonCommentNode(statement.first)
if (!nodeToCheck) {
return
}
checker.afterOneOnly({
source: rawNodeString(nodeToCheck),
index: -1,
lineCheckStr: blockString(statement),
err: m => {
report({
message: m,
node: statement,
index: beforeBlockString(statement, { noRawBefore: true }).length + 1,
result,
ruleName,
})
},
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,153 @@
# block-opening-brace-newline-before
Require a newline or disallow whitespace before the opening brace of blocks.
```css
a
{ color: pink; }
/** ↑
* The newline before this brace */
```
Refer to [the FAQ](../../../docs/user-guide/faq.md#how-do-i-disallow-single-line-blocks) for more information on using this rule with [`block-opening-brace-newline-after`](../block-opening-brace-newline-after/README.md) to disallow single-line rules.
## Options
`string`: `"always"|"always-single-line"|"never-single-line"|"always-multi-line"|"never-multi-line"`
### `"always"`
There *must always* be a newline before the opening brace.
The following patterns are considered warnings:
```css
a{ color: pink; }
```
```css
a{ color: pink;
}
```
The following patterns are *not* considered warnings:
```css
a
{ color: pink; }
```
```css
a
{
color: pink; }
```
```css
a /* foo */
{
color: pink;
}
```
### `"always-single-line"`
There *must always* be a newline before the opening brace in single-line blocks.
The following patterns are considered warnings:
```css
a{ color: pink; }
```
The following patterns are *not* considered warnings:
```css
a
{ color: pink; }
```
```css
a{
color: pink; }
```
### `"never-single-line"`
There *must never* be whitespace before the opening brace in single-line blocks.
The following patterns are considered warnings:
```css
a { color: pink; }
```
The following patterns are *not* considered warnings:
```css
a{ color: pink; }
```
```css
a {
color: pink; }
```
### `"always-multi-line"`
There *must always* be a newline before the opening brace in multi-line blocks.
The following patterns are considered warnings:
```css
a{
color: pink; }
```
```css
a {
color: pink; }
```
The following patterns are *not* considered warnings:
```css
a{ color: pink; }
```
```css
a { color: pink; }
```
```css
a
{ color: pink; }
```
```css
a
{
color: pink; }
```
### `"never-multi-line"`
There *must never* be whitespace before the opening brace in multi-line blocks.
The following patterns are considered warnings:
```css
a {
color: pink; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a{
color: pink;}
```

View File

@@ -0,0 +1,81 @@
"use strict"
const beforeBlockString = require("../../utils/beforeBlockString")
const blockString = require("../../utils/blockString")
const hasBlock = require("../../utils/hasBlock")
const hasEmptyBlock = require("../../utils/hasEmptyBlock")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "block-opening-brace-newline-before"
const messages = ruleMessages(ruleName, {
expectedBefore: () => "Expected newline before \"{\"",
expectedBeforeSingleLine: () => "Expected newline before \"{\" of a single-line block",
rejectedBeforeSingleLine: () => "Unexpected whitespace before \"{\" of a single-line block",
expectedBeforeMultiLine: () => "Expected newline before \"{\" of a multi-line block",
rejectedBeforeMultiLine: () => "Unexpected whitespace before \"{\" of a multi-line block",
})
const rule = function (expectation) {
const checker = whitespaceChecker("newline", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual : expectation,
possible : [
"always",
"always-single-line",
"never-single-line",
"always-multi-line",
"never-multi-line",
],
})
if (!validOptions) {
return
}
// Check both kinds of statement: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
function check(statement) {
// Return early if blockless or has an empty block
if (
!hasBlock(statement)
|| hasEmptyBlock(statement)
) {
return
}
const source = beforeBlockString(statement)
const beforeBraceNoRaw = beforeBlockString(statement, { noRawBefore: true })
let index = beforeBraceNoRaw.length - 1
if (beforeBraceNoRaw[index - 1] === "\r") {
index -= 1
}
checker.beforeAllowingIndentation({
lineCheckStr: blockString(statement),
source,
index: source.length,
err: m => {
report({
message: m,
node: statement,
index,
result,
ruleName,
})
},
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,151 @@
# block-opening-brace-space-after
Require a single space or disallow whitespace after the opening brace of blocks.
```css
a { color: pink; }
/** ↑
* The space after this brace */
```
## Options
`string`: `"always"|"never"|"always-single-line"|"never-single-line"|"always-multi-line"|"never-multi-line"`
### `"always"`
There *must always* be a single space after the opening brace.
The following patterns are considered warnings:
```css
a {color: pink; }
```
```css
a {
color: pink; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a { color: pink;
}
```
### `"never"`
There *must never* be whitespace after the opening brace.
The following patterns are considered warnings:
```css
a { color: pink; }
```
```css
a {
color: pink; }
```
The following patterns are *not* considered warnings:
```css
a {color: pink; }
```
```css
a
{color: pink; }
```
### `"always-single-line"`
There *must always* be a single space after the opening brace in single-line blocks.
The following patterns are considered warnings:
```css
a {color: pink; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a {color: pink;
}
```
### `"never-single-line"`
There *must never* be whitespace after the opening brace in single-line blocks.
The following patterns are considered warnings:
```css
a { color: pink; }
```
The following patterns are *not* considered warnings:
```css
a {color: pink; }
```
```css
a { color: pink;
}
```
### `"always-multi-line"`
There *must always* be a single space after the opening brace in multi-line blocks.
The following patterns are considered warnings:
```css
a {color: pink;
}
```
The following patterns are *not* considered warnings:
```css
a {color: pink; }
```
```css
a { color: pink;
}
```
### `"never-multi-line"`
There *must never* be whitespace after the opening brace in multi-line blocks.
The following patterns are considered warnings:
```css
a { color: pink;
}
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a {color: pink;
}
```

View File

@@ -0,0 +1,73 @@
"use strict"
const beforeBlockString = require("../../utils/beforeBlockString")
const blockString = require("../../utils/blockString")
const hasBlock = require("../../utils/hasBlock")
const hasEmptyBlock = require("../../utils/hasEmptyBlock")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "block-opening-brace-space-after"
const messages = ruleMessages(ruleName, {
expectedAfter: () => "Expected single space after \"{\"",
rejectedAfter: () => "Unexpected whitespace after \"{\"",
expectedAfterSingleLine: () => "Expected single space after \"{\" of a single-line block",
rejectedAfterSingleLine: () => "Unexpected whitespace after \"{\" of a single-line block",
expectedAfterMultiLine: () => "Expected single space after \"{\" of a multi-line block",
rejectedAfterMultiLine: () => "Unexpected whitespace after \"{\" of a multi-line block",
})
const rule = function (expectation) {
const checker = whitespaceChecker("space", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"never",
"always-single-line",
"never-single-line",
"always-multi-line",
"never-multi-line",
],
})
if (!validOptions) {
return
}
// Check both kinds of statements: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
function check(statement) {
// Return early if blockless or has an empty block
if (
!hasBlock(statement)
|| hasEmptyBlock(statement)
) {
return
}
checker.after({
source: blockString(statement),
index: 0,
err: m => {
report({
message: m,
node: statement,
index: beforeBlockString(statement, { noRawBefore: true }).length + 1,
result,
ruleName,
})
},
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,172 @@
# block-opening-brace-space-before
Require a single space or disallow whitespace before the opening brace of blocks.
```css
a { color: pink; }
/** ↑
* The space before this brace */
```
## Options
`string`: `"always"|"never"|"always-single-line"|"never-single-line"|"always-multi-line"|"never-multi-line"`
### `"always"`
There *must always* be a single space before the opening brace.
The following patterns are considered warnings:
```css
a{ color: pink; }
```
```css
a
{ color: pink; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a {
color: pink; }
```
### `"never"`
There *must never* be whitespace before the opening brace.
The following patterns are considered warnings:
```css
a { color: pink; }
```
```css
a
{ color: pink; }
```
The following patterns are *not* considered warnings:
```css
a{ color: pink; }
```
```css
a{
color: pink; }
```
### `"always-single-line"`
There *must always* be a single space before the opening brace in single-line blocks.
The following patterns are considered warnings:
```css
a{ color: pink; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a{
color: pink; }
```
### `"never-single-line"`
There *must never* be whitespace before the opening brace in single-line blocks.
The following patterns are considered warnings:
```css
a { color: pink; }
```
The following patterns are *not* considered warnings:
```css
a{ color: pink; }
```
```css
a {
color: pink; }
```
### `"always-multi-line"`
There *must always* be a single space before the opening brace in multi-line blocks.
The following patterns are considered warnings:
```css
a{
color: pink; }
```
The following patterns are *not* considered warnings:
```css
a{ color: pink; }
```
```css
a {
color: pink; }
```
### `"never-multi-line"`
There *must never* be whitespace before the opening brace in multi-line blocks.
The following patterns are considered warnings:
```css
a {
color: pink; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a{
color: pink;}
```
## Optional secondary options
### `ignoreAtRules: ["/regex/", "non-regex"]`
Given:
```js
["/fo/"]
```
The following patterns are *not* considered warnings:
```css
@for ...
{}
```
```css
@for ...{}
```

View File

@@ -0,0 +1,95 @@
"use strict"
const _ = require("lodash")
const beforeBlockString = require("../../utils/beforeBlockString")
const blockString = require("../../utils/blockString")
const hasBlock = require("../../utils/hasBlock")
const hasEmptyBlock = require("../../utils/hasEmptyBlock")
const optionsMatches = require("../../utils/optionsMatches")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "block-opening-brace-space-before"
const messages = ruleMessages(ruleName, {
expectedBefore: () => "Expected single space before \"{\"",
rejectedBefore: () => "Unexpected whitespace before \"{\"",
expectedBeforeSingleLine: () => "Expected single space before \"{\" of a single-line block",
rejectedBeforeSingleLine: () => "Unexpected whitespace before \"{\" of a single-line block",
expectedBeforeMultiLine: () => "Expected single space before \"{\" of a multi-line block",
rejectedBeforeMultiLine: () => "Unexpected whitespace before \"{\" of a multi-line block",
})
const rule = function (expectation, options) {
const checker = whitespaceChecker("space", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"never",
"always-single-line",
"never-single-line",
"always-multi-line",
"never-multi-line",
],
}, {
actual: options,
possible: {
ignoreAtRules: [_.isString],
},
optional: true,
})
if (!validOptions) {
return
}
// Check both kinds of statements: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
function check(statement) {
// Return early if blockless or has an empty block
if (
!hasBlock(statement)
|| hasEmptyBlock(statement)
) {
return
}
// Return early if at-rule is to be ignored
if (optionsMatches(options, "ignoreAtRules", statement.name)) {
return
}
const source = beforeBlockString(statement)
const beforeBraceNoRaw = beforeBlockString(statement, { noRawBefore: true })
let index = beforeBraceNoRaw.length - 1
if (beforeBraceNoRaw[index - 1] === "\r") {
index -= 1
}
checker.before({
source,
index: source.length,
lineCheckStr: blockString(statement),
err: m => {
report({
message: m,
node: statement,
index,
result,
ruleName,
})
},
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,51 @@
"use strict"
const hasEmptyLine = require("../utils/hasEmptyLine")
const isSingleLineString = require("../utils/isSingleLineString")
const optionsMatches = require("../utils/optionsMatches")
const report = require("../utils/report")
module.exports = function (opts) {
let expectEmptyLineBefore = opts.expectation.indexOf("always") !== -1 ? true : false
// Optionally ignore the expectation if a comment precedes this node
if (optionsMatches(opts.options, "ignore", "after-comment") && opts.rule.prev() && opts.rule.prev().type === "comment") {
return
}
// Ignore if the expectation is for multiple and the rule is single-line
if (opts.expectation.indexOf("multi-line") !== -1 && isSingleLineString(opts.rule.toString())) {
return
}
// Optionally reverse the expectation for the first nested node
if (optionsMatches(opts.options, "except", "first-nested") && opts.rule === opts.rule.parent.first) {
expectEmptyLineBefore = !expectEmptyLineBefore
}
// Optionally reverse the expectation if a rule precedes this node
if (optionsMatches(opts.options, "except", "after-rule") && opts.rule.prev() && opts.rule.prev().type === "rule") {
expectEmptyLineBefore = !expectEmptyLineBefore
}
// Optionally reverse the expectation for single line comments
if (optionsMatches(opts.options, "except", "after-single-line-comment") && opts.rule.prev() && opts.rule.prev().type === "comment" && isSingleLineString(opts.rule.prev().toString())) {
expectEmptyLineBefore = !expectEmptyLineBefore
}
const hasEmptyLineBefore = hasEmptyLine(opts.rule.raws.before)
// Return if the expectation is met
if (expectEmptyLineBefore === hasEmptyLineBefore) {
return
}
const message = expectEmptyLineBefore ? opts.messages.expected : opts.messages.rejected
report({
message,
node: opts.rule,
result: opts.result,
ruleName: opts.checkedRuleName,
})
}

View File

@@ -0,0 +1,49 @@
# color-hex-case
Specify lowercase or uppercase for hex colors.
```css
a { color: #fff }
/** ↑
* These hex colors */
```
## Options
`string`: `"lower"|"upper"`
### `"lower"`
The following patterns are considered warnings:
```css
a { color: #FFF; }
```
The following patterns are *not* considered warnings:
```css
a { color: #000; }
```
```css
a { color: #fff; }
```
### `"upper"`
The following patterns are considered warnings:
```css
a { color: #fff; }
```
The following patterns are *not* considered warnings:
```css
a { color: #000; }
```
```css
a { color: #FFF; }
```

View File

@@ -0,0 +1,61 @@
"use strict"
const blurFunctionArguments = require("../../utils/blurFunctionArguments")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const styleSearch = require("style-search")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "color-hex-case"
const messages = ruleMessages(ruleName, {
expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`,
})
const rule = function (expectation) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"lower",
"upper",
],
})
if (!validOptions) {
return
}
root.walkDecls(decl => {
const declString = blurFunctionArguments(decl.toString(), "url")
styleSearch({ source: declString, target: "#" }, match => {
const hexMatch = /^#[0-9A-Za-z]+/.exec(declString.substr(match.startIndex))
if (!hexMatch) {
return
}
const hexValue = hexMatch[0]
const hexValueLower = hexValue.toLowerCase()
const hexValueUpper = hexValue.toUpperCase()
const expectedHex = expectation === "lower"
? hexValueLower
: hexValueUpper
if (hexValue === expectedHex) {
return
}
report({
message: messages.expected(hexValue, expectedHex),
node: decl,
index: match.startIndex,
result,
ruleName,
})
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,61 @@
# color-hex-length
Specify short or long notation for hex colors.
```css
a { color: #fff }
/** ↑
* These hex colors */
```
## Options
`string`: `"short"|"long"`
### `"short"`
The following patterns are considered warnings:
```css
a { color: #ffffff; }
```
```css
a { color: #fffffaa; }
```
The following patterns are *not* considered warnings:
```css
a { color: #fff; }
```
```css
a { color: #fffa; }
```
```css
a { color: #a4a4a4; }
```
### `"long"`
The following patterns are considered warnings:
```css
a { color: #fff; }
```
```css
a { color: #fffa; }
```
The following patterns are *not* considered warnings:
```css
a { color: #ffffff; }
```
```css
a { color: #fffffaa; }
```

View File

@@ -0,0 +1,102 @@
"use strict"
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const styleSearch = require("style-search")
const ruleName = "color-hex-length"
const messages = ruleMessages(ruleName, {
expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`,
})
const rule = function (expectation) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"short",
"long",
],
})
if (!validOptions) {
return
}
root.walkDecls(decl => {
const declString = decl.toString()
styleSearch({ source: declString, target: "#" }, match => {
const hexMatch = /^#[0-9A-Za-z]+/.exec(declString.substr(match.startIndex))
if (!hexMatch) {
return
}
const hexValue = hexMatch[0]
if (
expectation === "long"
&& hexValue.length !== 4
&& hexValue.length !== 5
) {
return
}
if (
expectation === "short"
&& (
hexValue.length < 6
|| !canShrink(hexValue)
)
) {
return
}
const variant = expectation === "long"
? longer
: shorter
report({
message: messages.expected(hexValue, variant(hexValue)),
node: decl,
index: match.startIndex,
result,
ruleName,
})
})
})
}
}
function canShrink(hex) {
hex = hex.toLowerCase()
return hex[1] === hex[2]
&& hex[3] === hex[4]
&& hex[5] === hex[6]
&& (hex.length === 7
|| hex.length === 9
&& hex[7] === hex[8]
)
}
function shorter(hex) {
let hexVariant = "#"
for (let i = 1; i < hex.length; i = i + 2) {
hexVariant += hex[i]
}
return hexVariant
}
function longer(hex) {
let hexVariant = "#"
for (let i = 1; i < hex.length; i++) {
hexVariant += hex[i] + hex[i]
}
return hexVariant
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

159
node_modules/stylelint/lib/rules/color-named/README.md generated vendored Normal file
View File

@@ -0,0 +1,159 @@
# color-named
Require (where possible) or disallow named colors.
```css
a { color: black }
/** ↑
* These named colors */
```
## Options
`string`: `"always-where-possible"|"never"`
### `"always-where-possible"`
Colors *must always*, where possible, be named.
This will warn if a hex (3, 4, 6 and 8 digit), `rgb()`, `rgba()`, `hsl()`, `hsla()`, `hwb()` or `gray()` color can be represented as a named color.
The following patterns are considered warnings:
```css
a { color: #000; }
```
```css
a { color: #f000; }
```
```css
a { color: #ff000000; }
```
```css
a { color: rgb(0, 0, 0); }
```
```css
a { color: rgb(0%, 0%, 0%); }
```
```css
a { color: rgba(0, 0, 0, 0); }
```
```css
a { color: hsl(0, 0%, 0%); }
```
```css
a { color: hwb(0, 0%, 100%); }
```
```css
a { color: gray(0); }
```
The following patterns are *not* considered warnings:
```css
a { color: black; }
```
```css
a { color: rgb(10, 0, 0); }
```
```css
a { color: rgb(0, 0, 0, 0.5); }
```
### `"never"`
Colors *must never* be named.
The following patterns are considered warnings:
```css
a { color: black; }
```
```css
a { color: white; }
```
The following patterns are *not* considered warnings:
```css
a { color: #000; }
```
```css
a { color: rgb(0, 0, 0); }
```
```css
a { color: var(--white); }
```
```scss
a { color: $blue; }
```
```less
a { color: @blue; }
```
## Optional secondary options
### `ignore: ["inside-function"]`
Ignore colors that are inside a function.
For example, with `"never"`.
The following patterns are *not* considered warnings:
```css
a {
color: map-get($colour, blue);
}
```
```css
a {
background-image: url(red);
}
```
### `ignoreProperties: ["/regex/", "string"]`
For example with `"never"`.
Given:
```js
["/^my-/", "composes"]
```
The following patterns are *not* considered warnings:
```css
a {
my-property: red;
}
```
```css
a {
my-other-property: red;
}
```
```css
a {
composes: red from './index.css';
}
```

145
node_modules/stylelint/lib/rules/color-named/index.js generated vendored Normal file
View File

@@ -0,0 +1,145 @@
"use strict"
const _ = require("lodash")
const declarationValueIndex = require("../../utils/declarationValueIndex")
const isStandardSyntaxFunction = require("../../utils/isStandardSyntaxFunction")
const isStandardSyntaxValue = require("../../utils/isStandardSyntaxValue")
const keywordSets = require("../../reference/keywordSets")
const namedColorData = require("../../reference/namedColorData")
const optionsMatches = require("../../utils/optionsMatches")
const propertySets = require("../../reference/propertySets")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const valueParser = require("postcss-value-parser")
const ruleName = "color-named"
const messages = ruleMessages(ruleName, {
expected: (named, original) => `Expected "${original}" to be "${named}"`,
rejected: named => `Unexpected named color "${named}"`,
})
// Todo tested on case insensivity
const NODE_TYPES = [
"word",
"function",
]
const rule = function (expectation, options) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"never",
"always-where-possible",
],
}, {
actual: options,
possible: {
ignoreProperties: [_.isString],
ignore: ["inside-function"],
},
optional: true,
})
if (!validOptions) {
return
}
const namedColors = Object.keys(namedColorData)
root.walkDecls(decl => {
if (propertySets.acceptCustomIdents.has(decl.prop)) {
return
}
// Return early if the property is to be ignored
if (optionsMatches(options, "ignoreProperties", decl.prop)) {
return
}
valueParser(decl.value).walk(node => {
const value = node.value,
type = node.type,
sourceIndex = node.sourceIndex
if (
optionsMatches(options, "ignore", "inside-function")
&& type === "function"
) {
return false
}
if (!isStandardSyntaxFunction(node)) {
return false
}
if (!isStandardSyntaxValue(value)) {
return
}
// Return early if neither a word nor a function
if (NODE_TYPES.indexOf(type) === -1) {
return
}
// Check for named colors for "never" option
if (
expectation === "never"
&& type === "word"
&& namedColors.indexOf(value.toLowerCase()) !== -1
) {
complain(messages.rejected(value), decl, declarationValueIndex(decl) + sourceIndex)
return
}
// Check "always-where-possible" option ...
if (expectation !== "always-where-possible") {
return
}
// First by checking for alternative color function representations ...
if (
type === "function"
&& keywordSets.colorFunctionNames.has(value.toLowerCase())
) {
// Remove all spaces to match what's in `representations`
const normalizedFunctionString = valueParser.stringify(node).replace(/\s+/g, "")
let namedColor
for (let i = 0, l = namedColors.length; i < l; i++) {
namedColor = namedColors[i]
if (namedColorData[namedColor].func.indexOf(normalizedFunctionString.toLowerCase()) !== -1) {
complain(messages.expected(namedColor, normalizedFunctionString), decl, declarationValueIndex(decl) + sourceIndex)
return // Exit as soon as a problem is found
}
}
return
}
// Then by checking for alternative hex representations
let namedColor
for (let i = 0, l = namedColors.length; i < l; i++) {
namedColor = namedColors[i]
if (namedColorData[namedColor].hex.indexOf(value.toLowerCase()) !== -1) {
complain(messages.expected(namedColor, value), decl, declarationValueIndex(decl) + sourceIndex)
return // Exit as soon as a problem is found
}
}
})
})
function complain(message, node, index) {
report({
result,
ruleName,
message,
node,
index,
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,51 @@
# color-no-hex
Disallow hex colors.
```css
a { color: #333 }
/** ↑
* These hex colors */
```
## Options
### `true`
The following patterns are considered warnings:
```css
a { color: #000; }
```
```css
a { color: #fff1aa; }
```
```css
a { color: #123456aa; }
```
Hex values that are not valid also cause warnings:
```css
a { color: #foobar; }
```
```css
a { color: #0000000000000000; }
```
The following patterns are *not* considered warnings:
```css
a { color: black; }
```
```css
a { color: rgb(0, 0, 0); }
```
```css
a { color: rgba(0, 0, 0, 1); }
```

52
node_modules/stylelint/lib/rules/color-no-hex/index.js generated vendored Normal file
View File

@@ -0,0 +1,52 @@
"use strict"
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const styleSearch = require("style-search")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "color-no-hex"
const messages = ruleMessages(ruleName, {
rejected: hex => `Unexpected hex color "${hex}"`,
})
const rule = function (actual) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual })
if (!validOptions) {
return
}
root.walkDecls(decl => {
const declString = decl.toString()
styleSearch({ source: declString, target: "#" }, match => {
// If there's not a colon, comma, or whitespace character before, we'll assume this is
// not intended to be a hex color, but is instead something like the
// hash in a url() argument
if (!/[:,\s]/.test(declString[match.startIndex - 1])) {
return
}
const hexMatch = /^#[0-9A-Za-z]+/.exec(declString.substr(match.startIndex))
if (!hexMatch) {
return
}
const hexValue = hexMatch[0]
report({
message: messages.rejected(hexValue),
node: decl,
index: match.startIndex,
result,
ruleName,
})
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,47 @@
# color-no-invalid-hex
Disallow invalid hex colors.
```css
a { color: #y3 }
/** ↑
* These hex colors */
```
Longhand hex colors can be either 6 or 8 (with alpha channel) hexadecimal characters. And their shorthand variants are 3 and 4 characters respectively.
## Options
### `true`
The following patterns are considered warnings:
```css
a { color: #00; }
```
```css
a { color: #fff1az; }
```
```css
a { color: #12345aa; }
```
The following patterns are *not* considered warnings:
```css
a { color: #000; }
```
```css
a { color: #000f; }
```
```css
a { color: #fff1a0; }
```
```css
a { color: #123450aa; }
```

View File

@@ -0,0 +1,57 @@
"use strict"
const isValidHex = require("../../utils/isValidHex")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const styleSearch = require("style-search")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "color-no-invalid-hex"
const messages = ruleMessages(ruleName, {
rejected: hex => `Unexpected invalid hex color "${hex}"`,
})
const rule = function (actual) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual })
if (!validOptions) {
return
}
root.walkDecls(decl => {
const declString = decl.toString()
styleSearch({ source: declString, target: "#" }, match => {
// If there's not a colon, comma, or whitespace character before, we'll assume this is
// not intended to be a hex color, but is instead something like the
// hash in a url() argument
if (!/[:,\s]/.test(declString[match.startIndex - 1])) {
return
}
const hexMatch = /^#[0-9A-Za-z]+/.exec(declString.substr(match.startIndex))
if (!hexMatch) {
return
}
const hexValue = hexMatch[0]
if (isValidHex(hexValue)) {
return
}
report({
message: messages.rejected(hexValue),
node: decl,
index: match.startIndex,
result,
ruleName,
})
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,153 @@
# comment-empty-line-before
Require or disallow an empty line before comments.
```css
a {}
/* ← */
/* comment */ /* ↑ */
/** ↑
* This line */
```
If the comment is the very first node in a stylesheet then it is ignored. Shared-line comments are also ignored.
If you're using a custom syntax which support single-line comments with `//`, those are ignored as well.
**Caveat:** Comments within *selector and value lists* are currently ignored.
## Options
`string`: `"always"|"never"`
### `"always"`
There *must always* be an empty line before comments.
The following patterns are considered warnings:
```css
a {}
/* comment */
```
The following patterns are *not* considered warnings:
```css
a {}
/* comment */
```
```css
a {} /* comment */
```
### `"never"`
There *must never* be an empty line before comments.
The following patterns are considered warnings:
```css
a {}
/* comment */
```
The following patterns are *not* considered warnings:
```css
a {}
/* comment */
```
```css
a {} /* comment */
```
## Optional secondary options
### `except: ["first-nested"]`
Reverse the primary option for comments that are nested and the first child of their parent node.
For example, with `"always"`:
The following patterns are considered warnings:
```css
a {
/* comment */
color: pink;
}
```
The following patterns are *not* considered warnings:
```css
a {
/* comment */
color: pink;
}
```
### `ignore: ["after-comment", "stylelint-commands"]`
#### `"after-comment"`
***Note: This option was previously called `between-comments`.***
Don't require an empty line after a comment.
For example, with `"always"`:
The following patterns are *not* considered warnings:
```css
a {
background: pink;
/* comment */
/* comment */
color: #eee;
}
```
```css
a {
background: pink;
/* comment */
/* comment */
color: #eee;
}
```
#### `"stylelint-commands"`
Ignore comments that deliver commands to stylelint, e.g. `/* stylelint-disable color-no-hex */`.
For example, with `"always"`:
The following patterns are considered warnings:
```css
a {
background: pink;
/* not a stylelint command */
color: #eee;
}
```
The following patterns are *not* considered warnings:
```css
a {
background: pink;
/* stylelint-disable color-no-hex */
color: pink;
}
```

View File

@@ -0,0 +1,135 @@
"use strict"
const hasEmptyLine = require("../../utils/hasEmptyLine")
const optionsMatches = require("../../utils/optionsMatches")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "comment-empty-line-before"
const messages = ruleMessages(ruleName, {
expected: "Expected empty line before comment",
rejected: "Unexpected empty line before comment",
})
const stylelintCommandPrefix = "stylelint-"
const rule = function (expectation, options) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"never",
],
}, {
actual: options,
possible: {
except: ["first-nested"],
ignore: [
"stylelint-commands",
"stylelint-command",
"between-comments",
"after-comment",
],
},
optional: true,
})
if (!validOptions) {
return
}
if (
optionsMatches(options, "ignore", "between-comments")
) {
result.warn((
"'comment-empty-line-before\'s' \"between-comments\" option has been deprecated and in 8.0 will be removed. " +
"Instead use the \"after-comment\" option."
), {
stylelintType: "deprecation",
stylelintReference: "https://stylelint.io/user-guide/rules/comment-empty-line-before/",
})
}
root.walkComments(comment => {
// Ignore the first node
if (comment === root.first) {
return
}
// Optionally ignore stylelint commands
if (
comment.text.indexOf(stylelintCommandPrefix) === 0
&& optionsMatches(options, "ignore", "stylelint-commands")
) {
return
}
// Optionally ignore newlines between comments
const prev = comment.prev()
if (
prev
&& prev.type === "comment"
&& optionsMatches(options, "ignore", "between-comments")
) {
return
}
if (
prev
&& prev.type === "comment"
&& optionsMatches(options, "ignore", "after-comment")
) {
return
}
if (
comment.raws.inline
|| comment.inline
) {
return
}
const before = (comment.raws.before || "")
// Ignore shared-line comments
if (before.indexOf("\n") === -1) {
return
}
const expectEmptyLineBefore = (() => {
if (
optionsMatches(options, "except", "first-nested")
&& comment.parent !== root
&& comment === comment.parent.first
) {
return false
}
return expectation === "always"
})()
const hasEmptyLineBefore = hasEmptyLine(before)
// Return if the expectation is met
if (expectEmptyLineBefore === hasEmptyLineBefore) {
return
}
const message = expectEmptyLineBefore
? messages.expected
: messages.rejected
report({
message,
node: comment,
result,
ruleName,
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,43 @@
# comment-no-empty
Disallow empty comments.
```css
/* */
/** ↑
* Comments like this */
```
**Caveat:** Comments within *selector and value lists* are currently ignored.
## Options
### `true`
The following patterns are considered warnings:
```css
/**/
```
```css
/* */
```
```css
/*
*/
```
The following patterns are *not* considered warnings:
```css
/* comment */
```
```css
/*
* Multi-line Comment
**/
```

View File

@@ -0,0 +1,49 @@
"use strict"
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "comment-no-empty"
const messages = ruleMessages(ruleName, {
rejected: "Unexpected empty comment",
})
const rule = function (actual) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual })
if (!validOptions) {
return
}
root.walkComments(comment => {
// To ignore inline SCSS comments
if (
comment.raws.inline
|| comment.inline
) {
return
}
// To ignore comments that are not empty
if (
comment.text
&& comment.text.length !== 0
) {
return
}
report({
message: messages.rejected,
node: comment,
result,
ruleName,
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,84 @@
# comment-whitespace-inside
Require or disallow whitespace on the inside of comment markers.
```css
/* comment */
/** ↑ ↑
* The space inside these two markers */
```
Any number of asterisks are allowed at the beginning or end of the comment. So `/** comment **/` is treated the same way as `/* comment */`.
**Caveat:** Comments within *selector and value lists* are currently ignored.
## Options
`string`: `"always"|"never"`
### `"always"`
There *must always* be whitespace inside the markers.
The following patterns are considered warnings:
```css
/*comment*/
```
```css
/*comment */
```
```css
/** comment**/
```
The following patterns are *not* considered warnings:
```css
/* comment */
```
```css
/** comment **/
```
```css
/**
* comment
*/
```
```css
/* comment
*/
```
### `"never"`
There *must never* be whitespace on the inside the markers.
The following patterns are considered warnings:
```css
/* comment */
```
```css
/*comment */
```
```css
/** comment**/
```
The following patterns are *not* considered warnings:
```css
/*comment*/
```
```css
/****comment****/
```

View File

@@ -0,0 +1,96 @@
"use strict"
const isWhitespace = require("../../utils/isWhitespace")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "comment-whitespace-inside"
const messages = ruleMessages(ruleName, {
expectedOpening: "Expected whitespace after \"/*\"",
rejectedOpening: "Unexpected whitespace after \"/*\"",
expectedClosing: "Expected whitespace before \"*/\"",
rejectedClosing: "Unexpected whitespace before \"*/\"",
})
const rule = function (expectation) {
return function (root, result) {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"never",
],
})
if (!validOptions) {
return
}
root.walkComments(function (comment) {
if (
comment.raws.inline
|| comment.inline
) {
return
}
const rawComment = comment.toString()
const firstFourChars = rawComment.substr(0, 4)
// Return early if sourcemap or copyright comment
if (/^\/\*[#!]\s/.test(firstFourChars)) {
return
}
const leftMatches = rawComment.match(/(^\/\*+)(\s)?/)
const rightMatches = rawComment.match(/(\s)?(\*+\/)$/)
const opener = leftMatches[1]
const leftSpace = leftMatches[2] || ""
const rightSpace = rightMatches[1] || ""
const closer = rightMatches[2]
if (
expectation === "never"
&& leftSpace !== ""
) {
complain(messages.rejectedOpening, opener.length)
}
if (
expectation === "always"
&& !isWhitespace(leftSpace)
) {
complain(messages.expectedOpening, opener.length)
}
if (
expectation === "never"
&& rightSpace !== ""
) {
complain(messages.rejectedClosing, comment.toString().length - closer.length - 1)
}
if (
expectation === "always"
&& !isWhitespace(rightSpace)
) {
complain(messages.expectedClosing, comment.toString().length - closer.length - 1)
}
function complain(message, index) {
report({
message,
index,
result,
ruleName,
node: comment,
})
}
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,43 @@
# comment-word-blacklist
Specify a blacklist of disallowed words within comments.
```css
/* words within comments */
/** ↑ ↑ ↑
* These three words */
```
**Caveat:** Comments within *selector and value lists* are currently ignored.
## Options
`array|string`: `["array", "of", "words", "or", "/regex/"]|"word"|"/regex/"`
If a string is surrounded with `"/"` (e.g. `"/^TODO:/"`), it is interpreted as a regular expression.
Given:
```js
["/^TODO:/", "badword"]
```
The following patterns are considered warnings:
```css
/* TODO: */
```
```css
/* TODO: add fallback */
```
```css
/* some badword */
```
The following patterns are *not* considered warnings:
```css
/* comment */
```

View File

@@ -0,0 +1,56 @@
"use strict"
const _ = require("lodash")
const containsString = require("../../utils/containsString")
const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "comment-word-blacklist"
const messages = ruleMessages(ruleName, {
rejected: pattern => `Unexpected word matching pattern "${pattern}"`,
})
const rule = function (blacklist) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: blacklist,
possible: [_.isString],
})
if (!validOptions) {
return
}
root.walkComments(comment => {
const text = comment.text
const rawComment = comment.toString()
const firstFourChars = rawComment.substr(0, 4)
// Return early if sourcemap
if (firstFourChars === "/*# ") {
return
}
const matchesWord = matchesStringOrRegExp(text, blacklist) || containsString(text, blacklist)
if (!matchesWord) {
return
}
report({
message: messages.rejected(matchesWord.pattern),
node: comment,
result,
ruleName,
})
})
}
}
rule.primaryOptionArray = true
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,33 @@
# custom-media-pattern
Specify a pattern for custom media query names.
```css
@custom-media --foo (max-width: 30em);
/** ↑
* The pattern of this */
```
## Options
`regex|string`
A string will be translated into a RegExp like so `new RegExp(yourString)` so be sure to escape properly.
Given the string:
```js
"foo-.+"
```
The following patterns are considered warnings:
```css
@custom-media --bar (min-width: 30em);
```
The following patterns are *not* considered warnings:
```css
@custom-media --foo-bar (min-width: 30em);
```

View File

@@ -0,0 +1,54 @@
"use strict"
const _ = require("lodash")
const atRuleParamIndex = require("../../utils/atRuleParamIndex")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "custom-media-pattern"
const messages = ruleMessages(ruleName, {
expected: "Expected custom media query name to match specified pattern",
})
const rule = function (pattern) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: pattern,
possible: [
_.isRegExp,
_.isString,
],
})
if (!validOptions) {
return
}
const regexpPattern = _.isString(pattern) ? new RegExp(pattern) : pattern
root.walkAtRules(atRule => {
if (atRule.name.toLowerCase() !== "custom-media") {
return
}
const customMediaName = atRule.params.match(/^--(\S+)\b/)[1]
if (regexpPattern.test(customMediaName)) {
return
}
report({
message: messages.expected,
node: atRule,
index: atRuleParamIndex(atRule),
result,
ruleName,
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,197 @@
# custom-property-empty-line-before
Require or disallow an empty line before custom properties.
```css
a {
top: 10px;
/* ← */
--foo: pink; /* ↑ */
} /* ↑ */
/** ↑
* This line */
```
## Options
`string`: `"always"|"never"`
### `"always"`
The following patterns are considered warnings:
```css
a {
top: 10px;
--foo: pink;
--bar: red;
}
```
The following patterns are *not* considered warnings:
```css
a {
top: 10px;
--foo: pink;
--bar: red;
}
```
### `"never"`
The following patterns are considered warnings:
```css
a {
top: 10px;
--foo: pink;
--bar: red;
}
```
```css
a {
--foo: pink;
--bar: red;
}
```
The following patterns are *not* considered warnings:
```css
a {
top: 10px;
--foo: pink;
--bar: red;
}
```
```css
a {
--foo: pink;
--bar: red;
}
```
## Optional secondary options
### `except: ["after-comment", "after-custom-property", "first-nested"]`
#### `"after-comment"`
Reverse the primary option for custom properties that come after a comment.
For example, with `"always"`:
The following patterns are considered warnings:
```css
a {
--foo: pink;
/* comment */
--bar: red;
}
```
The following patterns are *not* considered warnings:
```css
a {
--foo: pink;
/* comment */
--bar: red;
}
```
#### `"after-custom-property"`
Reverse the primary option for custom properties that come after another custom property.
For example, with `"always"`:
The following patterns are considered warnings:
```css
a {
--foo: pink;
--bar: red;
}
```
The following patterns are *not* considered warnings:
```css
a {
--foo: pink;
--bar: red;
}
```
#### `"first-nested"`
Reverse the primary option for custom properties that are nested and the first child of their parent node.
For example, with `"always"`:
The following patterns are considered warnings:
```css
a {
--foo: pink;
--bar: red;
}
```
The following patterns are *not* considered warnings:
```css
a {
--foo: pink;
--bar: red;
}
```
### `ignore: ["after-comment", "inside-single-line-block"]`
#### `"after-comment"`
Ignore custom properties that are preceded by comments.
For example, with `"always"`:
The following patterns are *not* considered warnings:
```css
a {
/* comment */
--foo: pink;
}
```
#### `"inside-single-line-block"`
Ignore custom properties that are inside single-line blocks.
For example, with `"always"`:
The following patterns are *not* considered warnings:
```css
a { --foo: pink; --bar: red; }
```

View File

@@ -0,0 +1,124 @@
"use strict"
const blockString = require("../../utils/blockString")
const hasEmptyLine = require("../../utils/hasEmptyLine")
const isCustomProperty = require("../../utils/isCustomProperty")
const isSingleLineString = require("../../utils/isSingleLineString")
const isStandardSyntaxDeclaration = require("../../utils/isStandardSyntaxDeclaration")
const optionsMatches = require("../../utils/optionsMatches")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "custom-property-empty-line-before"
const messages = ruleMessages(ruleName, {
expected: "Expected empty line before custom property",
rejected: "Unexpected empty line before custom property",
})
const rule = function (expectation, options) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"never",
],
}, {
actual: options,
possible: {
except: [
"first-nested",
"after-comment",
"after-custom-property",
],
ignore: [
"after-comment",
"inside-single-line-block",
],
},
optional: true,
})
if (!validOptions) {
return
}
root.walkDecls(decl => {
const prop = decl.prop,
parent = decl.parent
if (!isStandardSyntaxDeclaration(decl)) {
return
}
if (!isCustomProperty(prop)) {
return
}
// Optionally ignore the node if a comment precedes it
if (
optionsMatches(options, "ignore", "after-comment")
&& decl.prev()
&& decl.prev().type === "comment"
) {
return
}
// Optionally ignore nodes inside single-line blocks
if (
optionsMatches(options, "ignore", "inside-single-line-block")
&& isSingleLineString(blockString(parent))
) {
return
}
let expectEmptyLineBefore = expectation === "always" ? true : false
// Optionally reverse the expectation for the first nested node
if (
optionsMatches(options, "except", "first-nested")
&& decl === parent.first
) {
expectEmptyLineBefore = !expectEmptyLineBefore
}
// Optionally reverse the expectation if a comment precedes this node
if (
optionsMatches(options, "except", "after-comment")
&& decl.prev()
&& decl.prev().type === "comment"
) {
expectEmptyLineBefore = !expectEmptyLineBefore
}
// Optionally reverse the expectation if a custom property precedes this node
if (
optionsMatches(options, "except", "after-custom-property")
&& decl.prev()
&& decl.prev().prop
&& isCustomProperty(decl.prev().prop)
) {
expectEmptyLineBefore = !expectEmptyLineBefore
}
const hasEmptyLineBefore = hasEmptyLine(decl.raws.before)
// Return if the expectation is met
if (expectEmptyLineBefore === hasEmptyLineBefore) {
return
}
const message = expectEmptyLineBefore ? messages.expected : messages.rejected
report({
message,
node: decl,
result,
ruleName,
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,31 @@
# custom-property-no-outside-root
***Deprecated: See [CHANGELOG](../../../CHANGELOG.md).***
Disallow custom properties outside of `:root` rules.
```css
a { --foo: 1px; }
/** ↑ ↑
* These selectors and these types of custom properties */
```
## Options
### `true`
The following patterns are considered warnings:
```css
a { --foo: 1px; }
```
```css
:root, a { --foo: 1px; }
```
The following patterns are *not* considered warnings:
```css
:root { --foo: 1px; }
```

View File

@@ -0,0 +1,51 @@
"use strict"
const isCustomProperty = require("../../utils/isCustomProperty")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "custom-property-no-outside-root"
const messages = ruleMessages(ruleName, {
rejected: "Unexpected custom property",
})
const rule = function (actual) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual })
if (!validOptions) {
return
}
result.warn((
`'${ruleName}' has been deprecated and in 8.0 will be removed.`
), {
stylelintType: "deprecation",
stylelintReference: `https://stylelint.io/user-guide/rules/${ruleName}/`,
})
root.walkRules(rule => {
// Ignore rules whose selector is just `:root`
if (rule.selector.toLowerCase().trim() === ":root") {
return
}
rule.walkDecls(decl => {
if (!isCustomProperty(decl.prop)) {
return
}
report({
message: messages.rejected,
node: decl,
result,
ruleName,
})
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,33 @@
# custom-property-pattern
Specify a pattern for custom properties.
```css
a { --foo-: 1px; }
/** ↑
* The pattern of this */
```
## Options
`regex|string`
A string will be translated into a RegExp like so `new RegExp(yourString)` so be sure to escape properly.
Given the string:
```js
"foo-.+"
```
The following patterns are considered warnings:
```css
:root { --boo-bar: 0; }
```
The following patterns are *not* considered warnings:
```css
:root { --foo-bar: 0; }
```

View File

@@ -0,0 +1,52 @@
"use strict"
const _ = require("lodash")
const isCustomProperty = require("../../utils/isCustomProperty")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "custom-property-pattern"
const messages = ruleMessages(ruleName, {
expected: "Expected custom property name to match specified pattern",
})
const rule = function (pattern) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: pattern,
possible: [
_.isRegExp,
_.isString,
],
})
if (!validOptions) {
return
}
const regexpPattern = _.isString(pattern) ? new RegExp(pattern) : pattern
root.walkDecls(decl => {
const prop = decl.prop
if (!isCustomProperty(prop)) {
return
}
if (regexpPattern.test(prop.slice(2))) {
return
}
report({
message: messages.expected,
node: decl,
result,
ruleName,
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,61 @@
# declaration-bang-space-after
Require a single space or disallow whitespace after the bang of declarations.
```css
a { color: pink !important; }
/** ↑
* The space after this exclamation mark */
```
## Options
`string`: `"always"|"never"`
### `"always"`
There *must always* be a single space after the bang.
The following patterns are considered warnings:
```css
a { color: pink !important; }
```
```css
a { color: pink !important; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink ! important; }
```
```css
a { color: pink! important; }
```
### `"never"`
There *must never* be whitespace after the bang.
The following patterns are considered warnings:
```css
a { color: pink ! important; }
```
```css
a { color: pink! important; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink !important; }
```
```css
a { color:pink!important; }
```

View File

@@ -0,0 +1,40 @@
"use strict"
const declarationBangSpaceChecker = require("../declarationBangSpaceChecker")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "declaration-bang-space-after"
const messages = ruleMessages(ruleName, {
expectedAfter: () => "Expected single space after \"!\"",
rejectedAfter: () => "Unexpected whitespace after \"!\"",
})
const rule = function (expectation) {
const checker = whitespaceChecker("space", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"never",
],
})
if (!validOptions) {
return
}
declarationBangSpaceChecker({
root,
result,
locationChecker: checker.after,
checkedRuleName: ruleName,
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,57 @@
# declaration-bang-space-before
Require a single space or disallow whitespace before the bang of declarations.
```css
a { color: pink !important; }
/** ↑
* The space before this exclamation mark */
```
## Options
`string`: `"always"|"never"`
### `"always"`
There *must always* be a single space before the bang.
The following patterns are considered warnings:
```css
a { color: pink!important; }
```
```css
a { color: pink ! important; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink !important; }
```
```css
a { color:pink ! important; }
```
### `"never"`
There *must never* be whitespace before the bang.
The following patterns are considered warnings:
```css
a { color : pink !important; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink!important; }
```
```css
a { color: pink! important; }
```

View File

@@ -0,0 +1,40 @@
"use strict"
const declarationBangSpaceChecker = require("../declarationBangSpaceChecker")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "declaration-bang-space-before"
const messages = ruleMessages(ruleName, {
expectedBefore: () => "Expected single space before \"!\"",
rejectedBefore: () => "Unexpected whitespace before \"!\"",
})
const rule = function (expectation) {
const checker = whitespaceChecker("space", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"never",
],
})
if (!validOptions) {
return
}
declarationBangSpaceChecker({
root,
result,
locationChecker: checker.before,
checkedRuleName: ruleName,
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

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

View File

@@ -0,0 +1,109 @@
# declaration-block-no-ignored-properties
***Deprecated: See [CHANGELOG](../../../CHANGELOG.md).***
Disallow property values that are ignored due to another property value in the same rule.
```css
a { display: inline; width: 100px; }
/** ↑
* This property */
```
Certain property value pairs rule out other property value pairs, causing them to be ignored by the browser. For example, when an element has display: inline, any further declarations about width, height and margin-top properties will be ignored. Sometimes this is confusing: maybe you forgot that your margin-top will have no effect because the element has display: inline, so you spend a while struggling to figure out what you've done wrong. This rule protects against that confusion by ensuring that within a single rule you don't use property values that are ruled out by other property values in that same rule.
The rule warns when it finds:
- `display: inline` used with `width`, `height`, `margin`, `margin-top`, `margin-bottom`, `overflow` (and all variants).
- `display: list-item` used with `vertical-align`.
- `display: block` used with `vertical-align`.
- `display: flex` used with `vertical-align`.
- `display: table` used with `vertical-align`.
- `display: table-*` used with `margin` (and all variants).
- `display: table-*` (except `table-cell`) used with `vertical-align`.
- `display: table-(row|row-group)` used with `width`, `min-width` or `max-width`.
- `display: table-(column|column-group)` used with `height`, `min-height` or `max-height`.
- `float: left` and `float: right` used with `vertical-align`.
- `position: static` used with `top`, `right`, `bottom`, or `left`.
- `position: absolute` used with `float`, `clear` or `vertical-align`.
- `position: fixed` used with `float`, `clear` or `vertical-align`.
## Options
### `true`
The following patterns are considered warnings:
```css
a { display: inline; width: 100px; }
```
`display: inline` causes `width` to be ignored.
```css
a { display: inline; height: 100px; }
```
`display: inline` causes `height` to be ignored.
```css
a { display: inline; margin: 10px; }
```
`display: inline` causes `margin` to be ignored.
```css
a { display: block; vertical-align: baseline; }
```
`display: block` causes `vertical-align` to be ignored.
```css
a { display: flex; vertical-align: baseline; }
```
`display: flex` causes `vertical-align` to be ignored.
```css
a { position: absolute; vertical-align: baseline; }
```
`position: absolute` causes `vertical-align` to be ignored.
```css
a { float: left; vertical-align: baseline; }
```
`float: left` causes `vertical-align` to be ignored.
The following patterns are *not* considered warnings:
```css
a { display: inline: margin-left: 10px; }
```
```css
a { display: inline: margin-right: 10px; }
```
```css
a { display: inline: padding: 10px; }
```
```css
a { display: inline-block; width: 100px; }
```
Although `display: inline` causes `width` to be ignored, `inline-block` works with `width`.
```css
a { display: table-cell; vertical-align: baseline; }
```
Although `display: block` causes `vertical-align` to be ignored, `table-cell` works with `vertical-align`.
```css
a { position: relative; vertical-align: baseline; }
```
Although `position: absolute` causes `vertical-align` to be ignored, `relative` works with `vertical-align`.

View File

@@ -0,0 +1,182 @@
"use strict"
const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp")
const postcss = require("postcss")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "declaration-block-no-ignored-properties"
const messages = ruleMessages(ruleName, {
rejected: (ignored, cause) => `Unexpected "${ignored}" with "${cause}"`,
})
const ignored = [
{
property: "display",
value: "inline",
ignoredProperties: [
"width",
"min-width",
"max-width",
"height",
"min-height",
"max-height",
"margin",
"margin-top",
"margin-bottom",
"overflow",
"overflow-x",
"overflow-y",
],
},
{
property: "display",
value: "list-item",
ignoredProperties: ["vertical-align"],
},
{
property: "display",
value: "block",
ignoredProperties: ["vertical-align"],
},
{
property: "display",
value: "flex",
ignoredProperties: ["vertical-align"],
},
{
property: "display",
value: "table",
ignoredProperties: ["vertical-align"],
},
{
property: "display",
value: "/^table-.*$/",
ignoredProperties: [
"margin",
"margin-top",
"margin-right",
"margin-bottom",
"margin-left",
],
},
{
property: "display",
value: "/^table-(row|row-group|column|column-group|header-group|footer-group|caption).*$/",
ignoredProperties: ["vertical-align"],
},
{
property: "display",
value: "/^table-(row|row-group).*$/",
ignoredProperties: [
"width",
"min-width",
"max-width",
],
},
{
property: "display",
value: "/^table-(column|column-group).*$/",
ignoredProperties: [
"height",
"min-height",
"max-height",
],
},
{
property: "float",
value: "left",
ignoredProperties: ["vertical-align"],
},
{
property: "float",
value: "right",
ignoredProperties: ["vertical-align"],
},
{
property: "position",
value: "static",
ignoredProperties: [
"top",
"right",
"bottom",
"left",
],
},
{
property: "position",
value: "absolute",
ignoredProperties: [
"float",
"clear",
"vertical-align",
],
},
{
property: "position",
value: "fixed",
ignoredProperties: [
"float",
"clear",
"vertical-align",
],
},
]
const rule = function (actual) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual })
if (!validOptions) {
return
}
result.warn((
"'declaration-block-no-ignored-properties' has been deprecated and in 8.0 will be removed."
), {
stylelintType: "deprecation",
stylelintReference: "https://stylelint.io/user-guide/rules/declaration-block-no-ignored-properties/",
})
const uniqueDecls = {}
root.walkDecls(decl => {
uniqueDecls[decl.prop] = decl
})
Object.keys(uniqueDecls).forEach((prop, index) => {
const decl = uniqueDecls[prop]
const unprefixedProp = postcss.vendor.unprefixed(prop)
const unprefixedValue = postcss.vendor.unprefixed(decl.value)
ignored.forEach(ignore => {
const matchProperty = matchesStringOrRegExp(unprefixedProp.toLowerCase(), ignore.property)
const matchValue = matchesStringOrRegExp(unprefixedValue.toLowerCase(), ignore.value)
if (!matchProperty || !matchValue) {
return
}
const ignoredProperties = ignore.ignoredProperties
decl.parent.nodes.forEach((node, nodeIndex) => {
if (!node.prop || ignoredProperties.indexOf(node.prop.toLowerCase()) === -1 || index === nodeIndex) {
return
}
report({
message: messages.rejected(node.prop, decl.toString()),
node,
result,
ruleName,
})
})
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,158 @@
# declaration-block-no-redundant-longhand-properties
Disallow longhand properties that can be combined into one shorthand property.
```css
a {
padding-top: 1px;
padding-right: 2px;
padding-bottom: 3px;
padding-left: 4px; }
/** ↑
* These longhand properties */
```
The longhand properties in the example above can be more concisely written as:
```css
a {
padding: 1px 2px 3px 4px;
}
```
This rule will only warn if you've used the longhand equivalent of *all* the properties that the shorthand will set.
This rule warns when the following shorthand properties can be used:
- `padding`
- `margin`
- `background`
- `font`
- `border`
- `border-top`
- `border-bottom`
- `border-left`
- `border-right`
- `border-width`
- `border-style`
- `border-color`
- `border-radius`
- `transition`
## Options
### `true`
The following patterns are considered warnings:
```css
a {
margin-top: 1px;
margin-right: 2px;
margin-bottom: 3px;
margin-left: 4px;
}
```
```css
a {
font-style: italic;
font-variant: normal;
font-weight: bold;
font-stretch: normal;
font-size: 14px;
line-height: 1.2;
font-family: serif;
}
```
```css
a {
-webkit-transition-property: top;
-webkit-transition-duration: 2s;
-webkit-transition-timing-function: ease;
-webkit-transition-delay: 0.5s;
}
```
The following patterns are *not* considered warnings:
```css
a {
margin: 1px 2px 3px 4px;
}
```
```css
a {
font: italic normal bold normal 14px/1.2 serif;
}
```
```css
a {
-webkit-transition: top 2s ease 0.5s;
}
```
```css
a {
margin-top: 1px;
margin-right: 2px;
}
```
```css
a {
margin-top: 1px;
margin-right: 2px;
margin-bottom: 3px;
}
```
## Optional secondary options
### `ignoreShorthands: ["/regex/", "string"]`
Given:
```js
["padding", "/border/"]
```
The following patterns are *not* considered warnings:
```css
a {
padding-top: 20px;
padding-right: 10px;
padding-bottom: 30px;
padding-left: 10px;
}
```
```css
a {
border-top-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-right-width: 1px;
}
```
```css
a {
border-top-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-right-width: 1px;
}
```
```css
a {
border-top-color: green;
border-top-style: double;
border-top-width: 7px;
}
```

View File

@@ -0,0 +1,80 @@
"use strict"
const _ = require("lodash")
const optionsMatches = require("../../utils/optionsMatches")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const shorthandData = require("../../reference/shorthandData")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "declaration-block-no-redundant-longhand-properties"
const messages = ruleMessages(ruleName, {
expected: props => `Expected shorthand property "${props}"`,
})
const rule = function (actual, options) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual }, {
actual: options,
possible: {
ignoreShorthands: [_.isString],
},
optional: true,
})
if (!validOptions) {
return
}
const longhandProperties = _.transform(shorthandData, (result, values, key) => {
if (optionsMatches(options, "ignoreShorthands", key)) {
return
}
values.forEach(value => {
(result[value] || (result[value] = [])).push(key)
})
})
root.walkRules(check)
root.walkAtRules(check)
function check(statement) {
const longhandDeclarations = {}
// Shallow iteration so nesting doesn't produce
// false positives
statement.each(node => {
if (node.type !== "decl") {
return
}
const prop = node.prop.toLowerCase()
const shorthandProperties = longhandProperties[prop]
if (!shorthandProperties) {
return
}
shorthandProperties.forEach(shorthandProperty => {
(longhandDeclarations[shorthandProperty] || (longhandDeclarations[shorthandProperty] = [])).push(prop)
if (!_.isEqual(shorthandData[shorthandProperty].sort(), longhandDeclarations[shorthandProperty].sort())) {
return
}
report({
ruleName,
result,
node,
message: messages.expected(shorthandProperty),
})
})
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,54 @@
# declaration-block-no-shorthand-property-overrides
Disallow shorthand properties that override related longhand properties.
```css
a { background-repeat: repeat; background: green; }
/** ↑
* This overrides the longhand property before it */
```
In almost every case, this is just an authorial oversight. For more about this behavior, see [MDN's documentation of shorthand properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties).
## Options
### `true`
The following patterns are considered warnings:
```css
a {
padding-left: 10px;
padding: 20px;
}
```
```css
a {
transition-property: opacity;
transition: opacity 1s linear;
}
```
```css
a {
border-top-width: 1px;
top: 0;
bottom: 3px;
border: 2px solid blue;
}
```
The following patterns are *not* considered warnings:
```css
a { padding: 10px; padding-left: 20px; }
```
```css
a { transition-property: opacity; } a { transition: opacity 1s linear; }
```
```css
a { transition-property: opacity; -webkit-transition: opacity 1s linear; }
```

View File

@@ -0,0 +1,57 @@
"use strict"
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const shorthandData = require("../../reference/shorthandData")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "declaration-block-no-shorthand-property-overrides"
const messages = ruleMessages(ruleName, {
rejected: (shorthand, original) => `Unexpected shorthand "${shorthand}" after "${original}"`,
})
const rule = function (actual) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual })
if (!validOptions) {
return
}
root.walkRules(check)
root.walkAtRules(check)
function check(statement) {
const declarations = {}
// Shallow iteration so nesting doesn't produce
// false positives
statement.each(node => {
if (node.type !== "decl") {
return
}
const prop = node.prop
const overrideables = shorthandData[prop.toLowerCase()]
if (!overrideables) {
declarations[prop.toLowerCase()] = prop
return
}
overrideables.forEach(longhandProp => {
if (!declarations.hasOwnProperty(longhandProp)) {
return
}
report({
ruleName,
result,
node,
message: messages.rejected(prop, declarations[longhandProp]),
})
})
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,556 @@
# declaration-block-properties-order
***Deprecated: instead use the community [`stylelint-order`](https://github.com/hudochenkov/stylelint-order) plugin pack.***
Specify the order of properties within declaration blocks.
```css
a {
color: pink;
top: 0;
}
/** ↑
* These properties */
```
Prefixed properties *must always* be alphabetically ordered and *must always* precede the unprefixed version.
This rule ignores variables (`$sass`, `@less`, `--custom-property`).
## Options
`string|array`: `"alphabetical"|["array", "of", "unprefixed", "property", "names", "or", "group", "objects"]`
### `"alphabetical"`
Properties *must always* be ordered alphabetically.
The following patterns are considered warnings:
```css
a {
top: 0;
color: pink;
}
```
```css
a {
-moz-transform: scale(1);
transform: scale(1);
-webkit-transform: scale(1);
}
```
The following patterns are *not* considered warnings:
```css
a {
color: pink;
top: 0;
}
```
```css
a {
-moz-transform: scale(1);
-webkit-transform: scale(1);
transform: scale(1);
}
```
### `["array", "of", "unprefixed", "property", "names", "or", "group", "objects"]`
Within an order array, you can include
- unprefixed property names
- group objects with these properties:
- `order ("strict"|"flexible")`: If `"strict"` (the default), the properties in this group must come in the order specified. If `"flexible"`, the properties can be in any order as long as they are grouped correctly.
- `properties (array of strings)`: The properties in this group.
There are some important details to keep in mind:
**By default, unlisted properties will be ignored.** So if you specify an array and do not include `display`, that means that the `display` property can be included before or after any other property. *This can be changed with the `unspecified` option* (see below).
**If an (unprefixed) property name is not included in your array and it contains a hyphen (e.g. `padding-left`), this rule will look for the string before that first hyphen in your array (e.g. `padding`) and use that position.** This means that you do not have to specify each extension of the root property; you can just specify the root property and the extensions will be accounted for.
For example, if you have included `border` in your array but not `border-top`, the rule will expect `border-top` to appear in the same relative position as `border`.
Other relevant rules include `margin`, `border`, `animation`, `transition`, etc.
Using this fallback, the order of these hyphenated relative to their peer extensions (e.g. `border-top` to `border-bottom`) will be *arbitrary*. If you would like to enforce a specific ordering (e.g. always put `border-right` before `border-left`), you should specify those particular names in your array.
Given:
```js
["transform", "top", "color"]
```
The following patterns are considered warnings:
```css
a {
color: pink;
top: 0;
}
```
```css
a {
-moz-transform: scale(1);
transform: scale(1);
-webkit-transform: scale(1);
}
```
The following patterns are *not* considered warnings:
```css
a {
top: 0;
color: pink;
}
```
```css
a {
-moz-transform: scale(1);
-webkit-transform: scale(1);
transform: scale(1);
}
```
Given:
```js
["padding", "color"]
```
The following patterns are considered warnings:
```css
a {
color: pink;
padding: 1em;
}
```
```css
a {
color: pink;
padding-top: 1em;
}
```
```css
a {
padding-left: 2em;
color: pink;
padding-top: 1em;
}
```
The following patterns are *not* considered warnings:
```css
a {
padding: 1em;
color: pink;
}
```
```css
a {
padding-top: 1em;
color: pink;
}
```
```css
a {
padding-left: 2em;
padding-top: 1em;
color: pink;
}
```
```css
a {
padding-top: 1em;
padding-left: 2em;
color: pink;
}
```
Given:
```js
["my", "font-smoothing", "color"]
```
Where `font-smoothing` is the unprefixed version of proprietary browser property `-webkit-font-smoothing` and `my` is a user-defined shorthand property.
The following patterns are considered warnings:
```css
a {
color: pink;
-webkit-font-smoothing: antialiased;
}
```
```css
a {
-webkit-font-smoothing: antialiased;
my-property: 2em;
}
```
```css
a {
my-property: 2em;
color: pink;
my-other-property: 1em;
}
```
The following patterns are *not* considered warnings:
```css
a {
-webkit-font-smoothing: antialiased;
color: pink;
}
```
```css
a {
my-property: 2em;
-webkit-font-smoothing: antialiased;
}
```
```css
a {
my-property: 2em;
my-other-property: 1em;
color: pink;
}
```
```css
a {
my-other-property: 1em;
my-property: 2em;
color: pink;
}
```
Given:
```js
["padding", "padding-top", "padding-right", "padding-bottom", "padding-left", "color"]
```
The following patterns are considered warnings:
```css
a {
padding-left: 2em;
padding-top: 1em;
padding: 1em;
color: pink;
}
```
The following patterns are *not* considered warnings:
```css
a {
padding-top: 1em;
padding-right: 1em;
padding-bottom: 0.5em;
padding-left: 0.5em;
color: pink;
}
```
```css
a {
padding: 1em;
padding-right: 2em;
padding-left: 2.5em;
color: pink;
}
```
Given:
```js
[
{
properties: [
"height",
"width",
],
},
{
properties: [
"font-size",
"font-weight",
],
},
]
```
The following patterns are considered warnings:
```css
a {
width: 2px;
height: 1px;
font-size: 2px;
font-weight: bold;
}
```
```css
a {
width: 2px;
height: 1px;
font-size: 2px;
font-weight: bold;
}
```
The following patterns are *not* considered warnings:
```css
a {
height: 1px;
width: 2px;
font-size: 2px;
font-weight: bold;
}
```
```css
a {
height: 1px;
width: 2px;
font-size: 2px;
font-weight: bold;
}
```
Given:
```js
[
"height",
"width",
{
order: "flexible",
properties: [
"color",
"font-size",
"font-weight",
],
},
]
```
The following patterns are considered warnings:
```css
a {
height: 1px;
font-weight: bold;
width: 2px;
}
```
```css
a {
width: 2px;
height: 1px;
font-weight: bold;
}
```
```css
a {
height: 1px;
color: pink;
width: 2px;
font-weight: bold;
}
```
The following patterns are *not* considered warnings:
```css
a {
height: 1px;
width: 2px;
color: pink;
font-size: 2px;
font-weight: bold;
}
```
```css
a {
height: 1px;
width: 2px;
font-size: 2px;
color: pink;
font-weight: bold;
}
```
## Optional secondary options
### `unspecified: "top"|"bottom"|"bottomAlphabetical"|"ignore"`
These options only apply if you've defined your own array of properties.
Default behavior is the same as `"ignore"`: an unspecified property can appear before or after any other property.
With `"top"`, unspecified properties are expected *before* any specified properties. With `"bottom"`, unspecified properties are expected *after* any specified properties. With `"bottomAlphabetical"`, unspecified properties are expected *after* any specified properties, and the unspecified properties are expected to be in alphabetical order.
Given:
```js
[["color", "background"], { unspecified: "ignore" }]
```
The following patterns are *not* considered warnings:
```css
a {
color: pink;
background: orange;
left: 0;
}
```
```css
a {
left: 0;
color: pink;
background: orange;
}
```
```css
a {
color: pink;
left: 0;
background: orange;
}
```
Given:
```js
[["color", "background"], { unspecified: "top" }]
```
The following patterns are considered warnings:
```css
a {
color: pink;
background: orange;
left: 0;
}
```
```css
a {
color: pink;
left: 0;
background: orange;
}
```
The following patterns are *not* considered warnings:
```css
a {
left: 0;
color: pink;
background: orange;
}
```
Given:
```js
[["color", "background"], { unspecified: "bottom" }]
```
The following patterns are considered warnings:
```css
a {
left: 0;
color: pink;
background: orange;
}
```
```css
a {
color: pink;
left: 0;
background: orange;
}
```
The following patterns are *not* considered warnings:
```css
a {
color: pink;
background: orange;
left: 0;
}
```
Given:
```js
[["composes"], { unspecified: "bottomAlphabetical" }]
```
The following patterns are considered warnings:
```css
a {
align-items: flex-end;
composes: b;
left: 0;
}
```
```css
a {
composes: b;
left: 0;
align-items: flex-end;
}
```
The following patterns are *not* considered warnings:
```css
a {
composes: b;
align-items: flex-end;
left: 0;
}
```

View File

@@ -0,0 +1,299 @@
"use strict"
const isCustomProperty = require("../../utils/isCustomProperty")
const isStandardSyntaxProperty = require("../../utils/isStandardSyntaxProperty")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const _ = require("lodash")
const postcss = require("postcss")
const ruleName = "declaration-block-properties-order"
const messages = ruleMessages(ruleName, {
expected: (first, second) => `Expected "${first}" to come before "${second}"`,
})
const rule = function (expectation, options) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: validatePrimaryOption,
}, {
actual: options,
possible: {
unspecified: [
"top",
"bottom",
"ignore",
"bottomAlphabetical",
],
},
optional: true,
})
if (!validOptions) {
return
}
result.warn((
"'declaration-block-properties-order'has been deprecated and in 8.0 will be removed. " +
"Instead use the community 'stylelint-order' plugin pack."
), {
stylelintType: "deprecation",
stylelintReference: "https://stylelint.io/user-guide/rules/declaration-block-properties-order/",
})
const alphabetical = expectation === "alphabetical"
const expectedOrder = alphabetical ? null : createExpectedOrder(expectation)
// By default, ignore unspecified properties
const unspecified = _.get(options, ["unspecified"], "ignore")
// Shallow loop
root.each(node => {
if (node.type === "rule" || node.type === "atrule") {
checkNode(node)
}
})
function checkNode(node) {
const allPropData = []
node.each(child => {
// If the child has nested nodes with child
// (e.g. a rule nested within a rule), make
// sure to check the children
if (child.nodes && child.nodes.length) {
checkNode(child)
}
if (child.type !== "decl") {
return
}
const prop = child.prop
if (!isStandardSyntaxProperty(prop)) {
return
}
if (isCustomProperty(prop)) {
return
}
let unprefixedPropName = postcss.vendor.unprefixed(prop)
// Hack to allow -moz-osx-font-smoothing to be understood
// just like -webkit-font-smoothing
if (unprefixedPropName.indexOf("osx-") === 0) {
unprefixedPropName = unprefixedPropName.slice(4)
}
const propData = {
name: prop,
unprefixedName: unprefixedPropName,
orderData: alphabetical ? null : getOrderData(expectedOrder, unprefixedPropName),
before: child.raws.before,
index: allPropData.length,
node: child,
}
const previousPropData = _.last(allPropData)
allPropData.push(propData)
// Skip first decl
if (!previousPropData) {
return
}
const isCorrectOrder = alphabetical ? checkAlpabeticalOrder(previousPropData, propData) : checkOrder(previousPropData, propData)
if (isCorrectOrder) {
return
}
complain({
message: messages.expected(propData.name, previousPropData.name),
node: child,
})
})
function checkOrder(firstPropData, secondPropData) {
// If the unprefixed property names are the same, resort to alphabetical ordering
if (firstPropData.unprefixedName === secondPropData.unprefixedName) {
return firstPropData.name <= secondPropData.name
}
const firstPropIsUnspecified = !firstPropData.orderData
const secondPropIsUnspecified = !secondPropData.orderData
// Now check actual known properties ...
if (!firstPropIsUnspecified && !secondPropIsUnspecified) {
return firstPropData.orderData.expectedPosition <= secondPropData.orderData.expectedPosition
}
if (firstPropIsUnspecified && !secondPropIsUnspecified) {
// If first prop is unspecified, look for a specified prop before it to
// compare to the current prop
const priorSpecifiedPropData = _.findLast(allPropData.slice(0, -1), d => !!d.orderData)
if (priorSpecifiedPropData && priorSpecifiedPropData.orderData && priorSpecifiedPropData.orderData.expectedPosition > secondPropData.orderData.expectedPosition) {
complain({
message: messages.expected(secondPropData.name, priorSpecifiedPropData.name),
node: secondPropData.node,
})
return true // avoid logging another warning
}
}
// Now deal with unspecified props ...
// Starting with bottomAlphabetical as it requires more specific conditionals
if (unspecified === "bottomAlphabetical" && !firstPropIsUnspecified && secondPropIsUnspecified) {
return true
}
if (unspecified === "bottomAlphabetical" && secondPropIsUnspecified && firstPropIsUnspecified) {
if (checkAlpabeticalOrder(firstPropData, secondPropData)) {
return true
} else {
return false
}
}
if (unspecified === "bottomAlphabetical" && firstPropIsUnspecified) {
return false
}
if (firstPropIsUnspecified && secondPropIsUnspecified) {
return true
}
if (unspecified === "ignore" && (firstPropIsUnspecified || secondPropIsUnspecified)) {
return true
}
if (unspecified === "top" && firstPropIsUnspecified) {
return true
}
if (unspecified === "top" && secondPropIsUnspecified) {
return false
}
if (unspecified === "bottom" && secondPropIsUnspecified) {
return true
}
if (unspecified === "bottom" && firstPropIsUnspecified) {
return false
}
}
}
function complain(opts) {
report({
message: opts.message,
node: opts.node,
result,
ruleName,
})
}
}
}
rule.primaryOptionArray = true
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule
function createExpectedOrder(input) {
const order = {}
let expectedPosition = 0
appendGroup(input)
function appendGroup(items) {
items.forEach(item => appendItem(item, false))
}
function appendItem(item, inFlexibleGroup) {
if (_.isString(item)) {
// In flexible groups, the expectedPosition does not ascend
// to make that flexibility work;
// otherwise, it will always ascend
if (!inFlexibleGroup) {
expectedPosition += 1
}
order[item] = { expectedPosition }
return
}
if (!item.order || item.order === "strict") {
appendGroup(item.properties)
return
} else if (item.order === "flexible") {
expectedPosition += 1
item.properties.forEach(property => {
appendItem(property, true)
})
}
}
return order
}
function getOrderData(expectedOrder, propName) {
let orderData = expectedOrder[propName]
// If prop was not specified but has a hyphen
// (e.g. `padding-top`), try looking for the segment preceding the hyphen
// and use that index
if (!orderData && propName.lastIndexOf("-") !== -1) {
const propNamePreHyphen = propName.slice(0, propName.lastIndexOf("-"))
orderData = getOrderData(expectedOrder, propNamePreHyphen)
}
return orderData
}
function checkAlpabeticalOrder(firstPropData, secondPropData) {
// If unprefixed prop names are the same, compare the prefixed versions
if (firstPropData.unprefixedName === secondPropData.unprefixedName) {
return firstPropData.name <= secondPropData.name
}
return firstPropData.unprefixedName < secondPropData.unprefixedName
}
function validatePrimaryOption(actualOptions) {
// Return true early if alphabetical
if (actualOptions === "alphabetical") {
return true
}
// Otherwise, begin checking array options
if (!Array.isArray(actualOptions)) {
return false
}
// Every item in the array must be a string or an object
// with a "properties" property
if (!actualOptions.every(item => {
if (_.isString(item)) {
return true
}
return _.isPlainObject(item) && !_.isUndefined(item.properties)
})) {
return false
}
const objectItems = actualOptions.filter(_.isPlainObject)
// Every object-item's "order" property must be "strict" or "flexible"
if (!objectItems.every(item => {
if (_.isUndefined(item.order)) {
return true
}
return _.includes([
"strict",
"flexible",
], item.order)
})) {
return false
}
return true
}

View File

@@ -0,0 +1,122 @@
# declaration-block-semicolon-newline-after
Require a newline or disallow whitespace after the semicolons of declaration blocks.
```css
a {
color: pink;
top: 0;
}
/** ↑
* The newline after this semicolon */
```
This rule ignores semicolons that are preceded by Less mixins.
This rule ignores the last semicolon of declaration blocks. Use the `block-closing-brace-*-before` rules to control the whitespace between the last semicolon and the closing brace instead.
This rule allows an end-of-line comment followed by a newline. For example,
```css
a {
color: pink; /* end-of-line comment */
top: 0;
}
```
## Options
`string`: `"always"|"always-multi-line"|"never-multi-line"`
### `"always"`
There *must always* be a newline after the semicolon.
The following patterns are considered warnings:
```css
a { color: pink; top: 0; }
```
```css
a {
color: pink; /* end-of-line comment
containing a newline */
top: 0;
}
```
The following patterns are *not* considered warnings:
```css
a {
color: pink;
top: 0;
}
```
```css
a {
color: pink; /* end-of-line comment */
top: 0;
}
```
### `"always-multi-line"`
There *must always* be a newline after the semicolon in multi-line rules.
The following patterns are considered warnings:
```css
a {
color: pink; top: 0;
}
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a { color: pink; top: 0; }
```
```css
a {
color: pink;
top: 0;
}
```
### `"never-multi-line"`
There *must never* be whitespace after the semicolon in multi-line rules.
The following patterns are considered warnings:
```css
a {
color: pink;
top: 0;
}
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a { color: pink; top: 0; }
```
```css
a {
color: pink
; top: 0;
}
```

View File

@@ -0,0 +1,76 @@
"use strict"
const blockString = require("../../utils/blockString")
const nextNonCommentNode = require("../../utils/nextNonCommentNode")
const rawNodeString = require("../../utils/rawNodeString")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "declaration-block-semicolon-newline-after"
const messages = ruleMessages(ruleName, {
expectedAfter: () => "Expected newline after \";\"",
expectedAfterMultiLine: () => "Expected newline after \";\" in a multi-line declaration block",
rejectedAfterMultiLine: () => "Unexpected newline after \";\" in a multi-line declaration block",
})
const rule = function (expectation) {
const checker = whitespaceChecker("newline", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"always-multi-line",
"never-multi-line",
],
})
if (!validOptions) {
return
}
root.walkDecls(decl => {
// Ignore last declaration if there's no trailing semicolon
const parentRule = decl.parent
if (
!parentRule.raws.semicolon
&& parentRule.last === decl
) {
return
}
const nextNode = decl.next()
if (!nextNode) {
return
}
// Allow end-of-line comment
const nodeToCheck = nextNonCommentNode(nextNode)
if (!nodeToCheck) {
return
}
checker.afterOneOnly({
source: rawNodeString(nodeToCheck),
index: -1,
lineCheckStr: blockString(parentRule),
err: m => {
report({
message: m,
node: decl,
index: decl.toString().length + 1,
result,
ruleName,
})
},
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,107 @@
# declaration-block-semicolon-newline-before
Require a newline or disallow whitespace before the semicolons of declaration blocks.
```css
a {
color: pink
; top: 0;
}
/** ↑
* The newline before this semicolon */
```
This rule ignores semicolons that are preceded by Less mixins.
## Options
`string`: `"always"|"always-multi-line"|"never-multi-line"`
### `"always"`
There *must always* be a newline before the semicolons.
The following patterns are considered warnings:
```css
a { color: pink; }
```
```css
a {
color: pink; top: 0;
}
```
The following patterns are *not* considered warnings:
```css
a { color: pink
; }
```
```css
a {
color: pink
; top: 0;
}
```
### `"always-multi-line"`
There *must always* be a newline before the semicolons in multi-line rules.
The following patterns are considered warnings:
```css
a {
color: pink; top: 0;
}
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a { color: pink; top: 0; }
```
```css
a {
color: pink
; top: 0;
}
```
### `"never-multi-line"`
There *must never* be whitespace before the semicolons in multi-line rules.
The following patterns are considered warnings:
```css
a {
color: pink
; top: 0;
}
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a { color: pink; top: 0; }
```
```css
a {
color: pink;
top: 0;
}
```

View File

@@ -0,0 +1,64 @@
"use strict"
const blockString = require("../../utils/blockString")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "declaration-block-semicolon-newline-before"
const messages = ruleMessages(ruleName, {
expectedBefore: () => "Expected newline before \";\"",
expectedBeforeMultiLine: () => "Expected newline before \";\" in a multi-line declaration block",
rejectedBeforeMultiLine: () => "Unexpected whitespace before \";\" in a multi-line declaration block",
})
const rule = function (expectation) {
const checker = whitespaceChecker("newline", expectation, messages)
return function (root, result) {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"always-multi-line",
"never-multi-line",
],
})
if (!validOptions) {
return
}
root.walkDecls(function (decl) {
const parentRule = decl.parent
if (
!parentRule.raws.semicolon
&& parentRule.last === decl
) {
return
}
const declString = decl.toString()
checker.beforeAllowingIndentation({
source: declString,
index: declString.length,
lineCheckStr: blockString(parentRule),
err: m => {
report({
message: m,
node: decl,
index: decl.toString().length - 1,
result,
ruleName,
})
},
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,125 @@
# declaration-block-semicolon-space-after
Require a single space or disallow whitespace after the semicolons of declaration blocks.
```css
a { color: pink; top: 0; }
/** ↑
* The space after this semicolon */
```
This rule ignores semicolons that are preceded by Less mixins.
This rule ignores the last semicolon of declaration blocks. Use the `block-closing-brace-*-before` rules to control the whitespace between the last semicolon and the closing brace instead.
## Options
`string`: `"always"|"never"|"always-single-line"|"never-single-line"`
### `"always"`
There *must always* be a single space after the semicolon.
The following patterns are considered warnings:
```css
a { color: pink;top: 0; }
```
```css
a {
color: pink;
top: 0;
}
```
The following patterns are *not* considered warnings:
```css
a { color: pink;}
```
```css
a { color: pink; }
```
```css
a { color: pink; top: 0; }
```
### `"never"`
There *must never* be whitespace after the semicolon.
The following patterns are considered warnings:
```css
a { color: pink; top: 0; }
```
```css
a {
color: pink;
top: 0;
}
```
The following patterns are *not* considered warnings:
```css
a { color: pink;}
```
```css
a { color: pink; }
```
```css
a { color: pink;top: 0; }
```
### `"always-single-line"`
There *must always* be a single space after the semicolon in single-line declaration blocks.
The following patterns are considered warnings:
```css
a { color: pink;top: 0; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; top: 0; }
```
```css
a {
color: pink;
top: 0;
}
```
### `"never-single-line"`
There *must never* be whitespace after the semicolon in single-line declaration blocks.
The following patterns are considered warnings:
```css
a { color: pink; top: 0; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink;top: 0; }
```
```css
a {
color: pink;
top: 0;
}
```

View File

@@ -0,0 +1,68 @@
"use strict"
const blockString = require("../../utils/blockString")
const rawNodeString = require("../../utils/rawNodeString")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "declaration-block-semicolon-space-after"
const messages = ruleMessages(ruleName, {
expectedAfter: () => "Expected single space after \";\"",
rejectedAfter: () => "Unexpected whitespace after \";\"",
expectedAfterSingleLine: () => "Expected single space after \";\" in a single-line declaration block",
rejectedAfterSingleLine: () => "Unexpected whitespace after \";\" in a single-line declaration block",
})
const rule = function (expectation) {
const checker = whitespaceChecker("space", expectation, messages)
return function (root, result) {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"never",
"always-single-line",
"never-single-line",
],
})
if (!validOptions) {
return
}
root.walkDecls(function (decl) {
// Ignore last declaration if there's no trailing semicolon
const parentRule = decl.parent
if (!parentRule.raws.semicolon && parentRule.last === decl) {
return
}
const nextDecl = decl.next()
if (!nextDecl) {
return
}
checker.after({
source: rawNodeString(nextDecl),
index: -1,
lineCheckStr: blockString(parentRule),
err: m => {
report({
message: m,
node: decl,
index: decl.toString().length + 1,
result,
ruleName,
})
},
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,111 @@
# declaration-block-semicolon-space-before
Require a single space or disallow whitespace before the semicolons of declaration blocks.
```css
a { color: pink; }
/** ↑
* The space before this semicolon */
```
This rule ignores semicolons that are preceded by Less mixins.
## Options
`string`: `"always"|"never"|"always-single-line"|"never-single-line"`
### `"always"`
There *must always* be a single space before the semicolons.
The following patterns are considered warnings:
```css
a { color: pink; }
```
```css
a { color: pink; top: 0; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink ; }
```
```css
a { color: pink ; top: 0 ; }
```
### `"never"`
There *must never* be whitespace before the semicolons.
The following patterns are considered warnings:
```css
a { color: pink ; }
```
```css
a { color: pink ; top: 0 ; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a { color: pink; top: 0; }
```
### `"always-single-line"`
There *must always* be a single space before the semicolons in single-line declaration blocks.
The following patterns are considered warnings:
```css
a { color: pink; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink ; }
```
```css
a { color: pink; top: 0; }
```
```css
a { color: pink ; top: 0 ; }
```
### `"never-single-line"`
There *must never* be whitespace before the semicolons in single-line declaration blocks.
The following patterns are considered warnings:
```css
a { color: pink ; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a { color: pink; top: 0; }
```
```css
a { color: pink ; top: 0 ; }
```

View File

@@ -0,0 +1,64 @@
"use strict"
const blockString = require("../../utils/blockString")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "declaration-block-semicolon-space-before"
const messages = ruleMessages(ruleName, {
expectedBefore: () => "Expected single space before \";\"",
rejectedBefore: () => "Unexpected whitespace before \";\"",
expectedBeforeSingleLine: () => "Expected single space before \";\" in a single-line declaration block",
rejectedBeforeSingleLine: () => "Unexpected whitespace before \";\" in a single-line declaration block",
})
const rule = function (expectation) {
const checker = whitespaceChecker("space", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"never",
"always-single-line",
"never-single-line",
],
})
if (!validOptions) {
return
}
root.walkDecls(decl => {
// Ignore last declaration if there's no trailing semicolon
const parentRule = decl.parent
if (!parentRule.raws.semicolon && parentRule.last === decl) {
return
}
const declString = decl.toString()
checker.before({
source: declString,
index: declString.length,
lineCheckStr: blockString(parentRule),
err: m => {
report({
message: m,
node: decl,
index: decl.toString().length - 1,
result,
ruleName,
})
},
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,44 @@
# declaration-block-single-line-max-declarations
Limit the number of declaration within a single line declaration block.
```css
a { color: pink; top: 0; }
/** ↑ ↑
* The number of these declarations */
```
## Options
`int`: Maximum number of declarations allowed.
For example, with `1`:
The following patterns are considered warnings:
```css
a { color: pink; top: 3px; }
```
```css
a,
b { color: pink; top: 3px; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a,
b { color: pink; }
```
```css
a {
color: pink;
top: 3px;
}
```

View File

@@ -0,0 +1,54 @@
"use strict"
const beforeBlockString = require("../../utils/beforeBlockString")
const blockString = require("../../utils/blockString")
const isSingleLineString = require("../../utils/isSingleLineString")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const _ = require("lodash")
const ruleName = "declaration-block-single-line-max-declarations"
const messages = ruleMessages(ruleName, {
expected: quantity => `Expected no more than ${quantity} declaration(s)`,
})
const rule = function (quantity) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: quantity,
possible: [_.isNumber],
})
if (!validOptions) {
return
}
root.walkRules(rule => {
if (!isSingleLineString(blockString(rule))) {
return
}
if (!rule.nodes) {
return
}
const decls = rule.nodes.filter(node => node.type === "decl")
if (decls.length <= quantity) {
return
}
report({
message: messages.expected(quantity),
node: rule,
index: beforeBlockString(rule, { noRawBefore: true }).length,
result,
ruleName,
})
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,73 @@
# declaration-block-trailing-semicolon
Require or disallow a trailing semicolon within declaration blocks.
```css
a { background: orange; color: pink; }
/** ↑
* This semicolon */
```
The trailing semicolon is the *last* semicolon in a declaration block and it is optional.
This rule will ignore Less mixins, trailing `//` comments, and declaration blocks containing nested (at-)rules.
## Options
`string`: `"always"|"never"`
### `"always"`
There *must always* be a trailing semicolon.
The following patterns are considered warnings:
```css
a { color: pink }
```
```css
a { background: orange; color: pink }
```
```css
a { @include foo }
```
The following patterns are *not* considered warnings:
```css
a { color: pink; }
```
```css
a { background: orange; color: pink; }
```
```css
a { @include foo; }
```
### `"never"`
There *must never* be a trailing semicolon.
The following patterns are considered warnings:
```css
a { color: pink; }
```
```css
a { background: orange; color: pink; }
```
The following patterns are *not* considered warnings:
```css
a { color: pink }
```
```css
a { background: orange; color: pink }
```

View File

@@ -0,0 +1,77 @@
"use strict"
const hasBlock = require("../../utils/hasBlock")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const ruleName = "declaration-block-trailing-semicolon"
const messages = ruleMessages(ruleName, {
expected: "Expected a trailing semicolon",
rejected: "Unexpected trailing semicolon",
})
const rule = function (expectation) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"never",
],
})
if (!validOptions) {
return
}
root.walkAtRules(atRule => {
if (atRule.parent === root) {
return
}
if (atRule !== atRule.parent.last) {
return
}
if (hasBlock(atRule)) {
return
}
checkLastNode(atRule)
})
root.walkDecls(decl => {
if (decl !== decl.parent.last) {
return
}
checkLastNode(decl)
})
function checkLastNode(node) {
let message
if (expectation === "always") {
if (node.parent.raws.semicolon) {
return
}
message = messages.expected
}
if (expectation === "never") {
if (!node.parent.raws.semicolon) {
return
}
message = messages.rejected
}
report({
message,
node,
index: node.toString().trim().length - 1,
result,
ruleName,
})
}
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,69 @@
# declaration-colon-newline-after
Require a newline or disallow whitespace after the colon of declarations.
```css
a {
box-shadow:
0 0 0 1px #5b9dd9,
0 0 2px 1px rgba(30, 140, 190, 0.8);
} /* ↑ */
/** ↑
* The newline after this colon */
```
## Options
`string`: `"always"|"always-multi-line"`
### `"always"`
There *must always* be a newline after the colon.
The following patterns are considered warnings:
```css
a { color:pink; }
```
```css
a { color: pink; }
```
The following patterns are *not* considered warnings:
```css
a {
color:
pink;
}
```
### `"always-multi-line"`
There *must always* be a newline after the colon *if the declaration's value is multi-line*.
The following patterns are considered warnings:
```css
a {
box-shadow: 0 0 0 1px #5b9dd9,
0 0 2px 1px rgba(30, 140, 190, 0.8);
}
```
The following patterns are *not* considered warnings:
```css
a {
box-shadow:
0 0 0 1px #5b9dd9,
0 0 2px 1px rgba(30, 140, 190, 0.8);
}
```
```css
a {
color: pink;
}
```

View File

@@ -0,0 +1,70 @@
"use strict"
const declarationValueIndex = require("../../utils/declarationValueIndex")
const isStandardSyntaxDeclaration = require("../../utils/isStandardSyntaxDeclaration")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const ruleName = "declaration-colon-newline-after"
const messages = ruleMessages(ruleName, {
expectedAfter: () => "Expected newline after \":\"",
expectedAfterMultiLine: () => "Expected newline after \":\" with a multi-line declaration",
})
const rule = function (expectation) {
const checker = whitespaceChecker("newline", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"always-multi-line",
],
})
if (!validOptions) {
return
}
root.walkDecls(decl => {
if (!isStandardSyntaxDeclaration(decl)) {
return
}
// Get the raw prop, and only the prop
const endOfPropIndex = declarationValueIndex(decl) + (decl.raws.between || "").length - 1
// The extra characters tacked onto the end ensure that there is a character to check
// after the colon. Otherwise, with `background:pink` the character after the
const propPlusColon = decl.toString().slice(0, endOfPropIndex) + "xxx"
for (let i = 0, l = propPlusColon.length; i < l; i++) {
if (propPlusColon[i] !== ":") {
continue
}
const indexToCheck = propPlusColon.substr(propPlusColon[i], 3) === "/*" ? propPlusColon.indexOf("*/", i) + 1 : i
checker.afterOneOnly({
source: propPlusColon,
index: indexToCheck,
lineCheckStr: decl.value,
err: m => {
report({
message: m,
node: decl,
index: indexToCheck,
result,
ruleName,
})
},
})
}
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,96 @@
# declaration-colon-space-after
Require a single space or disallow whitespace after the colon of declarations.
```css
a { color: pink }
/** ↑
* The space after this colon */
```
## Options
`string`: `"always"|"never"|"always-single-line"`
### `"always"`
There *must always* be a single space after the colon.
The following patterns are considered warnings:
```css
a { color :pink }
```
```css
a { color:pink }
```
The following patterns are *not* considered warnings:
```css
a { color : pink }
```
```css
a { color: pink }
```
### `"never"`
There *must never* be whitespace after the colon.
The following patterns are considered warnings:
```css
a { color:pink }
```
```css
a { color :pink }
```
The following patterns are *not* considered warnings:
```css
a { color :pink }
```
```css
a { color:pink }
```
### `"always-single-line"`
There *must always* be a single space after the colon *if the declaration's value is single-line*.
The following patterns are considered warnings:
```css
a {
box-shadow:0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, 0.8);
}
```
The following patterns are *not* considered warnings:
```css
a {
box-shadow: 0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, 0.8);
}
```
```css
a {
box-shadow:
0 0 0 1px #5b9dd9,
0 0 2px 1px rgba(30, 140, 190, 0.8);
}
```
```css
a {
box-shadow:0 0 0 1px #5b9dd9,
0 0 2px 1px rgba(30, 140, 190, 0.8);
}
```

View File

@@ -0,0 +1,42 @@
"use strict"
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const declarationColonSpaceChecker = require("../declarationColonSpaceChecker")
const ruleName = "declaration-colon-space-after"
const messages = ruleMessages(ruleName, {
expectedAfter: () => "Expected single space after \":\"",
rejectedAfter: () => "Unexpected whitespace after \":\"",
expectedAfterSingleLine: () => "Expected single space after \":\" with a single-line declaration",
})
const rule = function (expectation) {
const checker = whitespaceChecker("space", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"never",
"always-single-line",
],
})
if (!validOptions) {
return
}
declarationColonSpaceChecker({
root,
result,
locationChecker: checker.after,
checkedRuleName: ruleName,
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

View File

@@ -0,0 +1,61 @@
# declaration-colon-space-before
Require a single space or disallow whitespace before the colon of declarations.
```css
a { color :pink }
/** ↑
* The space before this colon */
```
## Options
`string`: `"always"|"never"`
### `"always"`
There *must always* be a single space before the colon.
The following patterns are considered warnings:
```css
a { color: pink }
```
```css
a { color:pink }
```
The following patterns are *not* considered warnings:
```css
a { color : pink }
```
```css
a { color :pink }
```
### `"never"`
There *must never* be whitespace before the colon.
The following patterns are considered warnings:
```css
a { color : pink }
```
```css
a { color :pink }
```
The following patterns are *not* considered warnings:
```css
a { color: pink }
```
```css
a { color:pink }
```

View File

@@ -0,0 +1,40 @@
"use strict"
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const whitespaceChecker = require("../../utils/whitespaceChecker")
const declarationColonSpaceChecker = require("../declarationColonSpaceChecker")
const ruleName = "declaration-colon-space-before"
const messages = ruleMessages(ruleName, {
expectedBefore: () => "Expected single space before \":\"",
rejectedBefore: () => "Unexpected whitespace before \":\"",
})
const rule = function (expectation) {
const checker = whitespaceChecker("space", expectation, messages)
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: [
"always",
"never",
],
})
if (!validOptions) {
return
}
declarationColonSpaceChecker({
root,
result,
locationChecker: checker.before,
checkedRuleName: ruleName,
})
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule

Some files were not shown because too many files have changed in this diff Show More