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,221 @@
# max-nesting-depth
Limit the allowed nesting depth.
```css
a { & > b { top: 0; }
/** ↑
* This nesting */
```
This rule works by checking rules' and at-rules' actual "nesting depth" against your specified max. Here's how nesting depths works:
```css
a {
& b { /* nesting depth 1 */
& .foo { /* nesting depth 2 */
@media print { /* nesting depth 3 */
& .baz { /* nesting depth 4 */
color: pink;
}
}
}
}
}
```
Note that **root-level at-rules will *not* be included in the nesting depth calculation**, because most users would take for granted that root-level at-rules are "free" (because necessary). So both of the following `.foo` rules have a nesting depth of 2, and will therefore pass if your `max` is less than or equal to 2:
```css
a {
b { /* 1 */
.foo {} /* 2 */
}
}
@media print { /* ignored */
a {
b { /* 1 */
.foo {} /* 2 */
}
}
}
```
This rule integrates into stylelint's core the functionality of the (now deprecated) plugin [`stylelint-statement-max-nesting-depth`](https://github.com/davidtheclark/stylelint-statement-max-nesting-depth).
## Options
`int`: Maximum nesting depth allowed.
For example, with `2`:
The following patterns are considered warnings:
```css
a {
& .foo { /* 1 */
&__foo { /* 2 */
& > .bar {} /* 3 */
}
}
}
```
```css
a {
@media print { /* 1 */
& .foo { /* 2 */
& .bar {} /* 3 */
}
}
}
```
The following patterns are *not* considered warnings:
```css
a {
& .foo { /* 1 */
&__foo {} /* 2 */
}
}
a .foo__foo .bar .baz {}
```
```css
@media print {
a {
& .foo { /* 1 */
&__foo {} /* 2 */
}
}
}
```
## Optional secondary options
### `ignore: ["blockless-at-rules"]`
***Note: This option was previously called `at-rules-without-declaration-blocks`.***
Ignore at-rules that only wrap other rules, and do not themselves have declaration blocks.
For example, with `1`:
The following patterns are considered warnings:
As the at-rules have a declarations blocks.
```css
a {
&:hover { /* 1 */
@media (min-width: 500px) { color: pink; } /* 2 */
}
}
```
```css
a {
@nest > b { /* 1 */
.foo { color: pink; } /* 2 */
}
}
```
The following patterns are *not* considered warnings:
As all of the following `.foo` rules would have a nesting depth of just 1.
```css
a {
.foo { color: pink; } /* 1 */
}
```
```css
@media print { /* ignored regardless of options */
a {
.foo { color: pink; } /* 1 */
}
}
```
```css
a {
@media print { /* ignored because it's an at-rule without a declaration block of its own */
.foo { color: pink; } /* 1 */
}
}
```
### `ignoreAtRules: ["/regex/", "string"]`
Ignore the specified at-rules.
For example, with `1` and given:
```js
["/^my-/", "media"]
```
The following patterns are *not* considered warnings:
```css
a {
@media print { /* 1 */
b { /* 2 */
c { top: 0; } /* 3 */
}
}
}
```
```css
a {
b { /* 1 */
@media print { /* 2 */
c { top: 0; } /* 3 */
}
}
}
```
```css
a {
@my-at-rule print { /* 1 */
b { /* 2 */
c { top: 0; } /* 3 */
}
}
}
```
```css
a {
@my-other-at-rule print { /* 1 */
b { /* 2 */
c { top: 0; } /* 3 */
}
}
}
```
The following patterns are considered warnings:
```css
a {
@import print { /* 1 */
b { top: 0; } /* 2 */
}
}
```
```css
a {
@not-my-at-rule print { /* 1 */
b { top: 0; } /* 2 */
}
}
```

View File

@@ -0,0 +1,97 @@
"use strict"
const hasBlock = require("../../utils/hasBlock")
const optionsMatches = require("../../utils/optionsMatches")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const _ = require("lodash")
const ruleName = "max-nesting-depth"
const messages = ruleMessages(ruleName, {
expected: depth => `Expected nesting depth to be no more than ${depth}`,
})
const rule = function (max, options) {
const ignoreAtRulesWithoutDeclarationBlocks = optionsMatches(options, "ignore", "at-rules-without-declaration-blocks") || optionsMatches(options, "ignore", "blockless-at-rules")
const isIgnoreAtRule = node => node.type === "atrule" && optionsMatches(options, "ignoreAtRules", node.name)
return (root, result) => {
validateOptions(result, ruleName, {
actual: max,
possible: [_.isNumber],
}, {
optional: true,
actual: options,
possible: {
ignore: [
"at-rules-without-declaration-blocks",
"blockless-at-rules",
],
ignoreAtRules: [_.isString],
},
})
if (optionsMatches(options, "ignore", "at-rules-without-declaration-blocks")) {
result.warn((
"'max-nesting-depth\'s' \"at-rules-without-declaration-blocks\" option has been deprecated and in 8.0 will be removed. Instead use the \"blockless-at-rules\" option."
), {
stylelintType: "deprecation",
stylelintReference: "https://stylelint.io/user-guide/rules/max-nesting-depth/",
})
}
root.walkRules(checkStatement)
root.walkAtRules(checkStatement)
function checkStatement(statement) {
if (isIgnoreAtRule(statement)) {
return
}
if (!hasBlock(statement)) {
return
}
const depth = nestingDepth(statement)
if (depth > max) {
report({
ruleName,
result,
node: statement,
message: messages.expected(max),
})
}
}
}
function nestingDepth(node, level) {
level = level || 0
const parent = node.parent
if (isIgnoreAtRule(parent)) {
return 0
}
// The nesting depth level's computation has finished
// when this function, recursively called, receives
// a node that is not nested -- a direct child of the
// root node
if (parent.type === "root" || parent.type === "atrule" && parent.parent.type === "root") {
return level
}
if (ignoreAtRulesWithoutDeclarationBlocks && node.type === "atrule" && node.every(child => child.type !== "decl")) {
return nestingDepth(parent, level)
}
// Unless any of the conditions above apply, we want to
// add 1 to the nesting depth level and then check the parent,
// continuing to add and move up the hierarchy
// until we hit the root node
return nestingDepth(parent, level + 1)
}
}
rule.ruleName = ruleName
rule.messages = messages
module.exports = rule