Skip to content

Commit

Permalink
Add except: ["after-closing-brace"] to block-closing-brace-empty-line… (
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasjbradley authored and jeddy3 committed Dec 7, 2017
1 parent 4e94180 commit 80aed9a
Show file tree
Hide file tree
Showing 3 changed files with 321 additions and 8 deletions.
153 changes: 153 additions & 0 deletions lib/rules/block-closing-brace-empty-line-before/README.md
Expand Up @@ -60,3 +60,156 @@ a {
```css
a { color: pink; }
```

## Optional secondary options

### `except: ["after-closing-brace"]`

When a rule is nested, `after-closing-brace` brace will reverse the primary option.

For example, with `"never"` and `except: ["after-closing-brace"]`:

The following patterns are considered violations:

```css
@media print {

a {
color: aquamarine;
}
}
```

```css
@supports (animation-name: test) {

a {
color: aquamarine;
}
}
```

```css
@keyframes test {

100% {
color: aquamarine;
}
}
```

The following patterns are *not* considered violations:

```css
@media print {

a {
color: aquamarine;
}

}
```

```css
@font-face {
font-family: "MyFont";
src: url("myfont.woff2") format("woff2");
}
```

```css
@supports (animation-name: test) {

a {
color: aquamarine;
}

}
```

```css
@keyframes test {

100% {
color: aquamarine;
}

}
```

For example, with `"always-multi-line"` and `except: ["after-closing-brace"]`:

The following patterns are considered violations:

```css
@media print {

a {
color: aquamarine;

}

}
```

```css
@supports (animation-name: test) {

a {
color: aquamarine;

}

}
```

```css
@keyframes test {

100% {
color: aquamarine;

}

}
```

The following patterns are *not* considered violations:

```css
@media print {

a {
color: aquamarine;

}
}
```

```css
@font-face {
font-family: "MyFont";
src: url("myfont.woff2") format("woff2");

}
```

```css
@supports (animation-name: test) {

a {
color: aquamarine;

}
}
```

```css
@keyframes test {

100% {
color: aquamarine;

}
}
```
137 changes: 137 additions & 0 deletions lib/rules/block-closing-brace-empty-line-before/__tests__/index.js
Expand Up @@ -195,3 +195,140 @@ testRule(rule, {
}
]
});

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

accept: [
{
code: "a {\n\tcolor: aquamarine;\n}"
},
{
code: "@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\t}\n\n}"
},
{
code:
'@font-face {\n\tfont-family: "MyFont";\n\tsrc: url("myfont.woff2") format("woff2");\n}'
},
{
code:
"@supports (animation-name: test) {\n\n\ta {\n\t\tcolor: aquamarine;\n\t}\n\n}"
},
{
code: "@keyframes test {\n\n\t100% {\n\t\tcolor: aquamarine;\n\t}\n\n}"
}
],

reject: [
{
code: "a {\n\tcolor: aquamarine;\n\n}",
message: messages.rejected,
line: 4,
column: 1
},
{
code: "@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\t}\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}",
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}",
message: messages.rejected,
line: 10,
column: 2
},
{
code:
"@supports (animation-name: test) {\n\n\ta {\n\t\tcolor: aquamarine;\n\t}\n}",
message: messages.expected,
line: 6,
column: 1
},
{
code: "@keyframes test {\n\n\t100% {\n\t\tcolor: aquamarine;\n\t}\n}",
message: messages.expected,
line: 6,
column: 1
}
]
});

testRule(rule, {
ruleName,
config: ["always-multi-line", { except: ["after-closing-brace"] }],

accept: [
{
code: "a {\n\tcolor: aquamarine;\n\n}"
},
{
code: "a { color: aquamarine; }"
},
{
code: "@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\n\t}\n}"
},
{
code:
'@font-face {\n\tfont-family: "MyFont";\n\tsrc: url("myfont.woff2") format("woff2");\n\n}'
},
{
code:
"@supports (animation-name: test) {\n\n\ta {\n\t\tcolor: aquamarine;\n\n\t}\n}"
},
{
code: "@keyframes test {\n\n\t100% {\n\t\tcolor: aquamarine;\n\n\t}\n}"
}
],

reject: [
{
code: "a {\n\tcolor: aquamarine;\n}",
message: messages.expected,
line: 3,
column: 1
},
{
code: "@media print {\n\n\ta {\n\t\tcolor: aquamarine;\n\n\t}\n\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}",
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}",
message: messages.rejected,
line: 13,
column: 1
},
{
code:
"@supports (animation-name: test) {\n\n\ta {\n\t\tcolor: aquamarine;\n\n\t}\n\n}",
message: messages.rejected,
line: 8,
column: 1
},
{
code: "@keyframes test {\n\n\t100% {\n\t\tcolor: aquamarine;\n\n\t}\n\n}",
message: messages.rejected,
line: 8,
column: 1
}
]
});
39 changes: 31 additions & 8 deletions lib/rules/block-closing-brace-empty-line-before/index.js
Expand Up @@ -5,6 +5,7 @@ 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 report = require("../../utils/report");
const ruleMessages = require("../../utils/ruleMessages");
const validateOptions = require("../../utils/validateOptions");
Expand All @@ -16,12 +17,23 @@ const messages = ruleMessages(ruleName, {
rejected: "Unexpected empty line before closing brace"
});

const rule = function(expectation) {
const rule = function(expectation, options) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
possible: ["always-multi-line", "never"]
});
const validOptions = validateOptions(
result,
ruleName,
{
actual: expectation,
possible: ["always-multi-line", "never"]
},
{
actual: options,
possible: {
except: ["after-closing-brace"]
},
optional: true
}
);
if (!validOptions) {
return;
}
Expand Down Expand Up @@ -50,11 +62,22 @@ const rule = function(expectation) {
}

// Set expectation
const expectEmptyLineBefore =
expectation === "always-multi-line" &&
!isSingleLineString(blockString(statement))
const expectEmptyLineBefore = (() => {
const childNodeTypes = statement.nodes.map(item => item.type);

// Reverse the primary options if `after-closing-brace` is set
if (
optionsMatches(options, "except", "after-closing-brace") &&
(statement.type === "atrule" && childNodeTypes.indexOf("decl") === -1)
) {
return expectation === "never" ? true : false;
}

return expectation === "always-multi-line" &&
!isSingleLineString(blockString(statement))
? true
: false;
})();

// Check for at least one empty line
const hasEmptyLineBefore = hasEmptyLine(before);
Expand Down

0 comments on commit 80aed9a

Please sign in to comment.