Skip to content

Commit 045e5ec

Browse files
jamesarosenhudochenkov
authored andcommitted
Alphabetical properties sorting do not override longhand with shorthand (#85)
Given ```css border-color: transparent; border-bottom-color: pink; ``` the default alphabetical sort would rorder that to ```css border-bottom-color: pink; border-color: transparent; ``` That changes the behavior since the shorthand `border-color` now overrides the longhand `border-bottom-color`. This commit makes the alphabetical sorting shorthand-aware. See hudochenkov/stylelint-order#50
1 parent 8c10243 commit 045e5ec

File tree

7 files changed

+218
-1
lines changed

7 files changed

+218
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
All notable changes to this project will be documented in this file.
33
This project adheres to [Semantic Versioning](https://semver.org/).
44

5+
## 5.0.1
6+
* Fixed: `properties-order: "alphabetical"` now puts shorthands before their longhand forms even if that isn't alphabetical to avoid broken CSS. E. g. `border-color` will be before `border-bottom-color`.
7+
58
## 5.0.0
69
* Dropped Node.js 6 support. Node.js 8.7.0 or greater is now required.
710

lib/properties-order/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Specify the order of properties within declaration blocks.
44

5-
This rule ignore prefixes to determine properties order. E. g. `-moz-transform` is treated as `transform`. But prefixed properties *will always* precede the unprefixed version (e. g. `-moz-transform` will be always before `transform`).
5+
This rule ignore prefixes to determine properties order. E. g. `-moz-transform` is treated as `transform`. Shorthand properties *will always* precede their longhand forms (e.g. `border-style` will always be before `border-bottom-style`). Prefixed properties *will always* precede the unprefixed version (e. g. `-moz-transform` will be always before `transform`).
66

77
Recommended to use this rule only on source files, rather autoprefixed files. Some “non-standard” prefixes could be treated wrong. E. g. different flexbox implementations; `-ms-flex-align: center; align-items: center;` with alphabetical order will be sorted as `align-items: center; -ms-flex-align: center;` because alphabetically `flex-align` is after `align-item`.
88

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
a {
2+
text-decoration: none;
3+
border-bottom-color: pink;
4+
border-color: transparent;
5+
border-width: 1px;
6+
border-style: solid;
7+
}
8+
9+
a {
10+
border-top-style: solid;
11+
animation-delay: 0.2s;
12+
align-items: center;
13+
align-self: flex-start;
14+
border-top-color: red;
15+
background: none;
16+
background-color: transparent;
17+
border-top: 1px solid;
18+
}
19+
20+
a {
21+
border-color: transparent;
22+
border-bottom-color: pink;
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
a {
2+
border-color: transparent;
3+
border-bottom-color: pink;
4+
border-style: solid;
5+
border-width: 1px;
6+
text-decoration: none;
7+
}
8+
9+
a {
10+
align-items: center;
11+
align-self: flex-start;
12+
animation-delay: 0.2s;
13+
background: none;
14+
background-color: transparent;
15+
border-top: 1px solid;
16+
border-top-color: red;
17+
border-top-style: solid;
18+
}
19+
20+
a {
21+
border-color: transparent;
22+
border-bottom-color: pink;
23+
}

lib/properties-order/__tests__/properties-order.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,15 @@ test('Should sort properties alphabetically', () =>
145145
__dirname
146146
));
147147

148+
test('Should sort shorthand properties before their longhand versions', () =>
149+
runTest(
150+
'properties-alphabetical-shorthand',
151+
{
152+
'properties-order': 'alphabetical',
153+
},
154+
__dirname
155+
));
156+
148157
test('Should sort prefixed properties before unprefixed property in alphabetical order', () =>
149158
runTest(
150159
'prefixed-alphabetical',

lib/shorthandData.js

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
'use strict';
2+
3+
// See https://github.com/stylelint/stylelint/blob/10.1.0/lib/reference/shorthandData.js
4+
module.exports = {
5+
margin: ['margin-top', 'margin-bottom', 'margin-left', 'margin-right'],
6+
padding: ['padding-top', 'padding-bottom', 'padding-left', 'padding-right'],
7+
background: [
8+
'background-image',
9+
'background-size',
10+
'background-position',
11+
'background-repeat',
12+
'background-origin',
13+
'background-clip',
14+
'background-attachment',
15+
'background-color',
16+
],
17+
font: [
18+
'font-style',
19+
'font-variant',
20+
'font-weight',
21+
'font-stretch',
22+
'font-size',
23+
'font-family',
24+
'line-height',
25+
],
26+
border: [
27+
'border-top-width',
28+
'border-bottom-width',
29+
'border-left-width',
30+
'border-right-width',
31+
'border-top-style',
32+
'border-bottom-style',
33+
'border-left-style',
34+
'border-right-style',
35+
'border-top-color',
36+
'border-bottom-color',
37+
'border-left-color',
38+
'border-right-color',
39+
],
40+
'border-top': ['border-top-width', 'border-top-style', 'border-top-color'],
41+
'border-bottom': ['border-bottom-width', 'border-bottom-style', 'border-bottom-color'],
42+
'border-left': ['border-left-width', 'border-left-style', 'border-left-color'],
43+
'border-right': ['border-right-width', 'border-right-style', 'border-right-color'],
44+
'border-width': [
45+
'border-top-width',
46+
'border-bottom-width',
47+
'border-left-width',
48+
'border-right-width',
49+
],
50+
'border-style': [
51+
'border-top-style',
52+
'border-bottom-style',
53+
'border-left-style',
54+
'border-right-style',
55+
],
56+
'border-color': [
57+
'border-top-color',
58+
'border-bottom-color',
59+
'border-left-color',
60+
'border-right-color',
61+
],
62+
'list-style': ['list-style-type', 'list-style-position', 'list-style-image'],
63+
'border-radius': [
64+
'border-top-right-radius',
65+
'border-top-left-radius',
66+
'border-bottom-right-radius',
67+
'border-bottom-left-radius',
68+
],
69+
transition: [
70+
'transition-delay',
71+
'transition-duration',
72+
'transition-property',
73+
'transition-timing-function',
74+
],
75+
animation: [
76+
'animation-name',
77+
'animation-duration',
78+
'animation-timing-function',
79+
'animation-delay',
80+
'animation-iteration-count',
81+
'animation-direction',
82+
'animation-fill-mode',
83+
'animation-play-state',
84+
],
85+
'border-block-end': [
86+
'border-block-end-width',
87+
'border-block-end-style',
88+
'border-block-end-color',
89+
],
90+
'border-block-start': [
91+
'border-block-start-width',
92+
'border-block-start-style',
93+
'border-block-start-color',
94+
],
95+
'border-image': [
96+
'border-image-source',
97+
'border-image-slice',
98+
'border-image-width',
99+
'border-image-outset',
100+
'border-image-repeat',
101+
],
102+
'border-inline-end': [
103+
'border-inline-end-width',
104+
'border-inline-end-style',
105+
'border-inline-end-color',
106+
],
107+
'border-inline-start': [
108+
'border-inline-start-width',
109+
'border-inline-start-style',
110+
'border-inline-start-color',
111+
],
112+
'column-rule': ['column-rule-width', 'column-rule-style', 'column-rule-color'],
113+
columns: ['column-width', 'column-count'],
114+
flex: ['flex-grow', 'flex-shrink', 'flex-basis'],
115+
'flex-flow': ['flex-direction', 'flex-wrap'],
116+
grid: [
117+
'grid-template-rows',
118+
'grid-template-columns',
119+
'grid-template-areas',
120+
'grid-auto-rows',
121+
'grid-auto-columns',
122+
'grid-auto-flow',
123+
'grid-column-gap',
124+
'grid-row-gap',
125+
],
126+
'grid-area': ['grid-row-start', 'grid-column-start', 'grid-row-end', 'grid-column-end'],
127+
'grid-column': ['grid-column-start', 'grid-column-end'],
128+
'grid-gap': ['grid-row-gap', 'grid-column-gap'],
129+
'grid-row': ['grid-row-start', 'grid-row-end'],
130+
'grid-template': ['grid-template-columns', 'grid-template-rows', 'grid-template-areas'],
131+
outline: ['outline-color', 'outline-style', 'outline-width'],
132+
'text-decoration': ['text-decoration-color', 'text-decoration-style', 'text-decoration-line'],
133+
'text-emphasis': ['text-emphasis-style', 'text-emphasis-color'],
134+
mask: [
135+
'mask-image',
136+
'mask-mode',
137+
'mask-position',
138+
'mask-size',
139+
'mask-repeat',
140+
'mask-origin',
141+
'mask-clip',
142+
'mask-composite',
143+
],
144+
};

lib/sorting.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const postcss = require('postcss');
22
const _ = require('lodash');
3+
const shorthandData = require('./shorthandData');
34

45
module.exports = {
56
sortDeclarations,
@@ -65,7 +66,21 @@ function sortDeclarations(a, b) {
6566
return a.initialIndex - b.initialIndex;
6667
}
6768

69+
function isShorthand(a, b) {
70+
const longhands = shorthandData[a] || [];
71+
72+
return longhands.includes(b);
73+
}
74+
6875
function sortDeclarationsAlphabetically(a, b) {
76+
if (isShorthand(a.unprefixedName, b.unprefixedName)) {
77+
return -1;
78+
}
79+
80+
if (isShorthand(b.unprefixedName, a.unprefixedName)) {
81+
return 1;
82+
}
83+
6984
if (a.unprefixedName === b.unprefixedName) {
7085
if (a.node.type === 'decl' && b.node.type === 'decl') {
7186
// If first property has no prefix and second property has prefix

0 commit comments

Comments
 (0)