diff --git a/docs/user-guide/example-config.md b/docs/user-guide/example-config.md index 3dd15a47d9..40069135f3 100644 --- a/docs/user-guide/example-config.md +++ b/docs/user-guide/example-config.md @@ -114,8 +114,7 @@ You might want to learn a little about [how rules are named and how they work to "property-no-unknown": true, "property-no-vendor-prefix": true, "property-whitelist": string|[], - "rule-nested-empty-line-before": "always"|"never", - "rule-non-nested-empty-line-before": "always"|"never", + "rule-empty-line-before": "always"|"never"|"always-multi-line"|"never-multi-line", "selector-attribute-brackets-space-inside": "always"|"never", "selector-attribute-operator-blacklist": string|[], "selector-attribute-operator-space-after": "always"|"never", diff --git a/docs/user-guide/rules.md b/docs/user-guide/rules.md index 71f7399c4f..0d67b7eab5 100644 --- a/docs/user-guide/rules.md +++ b/docs/user-guide/rules.md @@ -199,8 +199,9 @@ Here are all the rules within stylelint, grouped by the [*thing*](http://apps.wo ### Rule -- [`rule-nested-empty-line-before`](../../lib/rules/rule-nested-empty-line-before/README.md): Require or disallow an empty line before nested rules. -- [`rule-non-nested-empty-line-before`](../../lib/rules/rule-non-nested-empty-line-before/README.md): Require or disallow an empty line before non-nested rules. +- [`rule-empty-line-before`](../../lib/rules/rule-empty-line-before/README.md): Require or disallow an empty line before rules. +- [`rule-nested-empty-line-before`](../../lib/rules/rule-nested-empty-line-before/README.md): Require or disallow an empty line before nested rules (deprecated). +- [`rule-non-nested-empty-line-before`](../../lib/rules/rule-non-nested-empty-line-before/README.md): Require or disallow an empty line before non-nested rules (deprecated). ### Media feature diff --git a/lib/rules/index.js b/lib/rules/index.js index 4b11752dd6..6e1ffbbf63 100644 --- a/lib/rules/index.js +++ b/lib/rules/index.js @@ -116,6 +116,7 @@ const propertyNoUnknown = require("./property-no-unknown") const propertyNoVendorPrefix = require("./property-no-vendor-prefix") const propertyWhitelist = require("./property-whitelist") const rootNoStandardProperties = require("./root-no-standard-properties") +const ruleEmptyLineBefore = require("./rule-empty-line-before") const ruleNestedEmptyLineBefore = require("./rule-nested-empty-line-before") const ruleNonNestedEmptyLineBefore = require("./rule-non-nested-empty-line-before") const selectorAttributeBracketsSpaceInside = require("./selector-attribute-brackets-space-inside") @@ -291,6 +292,7 @@ module.exports = { "property-no-vendor-prefix": propertyNoVendorPrefix, "property-whitelist": propertyWhitelist, "root-no-standard-properties": rootNoStandardProperties, + "rule-empty-line-before": ruleEmptyLineBefore, "rule-nested-empty-line-before": ruleNestedEmptyLineBefore, "rule-non-nested-empty-line-before": ruleNonNestedEmptyLineBefore, "selector-attribute-brackets-space-inside": selectorAttributeBracketsSpaceInside, diff --git a/lib/rules/rule-empty-line-before/README.md b/lib/rules/rule-empty-line-before/README.md new file mode 100644 index 0000000000..617a002508 --- /dev/null +++ b/lib/rules/rule-empty-line-before/README.md @@ -0,0 +1,252 @@ +# rule-empty-line-before + +Require or disallow an empty line before rules. + +```css +a {} + /* ← */ +b {} /* ↑ */ +/** ↑ + * This line */ +``` + +If the rule is the very first node in a stylesheet then it is ignored. + +## Options + +`string`: `"always"|"never"|"always-multi-line"|"never-multi-line"` + +### `"always"` + +There *must always* be an empty line before rules. + +The following patterns are considered warnings: + +```css +a {} b {} +``` + +```css +a {} +b {} +``` + +The following patterns are *not* considered warnings: + +```css +a {} + +b {} +``` + +### `"never"` + +There *must never* be an empty line before rules. + +The following patterns are considered warnings: + +```css +a {} + +b {} +``` + +The following patterns are *not* considered warnings: + +```css +a {} b {} +``` + +```css +a {} +b {} +``` + +### `"always-multi-line"` + +There *must always* be an empty line before multi-line rules. + +The following patterns are considered warnings: + +```css +a { + color: red; +} +b { + color: blue; +} +``` + +The following patterns are *not* considered warnings: + +```css +a { + color: red; +} + +b { + color: blue; +} +``` + +### `"never-multi-line"` + +There *must never* be an empty line before multi-line rules. + +The following patterns are considered warnings: + +```css +a { + color: red; +} + +b { + color: blue; +} +``` + +The following patterns are *not* considered warnings: + +```css +a { + color: red; +} +b { + color: blue; +} +``` + +## Optional secondary options + +### `except: ["after-rule", "after-single-line-comment", "inside-block-and-after-rule", "first-nested"]` + +#### `"after-rule"` + +Reverse the primary option if the rule comes after another rule. + +For example, with `"always"`: + +The following patterns are considered warnings: + +```css +a {} + +b {} +``` + +The following patterns are *not* considered warnings: + +```css +a {} +b {} +``` + +#### `"after-single-line-comment"` + +Reverse the primary option if the rule comes after a single-line comment. + +For example, with `"always"`: + +The following patterns are considered warnings: + +```css +/* comment */ + +a {} +``` + +The following patterns are *not* considered warnings: + +```css +/* comment */ +a {} +``` + +#### `"inside-block-and-after-rule"` + +Reverse the primary option if the rule is inside a block and comes after another rule. + +For example, with `"always"`: + +The following patterns are considered warnings: + +```css +@media { + + a {} + + b {} +} +``` + +The following patterns are *not* considered warnings: + +```css +@media { + a {} + b {} +} +``` + +#### `"first-nested"` + +Reverse the primary option if the rule is the first in a block. + +For example, with `"always"`: + +The following patterns are considered warnings: + +```css +@media { + + a {} + + b {} +} +``` + +The following patterns are *not* considered warnings: + +```css +@media { + a {} + + b {} +} +``` + +### `ignore: ["after-comment", "inside-block"]` + +#### `"after-comment"` + +Ignore rules that come after a comment. + +For example, with `"always"`: + +The following patterns are *not* considered warnings: + +```css +/* comment */ +a {} +``` + +#### `"inside-block"` + +Ignore rules that are inside a block. + +For example, with `"always"`: + +The following patterns are *not* considered warnings: + +```css +@media { + a {} +} +``` + +```css +@media { + a {} + b {} +} +``` diff --git a/lib/rules/rule-empty-line-before/__tests__/index.js b/lib/rules/rule-empty-line-before/__tests__/index.js new file mode 100644 index 0000000000..d151c32732 --- /dev/null +++ b/lib/rules/rule-empty-line-before/__tests__/index.js @@ -0,0 +1,436 @@ +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") + +const rule = rules[ruleName] + +testRule(rule, { + ruleName, + config: ["always"], + + accept: [ { + code: "a {}", + description: "first node ignored", + }, { + code: "b {}\n\na {}", + }, { + code: "b {}\r\n\r\na {}", + description: "CRLF", + }, { + code: "b {}\n\r\na {}", + description: "Mixed", + }, { + code: "b {}\n \t\n\na {}", + }, { + code: "b {}\n\n\ta {}", + }, { + code: "b {}\r\n\r\n\ta {}", + description: "CRLF", + }, { + code: "@media {\n\na {} }", + description: "nested", + } ], + + reject: [ { + code: "b {} a {}", + message: messages.expected, + }, { + code: "b {}\na {}", + message: messages.expected, + }, { + code: "b {}\n\n/* comment here*/\na {}", + message: messages.expected, + }, { + code: "b {}\r\n\r\n/* comment here*/\r\na {}", + description: "CRLF", + message: messages.expected, + }, { + code: "@media { b {}\n\n/* comment here*/\na {} }", + description: "nested", + message: messages.expected, + } ], +}) + +testRule(rule, { + ruleName, + config: [ "always", { ignore: ["after-comment"] } ], + skipBasicChecks: true, + + accept: [ { + code: "/* foo */\na {}", + }, { + code: "/* foo */\n\na {}", + }, { + code: "/* foo */\r\n\r\na {}", + description: "CRLF", + }, { + code: "@media { /* foo */\na {} }", + description: "nested", + } ], + + reject: [ { + code: "b {} a {}", + message: messages.expected, + }, { + code: "@media { b {} a {} }", + message: messages.expected, + } ], +}) + +testRule(rule, { + ruleName, + config: [ "always", { ignore: ["inside-block"] } ], + skipBasicChecks: true, + + accept: [ { + code: "b {}\n\na {}", + }, { + code: "@media { b {} a {} }", + } ], + + reject: [{ + code: "b {} a {}", + message: messages.expected, + }], +}) + +testRule(rule, { + ruleName, + config: [ "always", { except: ["after-rule"] } ], + + accept: [ { + code: "a {} \nb {}", + }, { + code: "$var: pink;\n\nb {}", + description: "scss variable", + }, { + code: "@media {}\n\na{}", + description: "media rule", + }, { + code: "@media {\n\na {}\nb {} }", + description: "nested", + } ], + + reject: [ { + code: "a {}\n\nb {}", + message: messages.rejected, + }, { + code: "$var: pink;\nb {}", + message: messages.expected, + }, { + code: "@media {}\na{}", + message: messages.expected, + }, { + code: "@media {\n\na{}\n\nb{}}", + message: messages.rejected, + } ], +}) + +testRule(rule, { + ruleName, + config: [ "always", { except: ["after-single-line-comment"] } ], + skipBasicChecks: true, + + accept: [ { + code: "/**\n * comment\n*/\n\na {}", + }, { + code: "/* comment */\na {}", + }, { + code: "/* comment */\na {}", + }, { + code: "@media { /* comment */\na {} }", + } ], + + reject: [ { + code: "/**\n * comment\n*/\na {}", + message: messages.expected, + }, { + code: "/* comment */\n\na {}", + message: messages.rejected, + }, { + code: "@media { /* comment */\n\na {} }", + message: messages.rejected, + } ], +}) + +testRule(rule, { + ruleName, + config: [ "always", { except: ["first-nested"] } ], + + accept: [ { + code: "@media {\n a {}\n\n}", + }, { + code: "@media {\r\n a {}\r\n\r\n}", + description: "CRLF", + }, { + code: "@media {\n a {}\n\n b{}\n\n}", + }, { + code: "@media {\n\ta {}\n\n\tb{}\n}", + }, { + code: "@media {\n\ta {}}", + }, { + code: "@media {\r\n\ta {}}", + description: "CRLF", + }, { + code: "@media {\na {}\n/* comment */\n\nb {}}", + }, { + code: "@media {\r\na {}\r\n/* comment */\r\n\r\nb {}}", + description: "CRLF", + } ], + + reject: [ { + code: "b {} a {}", + message: messages.expected, + }, { + code: "@media {\n\n a {}\n}", + message: messages.rejected, + }, { + code: "@media {\n\n a {}\n\n b{}\n}", + message: messages.rejected, + }, { + code: "@media {\r\n\r\n a {}\r\n\r\n b{}\r\n}", + description: "CRLF", + message: messages.rejected, + }, { + code: "@media {\n b {} a {} }", + message: messages.expected, + }, { + code: "@media {\r\n b {} a {} }", + description: "CRLF", + message: messages.expected, + }, { + code: "@media {\n b {}\n a {}\n\n}", + message: messages.expected, + } ], +}) + +testRule(rule, { + ruleName, + config: [ "always", { except: ["inside-block-and-after-rule"] } ], + + accept: [ { + code: "a {\n color: pink; \n\n b {color: red; } \n c {color: blue; }\n}", + description: "css property", + }, { + code: "a {\n $var: pink; \n\n b {color: red; } \n c {color: blue; }\n}", + description: "scss variable", + }, { + code: "a {\n --custom-prop: pink; \n\n b {color: red; } \n c {color: blue; }\n}", + description: "custom property", + }, { + code: "a {\n @media {\n\n b {}\n }\n\n c {}\n d {}\n}", + description: "media rule", + }, { + code: "a {\n color: pink; \r\n\r\n b {color: red; } \n c {color: blue; }\n}", + description: "CRLF", + } ], + + reject: [ { + code: "a {} b {}", + message: messages.expected, + }, { + code: "a {}\nb {}", + message: messages.expected, + }, { + code: "a {\n color: pink; b {color: red; }\n c {color: blue; }\n}", + message: messages.expected, + }, { + code: "a {\n color: pink;\n b {color: red; }\n c {color: blue; }\n}", + message: messages.expected, + }, { + code: "a {\n color: pink;\n\n b {color: red; }\n\n c {color: blue; }\n}", + message: messages.rejected, + }, { + code: "a {\n @media {\n\n b {}\n }\n c {}\n d {}\n}", + description: "media rule", + message: messages.expected, + }, { + code: "a {\r\n color: pink;\r\n b {\r\ncolor: red; \r\n}\r\n c {\r\ncolor: blue; \r\n}\r\n}", + description: "CRLF", + message: messages.expected, + } ], +}) + +testRule(rule, { + ruleName, + config: ["never"], + + accept: [ { + code: "\n\na {}", + description: "first node ignored", + }, { + code: "\r\n\r\na {}", + description: "first node ignored and CRLF", + }, { + code: "b {}\na {}", + }, { + code: "b {}\n \t\na {}", + }, { + code: "b {}\r\n \t\r\na {}", + description: "CRLF", + }, { + code: "b {}\ta {}", + } ], + + reject: [ { + code: "b {}\n\na {}", + message: messages.rejected, + }, { + code: "b {}\t\n\n\ta {}", + message: messages.rejected, + }, { + code: "b {}\t\r\n\r\n\ta {}", + description: "CRLF", + message: messages.rejected, + }, { + code: "b {}\n\n/* comment here*/\n\na {}", + message: messages.rejected, + }, { + code: "@media {\n\na {} }", + message: messages.rejected, + } ], +}) + +testRule(rule, { + ruleName, + config: [ "never", { except: ["after-single-line-comment"] } ], + skipBasicChecks: true, + + accept: [ { + code: "/**\n * comment\n*/\na {}", + }, { + code: "/* comment */\n\na {}", + } ], + + reject: [ { + code: "/**\n * comment\n*/\n\na {}", + message: messages.rejected, + }, { + code: "/* comment */\na {}", + message: messages.expected, + } ], +}) + +testRule(rule, { + ruleName, + config: [ "never", { ignore: ["after-comment"] } ], + skipBasicChecks: true, + + accept: [ { + code: "/* foo */\na {}", + }, { + code: "/* foo */\r\na {}", + description: "CRLF", + }, { + code: "/* foo */\n\na {}", + } ], + + reject: [{ + code: "b {}\n\na {}", + message: messages.rejected, + }], +}) + +testRule(rule, { + ruleName, + config: ["always-multi-line"], + + accept: [ { + code: "a {}", + description: "first node ignored", + }, { + code: "b {}\na {}", + description: "single-line ignored", + }, { + code: "b\n{}\n\na\n{}", + }, { + code: "b\r\n{}\r\n\r\na\r\n{}", + description: "CRLF", + }, { + code: "b\n{}\n \t\n\na\n{}", + }, { + code: "b {}\n\n\ta\n{}", + }, { + code: "b {}\r\n\r\n\ta\r\n{}", + description: "CRLF", + } ], + + reject: [ { + code: "b {} a\n{}", + message: messages.expected, + }, { + code: "b\n{}\na\n{}", + message: messages.expected, + }, { + code: "b\r\n{}\r\na\r\n{}", + description: "CRLF", + message: messages.expected, + }, { + code: "b {}\n\n/* comment here*/\na\n{}", + message: messages.expected, + }, { + code: "@media { a\n{} }", + message: messages.expected, + } ], +}) + +testRule(rule, { + ruleName, + config: ["never-multi-line"], + + accept: [ { + code: "\n\na\n{}", + description: "first node ignored", + }, { + code: "b {}\n\na {}", + description: "single-line ignored", + }, { + code: "b\n{}\n \t\na\n{}", + }, { + code: "b\r\n{}\r\n \t\r\na\r\n{}", + description: "CRLF", + }, { + code: "b {}\ta\n{}", + } ], + + reject: [ { + code: "b {}\n\na\n{}", + message: messages.rejected, + }, { + code: "b {}\t\n\n\ta\n{}", + message: messages.rejected, + }, { + code: "b {}\t\r\n\r\n\ta\r\n{}", + description: "CRLF", + message: messages.rejected, + }, { + code: "b {}\n\n/* comment here*/\n\na\n{}", + message: messages.rejected, + }, { + code: "b {}\r\n\r\n/* comment here*/\r\n\r\na\r\n{}", + description: "CRLF", + message: messages.rejected, + }, { + code: "@media\n{\n\na\n{} }", + message: messages.rejected, + }, { + code: "@media\r\n{\r\n\r\na\r\n{} }", + message: messages.rejected, + } ], +}) + +testRule(rule, { + ruleName, + syntax: "less", + config: ["always"], + + accept: [ { + code: "a {}\n.mixin-call() {}", + description: "ignore non-ouputting Less class mixin definition", + }, { + code: "@foo: {};\n@bar: {};", + description: "ignore non-ouputting Less class mixin definition", + } ], +}) diff --git a/lib/rules/rule-empty-line-before/index.js b/lib/rules/rule-empty-line-before/index.js new file mode 100644 index 0000000000..a51a6f4f66 --- /dev/null +++ b/lib/rules/rule-empty-line-before/index.js @@ -0,0 +1,144 @@ +"use strict" + +const hasEmptyLine = require("../../utils/hasEmptyLine") +const isSingleLineString = require("../../utils/isSingleLineString") +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "rule-empty-line-before" + +const messages = ruleMessages(ruleName, { + expected: "Expected empty line before rule", + rejected: "Unexpected empty line before rule", +}) + +const rule = function (expectation, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ + "always", + "never", + "always-multi-line", + "never-multi-line", + ], + }, { + actual: options, + possible: { + ignore: [ + "after-comment", + "inside-block", + ], + except: [ + "after-rule", + "after-single-line-comment", + "first-nested", + "inside-block-and-after-rule", + ], + }, + optional: true, + }) + + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + + // Ignore the first node + if (rule === root.first) { + return + } + + let expectEmptyLineBefore = expectation.indexOf("always") !== -1 ? true : false + + // Optionally ignore the expectation if a comment precedes this node + if ( + optionsMatches(options, "ignore", "after-comment") + && rule.prev() + && rule.prev().type === "comment" + ) { + return + } + + // Optionally ignore the expectation if inside a block + if ( + optionsMatches(options, "ignore", "inside-block") + && rule.parent !== root + ) { + return + } + + // Ignore if the expectation is for multiple and the rule is single-line + if ( + expectation.indexOf("multi-line") !== -1 + && isSingleLineString(rule.toString()) + ) { + return + } + + // Optionally reverse the expectation for the first nested node + if ( + optionsMatches(options, "except", "first-nested") + && rule === rule.parent.first + ) { + expectEmptyLineBefore = !expectEmptyLineBefore + } + + // Optionally reverse the expectation if a rule precedes this node + if ( + optionsMatches(options, "except", "after-rule") + && rule.prev() + && rule.prev().type === "rule" + ) { + expectEmptyLineBefore = !expectEmptyLineBefore + } + + // Optionally reverse the expectation if a rule precedes this node and is inside a block + if ( + optionsMatches(options, "except", "inside-block-and-after-rule") + && rule.prev() + && rule.prev().type === "rule" + && rule.parent !== root + ) { + expectEmptyLineBefore = !expectEmptyLineBefore + } + + // Optionally reverse the expectation for single line comments + if ( + optionsMatches(options, "except", "after-single-line-comment") + && rule.prev() + && rule.prev().type === "comment" + && isSingleLineString(rule.prev().toString()) + ) { + expectEmptyLineBefore = !expectEmptyLineBefore + } + + const hasEmptyLineBefore = hasEmptyLine(rule.raws.before) + + // Return if the expectation is met + if (expectEmptyLineBefore === hasEmptyLineBefore) { + return + } + + const message = expectEmptyLineBefore ? messages.expected : messages.rejected + + report({ + message, + node: rule, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/lib/rules/rule-nested-empty-line-before/README.md b/lib/rules/rule-nested-empty-line-before/README.md index 2eabb33aa8..2ce34a361f 100644 --- a/lib/rules/rule-nested-empty-line-before/README.md +++ b/lib/rules/rule-nested-empty-line-before/README.md @@ -1,5 +1,7 @@ # rule-nested-empty-line-before +***Deprecated: instead use the [rule-empty-line-before](../rule-empty-line-before/README.md).*** + Require or disallow an empty line before nested rules. ```css diff --git a/lib/rules/rule-nested-empty-line-before/__tests__/index.js b/lib/rules/rule-nested-empty-line-before/__tests__/index.js index bd4c43a261..e04a4ad531 100644 --- a/lib/rules/rule-nested-empty-line-before/__tests__/index.js +++ b/lib/rules/rule-nested-empty-line-before/__tests__/index.js @@ -3,9 +3,27 @@ const messages = require("..").messages const ruleName = require("..").ruleName const rules = require("../../../rules") +const stylelint = require("../../../standalone") const rule = rules[ruleName] +it("deprecation warning", () => { + const config = { + rules: { + [ruleName]: "always", + }, + } + + const code = "" + + return stylelint({ code, config }).then(output => { + const result = output.results[0] + expect(result.deprecations.length).toEqual(1) + expect(result.deprecations[0].text).toEqual(`\'${ruleName}\' has been deprecated and in 8.0 will be removed. Instead use \'rule-empty-line-before\'.`) + expect(result.deprecations[0].reference).toEqual(`https://stylelint.io/user-guide/rules/${ruleName}/`) + }) +}) + testRule(rule, { ruleName, config: ["always"], diff --git a/lib/rules/rule-nested-empty-line-before/index.js b/lib/rules/rule-nested-empty-line-before/index.js index 13125389c9..2bdeab34c3 100644 --- a/lib/rules/rule-nested-empty-line-before/index.js +++ b/lib/rules/rule-nested-empty-line-before/index.js @@ -38,6 +38,13 @@ const rule = function (expectation, options) { return } + result.warn(( + `'${ruleName}' has been deprecated and in 8.0 will be removed. Instead use 'rule-empty-line-before'.` + ), { + stylelintType: "deprecation", + stylelintReference: `https://stylelint.io/user-guide/rules/${ruleName}/`, + }) + root.walkRules(rule => { if (!isStandardSyntaxRule(rule)) { return diff --git a/lib/rules/rule-non-nested-empty-line-before/README.md b/lib/rules/rule-non-nested-empty-line-before/README.md index 9da36fffd6..4f621ee020 100644 --- a/lib/rules/rule-non-nested-empty-line-before/README.md +++ b/lib/rules/rule-non-nested-empty-line-before/README.md @@ -1,5 +1,7 @@ # rule-non-nested-empty-line-before +***Deprecated: instead use the [rule-empty-line-before](../rule-empty-line-before/README.md).*** + Require or disallow an empty line before non-nested rules. ```css diff --git a/lib/rules/rule-non-nested-empty-line-before/__tests__/index.js b/lib/rules/rule-non-nested-empty-line-before/__tests__/index.js index 2bdc057e50..63d53db162 100644 --- a/lib/rules/rule-non-nested-empty-line-before/__tests__/index.js +++ b/lib/rules/rule-non-nested-empty-line-before/__tests__/index.js @@ -3,9 +3,27 @@ const messages = require("..").messages const ruleName = require("..").ruleName const rules = require("../../../rules") +const stylelint = require("../../../standalone") const rule = rules[ruleName] +it("deprecation warning", () => { + const config = { + rules: { + [ruleName]: "always", + }, + } + + const code = "" + + return stylelint({ code, config }).then(output => { + const result = output.results[0] + expect(result.deprecations.length).toEqual(1) + expect(result.deprecations[0].text).toEqual(`\'${ruleName}\' has been deprecated and in 8.0 will be removed. Instead use \'rule-empty-line-before\'.`) + expect(result.deprecations[0].reference).toEqual(`https://stylelint.io/user-guide/rules/${ruleName}/`) + }) +}) + testRule(rule, { ruleName, config: ["always"], diff --git a/lib/rules/rule-non-nested-empty-line-before/index.js b/lib/rules/rule-non-nested-empty-line-before/index.js index 0f3b9a8b03..3428bc479f 100644 --- a/lib/rules/rule-non-nested-empty-line-before/index.js +++ b/lib/rules/rule-non-nested-empty-line-before/index.js @@ -34,6 +34,13 @@ const rule = function (expectation, options) { return } + result.warn(( + `'${ruleName}' has been deprecated and in 8.0 will be removed. Instead use 'rule-empty-line-before'.` + ), { + stylelintType: "deprecation", + stylelintReference: `https://stylelint.io/user-guide/rules/${ruleName}/`, + }) + root.walkRules(rule => { if (!isStandardSyntaxRule(rule)) { return diff --git a/system-tests/001/__snapshots__/001.test.js.snap b/system-tests/001/__snapshots__/001.test.js.snap index f4a0e9bee3..a76655d5b6 100644 --- a/system-tests/001/__snapshots__/001.test.js.snap +++ b/system-tests/001/__snapshots__/001.test.js.snap @@ -10,6 +10,14 @@ Array [ "reference": "https://stylelint.io/user-guide/rules/media-feature-no-missing-punctuation/", "text": "\'media-feature-no-missing-punctuation\' has been deprecated and in 8.0 will be removed.", }, + Object { + "reference": "https://stylelint.io/user-guide/rules/rule-nested-empty-line-before/", + "text": "\'rule-nested-empty-line-before\' has been deprecated and in 8.0 will be removed. Instead use \'rule-empty-line-before\'.", + }, + Object { + "reference": "https://stylelint.io/user-guide/rules/rule-non-nested-empty-line-before/", + "text": "\'rule-non-nested-empty-line-before\' has been deprecated and in 8.0 will be removed. Instead use \'rule-empty-line-before\'.", + }, Object { "reference": "https://stylelint.io/user-guide/rules/selector-no-empty/", "text": "\'selector-no-empty\' has been deprecated and in 8.0 will be removed.", diff --git a/system-tests/002/__snapshots__/002.test.js.snap b/system-tests/002/__snapshots__/002.test.js.snap index 0a4ebc9dac..00e5d86d0d 100644 --- a/system-tests/002/__snapshots__/002.test.js.snap +++ b/system-tests/002/__snapshots__/002.test.js.snap @@ -10,6 +10,14 @@ Array [ "reference": "https://stylelint.io/user-guide/rules/media-feature-no-missing-punctuation/", "text": "\'media-feature-no-missing-punctuation\' has been deprecated and in 8.0 will be removed.", }, + Object { + "reference": "https://stylelint.io/user-guide/rules/rule-nested-empty-line-before/", + "text": "\'rule-nested-empty-line-before\' has been deprecated and in 8.0 will be removed. Instead use \'rule-empty-line-before\'.", + }, + Object { + "reference": "https://stylelint.io/user-guide/rules/rule-non-nested-empty-line-before/", + "text": "\'rule-non-nested-empty-line-before\' has been deprecated and in 8.0 will be removed. Instead use \'rule-empty-line-before\'.", + }, ], "errored": undefined, "ignored": undefined, diff --git a/system-tests/003/__snapshots__/003.test.js.snap b/system-tests/003/__snapshots__/003.test.js.snap index a2d88db4e8..171b2cecd1 100644 --- a/system-tests/003/__snapshots__/003.test.js.snap +++ b/system-tests/003/__snapshots__/003.test.js.snap @@ -6,6 +6,14 @@ Array [ "reference": "https://stylelint.io/user-guide/rules/declaration-block-properties-order/", "text": "\'declaration-block-properties-order\'has been deprecated and in 8.0 will be removed. Instead use the community \'stylelint-order\' plugin pack.", }, + Object { + "reference": "https://stylelint.io/user-guide/rules/rule-nested-empty-line-before/", + "text": "\'rule-nested-empty-line-before\' has been deprecated and in 8.0 will be removed. Instead use \'rule-empty-line-before\'.", + }, + Object { + "reference": "https://stylelint.io/user-guide/rules/rule-non-nested-empty-line-before/", + "text": "\'rule-non-nested-empty-line-before\' has been deprecated and in 8.0 will be removed. Instead use \'rule-empty-line-before\'.", + }, ], "errored": undefined, "ignored": undefined, diff --git a/system-tests/005/__snapshots__/005.test.js.snap b/system-tests/005/__snapshots__/005.test.js.snap index d508ab65e7..7e2b7355c5 100644 --- a/system-tests/005/__snapshots__/005.test.js.snap +++ b/system-tests/005/__snapshots__/005.test.js.snap @@ -38,6 +38,14 @@ Array [ "reference": "https://stylelint.io/user-guide/rules/root-no-standard-properties/", "text": "\'root-no-standard-properties\' has been deprecated and in 8.0 will be removed.", }, + Object { + "reference": "https://stylelint.io/user-guide/rules/rule-nested-empty-line-before/", + "text": "\'rule-nested-empty-line-before\' has been deprecated and in 8.0 will be removed. Instead use \'rule-empty-line-before\'.", + }, + Object { + "reference": "https://stylelint.io/user-guide/rules/rule-non-nested-empty-line-before/", + "text": "\'rule-non-nested-empty-line-before\' has been deprecated and in 8.0 will be removed. Instead use \'rule-empty-line-before\'.", + }, Object { "reference": "https://stylelint.io/user-guide/rules/selector-no-empty/", "text": "\'selector-no-empty\' has been deprecated and in 8.0 will be removed.", diff --git a/system-tests/deprecations/001/__snapshots__/001.test.js.snap b/system-tests/deprecations/001/__snapshots__/001.test.js.snap index 53d8c1decf..a3c00bdb26 100644 --- a/system-tests/deprecations/001/__snapshots__/001.test.js.snap +++ b/system-tests/deprecations/001/__snapshots__/001.test.js.snap @@ -46,6 +46,14 @@ Array [ "reference": "https://stylelint.io/user-guide/rules/root-no-standard-properties/", "text": "\'root-no-standard-properties\' has been deprecated and in 8.0 will be removed.", }, + Object { + "reference": "https://stylelint.io/user-guide/rules/rule-nested-empty-line-before/", + "text": "\'rule-nested-empty-line-before\' has been deprecated and in 8.0 will be removed. Instead use \'rule-empty-line-before\'.", + }, + Object { + "reference": "https://stylelint.io/user-guide/rules/rule-non-nested-empty-line-before/", + "text": "\'rule-non-nested-empty-line-before\' has been deprecated and in 8.0 will be removed. Instead use \'rule-empty-line-before\'.", + }, Object { "reference": "https://stylelint.io/user-guide/rules/selector-root-no-composition/", "text": "\'selector-root-no-composition\' has been deprecated and in 8.0 will be removed.", diff --git a/system-tests/deprecations/001/config.json b/system-tests/deprecations/001/config.json index 69837153f2..e01758c877 100644 --- a/system-tests/deprecations/001/config.json +++ b/system-tests/deprecations/001/config.json @@ -11,6 +11,8 @@ "stylelint-disable-reason": "always-before", "custom-property-no-outside-root": true, "root-no-standard-properties": true, + "rule-nested-empty-line-before": "always", + "rule-non-nested-empty-line-before": "always", "selector-root-no-composition": true, "time-no-imperceptible": true } diff --git a/system-tests/deprecations/002/__snapshots__/002.test.js.snap b/system-tests/deprecations/002/__snapshots__/002.test.js.snap index e1144ae259..03f27f84f7 100644 --- a/system-tests/deprecations/002/__snapshots__/002.test.js.snap +++ b/system-tests/deprecations/002/__snapshots__/002.test.js.snap @@ -18,6 +18,10 @@ Array [ "reference": "https://stylelint.io/user-guide/rules/max-nesting-depth/", "text": "\'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.", }, + Object { + "reference": "https://stylelint.io/user-guide/rules/rule-nested-empty-line-before/", + "text": "\'rule-nested-empty-line-before\' has been deprecated and in 8.0 will be removed. Instead use \'rule-empty-line-before\'.", + }, ], "errored": true, "ignored": undefined,