Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add autofix to block-closing-brace-empty-line-before #3617

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/user-guide/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ Here are all the rules within stylelint, grouped first [by category](../../VISIO

#### Block

- [`block-closing-brace-empty-line-before`](../../lib/rules/block-closing-brace-empty-line-before/README.md): Require or disallow an empty line before the closing brace of blocks.
- [`block-closing-brace-empty-line-before`](../../lib/rules/block-closing-brace-empty-line-before/README.md): Require or disallow an empty line before the closing brace of blocks (Autofixable).
- [`block-closing-brace-newline-after`](../../lib/rules/block-closing-brace-newline-after/README.md): Require a newline or disallow whitespace after the closing brace of blocks (Autofixable).
- [`block-closing-brace-newline-before`](../../lib/rules/block-closing-brace-newline-before/README.md): Require a newline or disallow whitespace before the closing brace of blocks (Autofixable).
- [`block-closing-brace-space-after`](../../lib/rules/block-closing-brace-space-after/README.md): Require a single space or disallow whitespace after the closing brace of blocks.
Expand Down
2 changes: 2 additions & 0 deletions lib/rules/block-closing-brace-empty-line-before/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ a {
* This line */
```

The `--fix` option on the [command line](../../../docs/user-guide/cli.md#autofixing-errors) can automatically fix all of the problems reported by this rule.

## Options

`string`: `"always-multi-line"|"never"`
Expand Down
75 changes: 75 additions & 0 deletions lib/rules/block-closing-brace-empty-line-before/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const { messages, ruleName } = rule;
testRule(rule, {
ruleName,
config: ["always-multi-line"],
fix: true,

accept: [
{
Expand Down Expand Up @@ -51,58 +52,90 @@ testRule(rule, {
reject: [
{
code: "a { color: pink;\n}",
fixed: "a { color: pink;\n\n}",
message: messages.expected,
line: 2,
column: 1
},
{
code: "a { color: pink;\r\n}",
fixed: "a { color: pink;\r\n\r\n}",
message: messages.expected,
line: 2,
column: 1
},
{
code: "a { color: pink;\n }",
fixed: "a { color: pink;\n\n }",
message: messages.expected,
line: 2,
column: 2
},
{
code: "a { color: pink;\n\t}",
fixed: "a { color: pink;\n\n\t}",
message: messages.expected,
line: 2,
column: 2
},
{
code: "a { color: pink;\r\n }",
fixed: "a { color: pink;\r\n\r\n }",
message: messages.expected,
line: 2,
column: 3
},
{
code: "a { color: pink;\n;}",
fixed: "a { color: pink;\n;\n\n}",
message: messages.expected,
line: 2,
column: 2
},
{
code: "a {\ncolor: pink;\n}",
fixed: "a {\ncolor: pink;\n\n}",
message: messages.expected,
line: 3,
column: 1
},
{
code: "a {\n\ncolor: pink;\n}",
fixed: "a {\n\ncolor: pink;\n\n}",
message: messages.expected,
line: 4,
column: 1
},
{
code: "a { color: pink;\n\n/* comment here*/\n}",
fixed: "a { color: pink;\n\n/* comment here*/\n\n}",
message: messages.expected,
line: 4,
column: 1
},
{
code: "a { color: pink;\r\n\r\n/* comment here*/\r\n}",
fixed: "a { color: pink;\r\n\r\n/* comment here*/\r\n\r\n}",
message: messages.expected,
line: 4,
column: 1
},
{
code:
"@media print {\n a {\n color: pink;\n/* comment here*/\n }\n}",
fixed:
"@media print {\n a {\n color: pink;\n/* comment here*/\n\n }\n\n}",
message: messages.expected,
line: 5,
column: 3
}
]
});

testRule(rule, {
ruleName,
config: ["never"],
fix: true,

accept: [
{
Expand Down Expand Up @@ -144,58 +177,81 @@ testRule(rule, {
reject: [
{
code: "a { color: pink;\n\n}",
fixed: "a { color: pink;\n}",
message: messages.rejected,
line: 3,
column: 1
},
{
code: "a { color: pink;\r\n\r\n}",
fixed: "a { color: pink;\r\n}",
message: messages.rejected,
line: 3,
column: 1
},
{
code: "a { color: pink;\n\n }",
fixed: "a { color: pink;\n }",
message: messages.rejected,
line: 3,
column: 2
},
{
code: "a { color: pink;\n\n\t}",
fixed: "a { color: pink;\n\t}",
message: messages.rejected,
line: 3,
column: 2
},
{
code: "a { color: pink;\r\n\r\n }",
fixed: "a { color: pink;\r\n }",
message: messages.rejected,
line: 3,
column: 3
},
{
code: "a { color: pink;\n\n;}",
fixed: "a { color: pink;\n;}",
message: messages.rejected,
line: 3,
column: 2
},
{
code: "a {\ncolor: pink;\n\n}",
fixed: "a {\ncolor: pink;\n}",
message: messages.rejected,
line: 4,
column: 1
},
{
code: "a {\n\ncolor: pink;\n\n}",
fixed: "a {\n\ncolor: pink;\n}",
message: messages.rejected,
line: 5,
column: 1
},
{
code: "@media print {\n a {\n color: pink;\n\n }\n\n}",
fixed: "@media print {\n a {\n color: pink;\n }\n}",
message: messages.rejected,
line: 5,
column: 3
},
{
code: "a {\n\ncolor: pink;\n\n/* comment here */\n\n}",
fixed: "a {\n\ncolor: pink;\n\n/* comment here */\n}",
message: messages.rejected,
line: 7,
column: 1
}
]
});

testRule(rule, {
ruleName,
config: ["never", { except: ["after-closing-brace"] }],
fix: true,

accept: [
{
Expand All @@ -220,39 +276,48 @@ testRule(rule, {
reject: [
{
code: "a {\n\tcolor: aquamarine;\n\n}",
fixed: "a {\n\tcolor: aquamarine;\n}",
message: messages.rejected,
line: 4,
column: 1
},
{
code: "@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\t}\n}",
fixed: "@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\t}\n\n}",
message: messages.expected,
line: 6,
column: 1
},
{
code:
"@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\t}\n\n\tb {\n\t\tcolor: hotpink;\n\t}\n}",
fixed:
"@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\t}\n\n\tb {\n\t\tcolor: hotpink;\n\t}\n\n}",
message: messages.expected,
line: 10,
column: 1
},
{
code:
"@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\t}\n\n\tb {\n\t\tcolor: hotpink;\n\n\t}\n}",
fixed:
"@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\t}\n\n\tb {\n\t\tcolor: hotpink;\n\t}\n\n}",
message: messages.rejected,
line: 10,
column: 2
},
{
code:
"@supports (animation-name: test) {\n\n\ta {\n\t\tcolor: aquamarine;\n\t}\n}",
fixed:
"@supports (animation-name: test) {\n\n\ta {\n\t\tcolor: aquamarine;\n\t}\n\n}",
message: messages.expected,
line: 6,
column: 1
},
{
code: "@keyframes test {\n\n\t100% {\n\t\tcolor: aquamarine;\n\t}\n}",
fixed: "@keyframes test {\n\n\t100% {\n\t\tcolor: aquamarine;\n\t}\n\n}",
message: messages.expected,
line: 6,
column: 1
Expand All @@ -263,6 +328,7 @@ testRule(rule, {
testRule(rule, {
ruleName,
config: ["always-multi-line", { except: ["after-closing-brace"] }],
fix: true,

accept: [
{
Expand Down Expand Up @@ -290,39 +356,48 @@ testRule(rule, {
reject: [
{
code: "a {\n\tcolor: aquamarine;\n}",
fixed: "a {\n\tcolor: aquamarine;\n\n}",
message: messages.expected,
line: 3,
column: 1
},
{
code: "@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\n\t}\n\n}",
fixed: "@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\n\t}\n}",
message: messages.rejected,
line: 8,
column: 1
},
{
code:
"@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\n\t}\n\n\tb {\n\t\tcolor: hotpink;\n\n\t}\n\n}",
fixed:
"@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\n\t}\n\n\tb {\n\t\tcolor: hotpink;\n\n\t}\n}",
message: messages.rejected,
line: 13,
column: 1
},
{
code:
"@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\n\t}\n\n\tb {\n\t\tcolor: hotpink;\n\n\t}\n\n}",
fixed:
"@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\n\t}\n\n\tb {\n\t\tcolor: hotpink;\n\n\t}\n}",
message: messages.rejected,
line: 13,
column: 1
},
{
code:
"@supports (animation-name: test) {\n\n\ta {\n\t\tcolor: aquamarine;\n\n\t}\n\n}",
fixed:
"@supports (animation-name: test) {\n\n\ta {\n\t\tcolor: aquamarine;\n\n\t}\n}",
message: messages.rejected,
line: 8,
column: 1
},
{
code: "@keyframes test {\n\n\t100% {\n\t\tcolor: aquamarine;\n\n\t}\n\n}",
fixed: "@keyframes test {\n\n\t100% {\n\t\tcolor: aquamarine;\n\n\t}\n}",
message: messages.rejected,
line: 8,
column: 1
Expand Down
13 changes: 12 additions & 1 deletion lib/rules/block-closing-brace-empty-line-before/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"use strict";

const addEmptyLineAfter = require("../../utils/addEmptyLineAfter");
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 optionsMatches = require("../../utils/optionsMatches");
const removeEmptyLineAfter = require("../../utils/removeEmptyLinesAfter");
const report = require("../../utils/report");
const ruleMessages = require("../../utils/ruleMessages");
const validateOptions = require("../../utils/validateOptions");
Expand All @@ -17,7 +19,7 @@ const messages = ruleMessages(ruleName, {
rejected: "Unexpected empty line before closing brace"
});

const rule = function(expectation, options) {
const rule = function(expectation, options, context) {
return (root, result) => {
const validOptions = validateOptions(
result,
Expand Down Expand Up @@ -87,6 +89,15 @@ const rule = function(expectation, options) {
return;
}

if (context.fix) {
if (expectEmptyLineBefore) {
addEmptyLineAfter(statement, context.newline);
} else {
removeEmptyLineAfter(statement, context.newline);
}
return;
}

const message = expectEmptyLineBefore
? messages.expected
: messages.rejected;
Expand Down
54 changes: 54 additions & 0 deletions lib/utils/__tests__/addEmptyLineAfter.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"use strict";

const addEmptyLineAfter = require("../addEmptyLineAfter");
const postcss = require("postcss");

describe("addEmptyLineBefore", () => {
it("adds single newline to the newline at the beginning", () => {
expect(run("a {\n}", "\n")).toBe("a {\n\n}");
});

it("adds single newline to newline at the beginning with CRLF", () => {
expect(run("a {\r\n}", "\r\n")).toBe("a {\r\n\r\n}");
});

it("adds single newline to newline at the end", () => {
expect(run("a {\t\n}", "\n")).toBe("a {\t\n\n}");
});

it("adds single newline to newline at the end with CRLF", () => {
expect(run("a {\t\r\n}", "\r\n")).toBe("a {\t\r\n\r\n}");
});

it("adds single newline to newline in the middle", () => {
expect(run("a { \n\t}", "\n")).toBe("a { \n\n\t}");
});

it("adds single newline to newline in the middle with CRLF", () => {
expect(run("a { \r\n\t}", "\r\n")).toBe("a { \r\n\r\n\t}");
});

it("adds two newlines if there aren't any existing newlines", () => {
expect(run("a { }", "\n")).toBe("a { \n\n}");
});

it("adds two newlines if there aren't any existing newlines with CRLF", () => {
expect(run("a { }", "\r\n")).toBe("a { \r\n\r\n}");
});

it("adds two newlines if there aren't any newlines after semicolon", () => {
expect(run("a {\n;}", "\n")).toBe("a {\n;\n\n}");
});

it("adds two newlines if there aren't any newlines after semicolon with CRLF", () => {
expect(run("a {\r\n;}", "\r\n")).toBe("a {\r\n;\r\n\r\n}");
});
});

function run(css, lineEnding) {
const root = postcss.parse(css);

addEmptyLineAfter(root.nodes[0], lineEnding);

return root.toString();
}
Loading