Skip to content

Commit

Permalink
Add noEmptyLineBetween for groups in properties-order
Browse files Browse the repository at this point in the history
Closes #11
  • Loading branch information
hudochenkov committed Apr 7, 2019
1 parent a274e7b commit 3817961
Show file tree
Hide file tree
Showing 6 changed files with 338 additions and 2 deletions.
50 changes: 50 additions & 0 deletions rules/properties-order/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Within an order array, you can include
* `order: "flexible"`: If property isn't set (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.
* `emptyLineBefore ("always"|"never")`: If `always`, this group must be separated from other properties by an empty newline. If emptyLineBefore is `never`, the group must have no empty lines separating it from other properties. By default this property isn't set. Rule will check empty lines between properties _only_. However, shared-line comments ignored by rule. Shared-line comment is a comment on the same line as declaration before this comment.
* `noEmptyLineBetween`: If `true` properties withing group should not have empty lines between them.
* `groupName`: An optional name for the group. This will be used in error messages.

There are some important details to keep in mind:
Expand Down Expand Up @@ -336,6 +337,55 @@ a {

Given:

```js
[
{
emptyLineBefore: "always",
noEmptyLineBetween: true,
properties: [
"height",
"width",
],
},
{
emptyLineBefore: "always",
noEmptyLineBetween: true,
properties: [
"font-size",
"font-weight",
],
},
]
```

The following pattern is considered warnings:

```css
a {
height: 1px;

width: 2px;

font-size: 2px;

font-weight: bold;
}
```

The following patterns is *not* considered warnings:

```css
a {
height: 1px;
width: 2px;

font-size: 2px;
font-weight: bold;
}
```

Given:

```js
[
"height",
Expand Down
24 changes: 23 additions & 1 deletion rules/properties-order/checkEmptyLineBefore.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = function checkEmptyLineBefore(firstPropData, secondPropData, sh
const firstPropIsUnspecified = !firstPropData.orderData;
const secondPropIsUnspecified = !secondPropData.orderData;

// Now check newlines between ...
// Check newlines between groups
const firstPropSeparatedGroup = !firstPropIsUnspecified
? firstPropData.orderData.separatedGroup
: sharedInfo.lastKnownSeparatedGroup;
Expand Down Expand Up @@ -50,4 +50,26 @@ module.exports = function checkEmptyLineBefore(firstPropData, secondPropData, sh
}
}
}

// Check newlines between properties inside a group
if (
!firstPropIsUnspecified &&
!secondPropIsUnspecified &&
firstPropData.orderData.groupPosition === secondPropData.orderData.groupPosition
) {
const noEmptyLineBefore = secondPropData.orderData.noEmptyLineBeforeInsideGroup;

if (hasEmptyLineBefore(secondPropData.node) && noEmptyLineBefore) {
if (sharedInfo.isFixEnabled) {
removeEmptyLinesBefore(secondPropData.node, sharedInfo.context.newline);
} else {
stylelint.utils.report({
message: sharedInfo.messages.rejectedEmptyLineBefore(secondPropData.name),
node: secondPropData.node,
result: sharedInfo.result,
ruleName: sharedInfo.ruleName,
});
}
}
}
};
10 changes: 9 additions & 1 deletion rules/properties-order/createExpectedOrder.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ module.exports = function createExpectedOrder(input) {
const order = {};
let expectedPosition = 0;
let separatedGroup = 1;
let groupPosition = -1;

appendGroup({ properties: input });

function appendGroup(group) {
groupPosition += 1;
group.properties.forEach(item => appendItem(item, false, group));
}

Expand All @@ -20,7 +22,13 @@ module.exports = function createExpectedOrder(input) {
expectedPosition += 1;
}

order[item] = { separatedGroup, expectedPosition, groupName: group.groupName };
order[item] = {
separatedGroup,
groupPosition,
expectedPosition,
groupName: group.groupName,
noEmptyLineBeforeInsideGroup: group.noEmptyLineBetween,
};

return;
}
Expand Down
217 changes: 217 additions & 0 deletions rules/properties-order/tests/no-empty-line-between.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
const rule = require('..');
const { ruleName, messages } = rule;

testRule(rule, {
ruleName,
config: [
[
{
noEmptyLineBetween: true,
properties: ['display', 'vertical-align', 'content'],
},
{
noEmptyLineBetween: true,
properties: ['position', 'top', 'bottom'],
},
],
],
fix: true,

accept: [
{
code: `
a {
display: block;
vertical-align: middle;
content: "";
position: absolute;
top: 0;
bottom: 0;
}
`,
},
{
code: `
a {
display: block;
vertical-align: middle;
content: "";
position: absolute;
top: 0;
bottom: 0;
}
`,
},
{
code: `
a {
vertical-align: middle;
top: 0;
bottom: 0;
}
`,
},
],

reject: [
{
code: `
a {
display: block;
vertical-align: middle;
content: "";
}
`,
fixed: `
a {
display: block;
vertical-align: middle;
content: "";
}
`,
message: messages.rejectedEmptyLineBefore('vertical-align'),
},
{
code: `
a {
display: block;
vertical-align: middle;
content: "";
}
`,
fixed: `
a {
display: block;
vertical-align: middle;
content: "";
}
`,
message: messages.rejectedEmptyLineBefore('content'),
},
],
});

testRule(rule, {
ruleName,
config: [
[
{
noEmptyLineBetween: true,
properties: ['display', 'vertical-align', 'content'],
},
{
noEmptyLineBetween: true,
properties: ['position', 'top', 'bottom'],
},
],
],
syntax: 'css-in-js',
fix: true,

accept: [
{
code: `
const Component = styled.div\`
display: block;
vertical-align: middle;
content: "";
position: absolute;
top: 0;
bottom: 0;
\`;
`,
},
],

reject: [
{
code: `
const Component = styled.div\`
display: block;
vertical-align: middle;
content: "";
\`;
`,
fixed: `
const Component = styled.div\`
display: block;
vertical-align: middle;
content: "";
\`;
`,
message: messages.rejectedEmptyLineBefore('vertical-align'),
},
],
});

testRule(rule, {
ruleName,
config: [
[
{
emptyLineBefore: 'always',
noEmptyLineBetween: true,
properties: ['display', 'vertical-align', 'content'],
},
{
emptyLineBefore: 'always',
noEmptyLineBetween: true,
properties: ['position', 'top', 'bottom'],
},
],
],
fix: true,

accept: [
{
code: `
a {
display: block;
vertical-align: middle;
content: "";
position: absolute;
top: 0;
bottom: 0;
}
`,
},
{
code: `
a {
vertical-align: middle;
top: 0;
bottom: 0;
}
`,
},
],

reject: [
{
code: `
a {
display: block;
vertical-align: middle;
content: "";
}
`,
fixed: `
a {
display: block;
vertical-align: middle;
content: "";
}
`,
message: messages.rejectedEmptyLineBefore('vertical-align'),
},
],
});
26 changes: 26 additions & 0 deletions rules/properties-order/tests/validate-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,32 @@ testConfig({
],
});

testConfig({
ruleName,
description: 'noEmptyLineBetween',
valid: true,
config: [
{
emptyLineBefore: 'always',
noEmptyLineBetween: true,
properties: [],
},
],
});

testConfig({
ruleName,
description: 'invalid noEmptyLineBetween',
valid: false,
config: [
{
noEmptyLineBetween: 'true',
properties: [],
},
],
message: `Invalid option "[{"noEmptyLineBetween":"true","properties":[]}]" for rule ${ruleName}`,
});

testConfig({
ruleName,
description: 'invalid emptyLineBefore',
Expand Down
13 changes: 13 additions & 0 deletions rules/properties-order/validatePrimaryOption.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,18 @@ module.exports = function validatePrimaryOption(actualOptions) {
return false;
}

// Every object-item's "noEmptyLineBetween" must be a boolean
if (
!objectItems.every(item => {
if (_.isUndefined(item.noEmptyLineBetween)) {
return true;
}

return _.isBoolean(item.noEmptyLineBetween);
})
) {
return false;
}

return true;
};

0 comments on commit 3817961

Please sign in to comment.