stylelint-order 3.1.1 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/package.json +9 -12
- package/rules/order/checkNode.js +8 -12
- package/rules/order/checkOrder.js +16 -16
- package/rules/order/{createExpectedOrder.js → createOrderInfo.js} +23 -23
- package/rules/order/getOrderData.js +5 -5
- package/rules/order/index.js +19 -25
- package/rules/order/validatePrimaryOption.js +9 -12
- package/rules/properties-alphabetical-order/checkNode.js +62 -0
- package/rules/properties-alphabetical-order/index.js +19 -79
- package/rules/properties-order/README.md +11 -2
- package/rules/properties-order/checkEmptyLineBefore.js +32 -36
- package/rules/properties-order/checkEmptyLineBeforeFirstProp.js +30 -0
- package/rules/properties-order/checkNode.js +21 -14
- package/rules/properties-order/{createExpectedOrder.js → createOrderInfo.js} +3 -3
- package/rules/properties-order/getNodeData.js +5 -5
- package/rules/properties-order/index.js +22 -33
- package/rules/properties-order/validatePrimaryOption.js +1 -1
- package/utils/isCustomProperty.js +1 -1
- package/utils/isDollarVariable.js +1 -1
- package/utils/isStandardSyntaxProperty.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
|
3
3
|
This project adheres to [Semantic Versioning](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 4.0.0
|
|
6
|
+
|
|
7
|
+
* Breaking change: Dropped Node.js 8 support. Node.js 10 or greater is now required.
|
|
8
|
+
* Breaking change: Always remove empty line before the first property if this property has any `emptyLineBefore*` option targeting it in `properties-order`. Even if option set to `always` empty line before the first property will be removed.
|
|
9
|
+
* Fixed false positives for `emptyLineBeforeUnspecified`.
|
|
10
|
+
|
|
5
11
|
## 3.1.1
|
|
6
12
|
|
|
7
13
|
* Added `stylelint@11` as a peer dependency.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stylelint-order",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "A collection of order related linting rules for stylelint.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"stylelint-plugin",
|
|
@@ -16,9 +16,6 @@
|
|
|
16
16
|
"url": "https://github.com/hudochenkov/stylelint-order/issues"
|
|
17
17
|
},
|
|
18
18
|
"homepage": "https://github.com/hudochenkov/stylelint-order",
|
|
19
|
-
"engines": {
|
|
20
|
-
"node": ">=8.7.0"
|
|
21
|
-
},
|
|
22
19
|
"files": [
|
|
23
20
|
"rules",
|
|
24
21
|
"utils",
|
|
@@ -29,21 +26,21 @@
|
|
|
29
26
|
"main": "index.js",
|
|
30
27
|
"dependencies": {
|
|
31
28
|
"lodash": "^4.17.15",
|
|
32
|
-
"postcss": "^7.0.
|
|
29
|
+
"postcss": "^7.0.26",
|
|
33
30
|
"postcss-sorting": "^5.0.1"
|
|
34
31
|
},
|
|
35
32
|
"peerDependencies": {
|
|
36
|
-
"stylelint": "
|
|
33
|
+
"stylelint": "^10.0.1 || ^11.0.0 || ^12.0.0 || ^13.0.0"
|
|
37
34
|
},
|
|
38
35
|
"devDependencies": {
|
|
39
|
-
"eslint": "^6.
|
|
36
|
+
"eslint": "^6.8.0",
|
|
40
37
|
"eslint-config-hudochenkov": "^3.0.1",
|
|
41
|
-
"eslint-config-prettier": "^6.
|
|
42
|
-
"husky": "^3.0
|
|
38
|
+
"eslint-config-prettier": "^6.9.0",
|
|
39
|
+
"husky": "^3.1.0",
|
|
43
40
|
"jest": "^24.9.0",
|
|
44
|
-
"lint-staged": "^9.
|
|
45
|
-
"prettier": "~1.
|
|
46
|
-
"stylelint": "^
|
|
41
|
+
"lint-staged": "^9.5.0",
|
|
42
|
+
"prettier": "~1.19.1",
|
|
43
|
+
"stylelint": "^12.0.1"
|
|
47
44
|
},
|
|
48
45
|
"scripts": {
|
|
49
46
|
"pretest": "eslint .",
|
package/rules/order/checkNode.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
const stylelint = require('stylelint');
|
|
2
|
-
const _ = require('lodash');
|
|
3
2
|
const checkOrder = require('./checkOrder');
|
|
4
3
|
const getOrderData = require('./getOrderData');
|
|
5
4
|
|
|
6
5
|
module.exports = function checkNode(node, sharedInfo) {
|
|
7
|
-
|
|
6
|
+
let allNodesData = [];
|
|
8
7
|
|
|
9
8
|
node.each(function processEveryNode(child) {
|
|
10
9
|
// Skip comments
|
|
@@ -13,27 +12,24 @@ module.exports = function checkNode(node, sharedInfo) {
|
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
// Receive node description and expectedPosition
|
|
16
|
-
|
|
15
|
+
let nodeOrderData = getOrderData(sharedInfo.orderInfo, child);
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
description: nodeOrderData.description,
|
|
17
|
+
let nodeData = {
|
|
20
18
|
node: child,
|
|
19
|
+
description: nodeOrderData.description,
|
|
20
|
+
expectedPosition: nodeOrderData.expectedPosition,
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
if (nodeOrderData.expectedPosition) {
|
|
24
|
-
nodeData.expectedPosition = nodeOrderData.expectedPosition;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const previousNodeData = _.last(allNodesData);
|
|
28
|
-
|
|
29
23
|
allNodesData.push(nodeData);
|
|
30
24
|
|
|
25
|
+
let previousNodeData = allNodesData[allNodesData.length - 2];
|
|
26
|
+
|
|
31
27
|
// Skip first node
|
|
32
28
|
if (!previousNodeData) {
|
|
33
29
|
return;
|
|
34
30
|
}
|
|
35
31
|
|
|
36
|
-
|
|
32
|
+
let isCorrectOrder = checkOrder(previousNodeData, nodeData, allNodesData, sharedInfo);
|
|
37
33
|
|
|
38
34
|
if (isCorrectOrder) {
|
|
39
35
|
return;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
let stylelint = require('stylelint');
|
|
2
|
+
let _ = require('lodash');
|
|
3
3
|
|
|
4
4
|
module.exports = function checkOrder(firstNodeData, secondNodeData, allNodesData, sharedInfo) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
let firstNodeIsSpecified = Boolean(firstNodeData.expectedPosition);
|
|
6
|
+
let secondNodeIsSpecified = Boolean(secondNodeData.expectedPosition);
|
|
7
7
|
|
|
8
8
|
// If both nodes have their position
|
|
9
|
-
if (
|
|
9
|
+
if (firstNodeIsSpecified && secondNodeIsSpecified) {
|
|
10
10
|
return firstNodeData.expectedPosition <= secondNodeData.expectedPosition;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
if (
|
|
14
|
-
// If first node is unspecified, look for a specified node before it
|
|
15
|
-
// compare to the current node
|
|
16
|
-
|
|
13
|
+
if (!firstNodeIsSpecified && secondNodeIsSpecified) {
|
|
14
|
+
// If first node is unspecified, look for a specified node before it
|
|
15
|
+
// to compare to the current node
|
|
16
|
+
let priorSpecifiedNodeData = _.findLast(allNodesData.slice(0, -1), d =>
|
|
17
17
|
Boolean(d.expectedPosition)
|
|
18
18
|
);
|
|
19
19
|
|
|
@@ -43,29 +43,29 @@ module.exports = function checkOrder(firstNodeData, secondNodeData, allNodesData
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
if (
|
|
46
|
+
if (!firstNodeIsSpecified && !secondNodeIsSpecified) {
|
|
47
47
|
return true;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
let { unspecified } = sharedInfo;
|
|
51
51
|
|
|
52
|
-
if (unspecified === 'ignore' && (
|
|
52
|
+
if (unspecified === 'ignore' && (!firstNodeIsSpecified || !secondNodeIsSpecified)) {
|
|
53
53
|
return true;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
if (unspecified === 'top' &&
|
|
56
|
+
if (unspecified === 'top' && !firstNodeIsSpecified) {
|
|
57
57
|
return true;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
if (unspecified === 'top' &&
|
|
60
|
+
if (unspecified === 'top' && !secondNodeIsSpecified) {
|
|
61
61
|
return false;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
if (unspecified === 'bottom' &&
|
|
64
|
+
if (unspecified === 'bottom' && !secondNodeIsSpecified) {
|
|
65
65
|
return true;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
if (unspecified === 'bottom' &&
|
|
68
|
+
if (unspecified === 'bottom' && !firstNodeIsSpecified) {
|
|
69
69
|
return false;
|
|
70
70
|
}
|
|
71
71
|
};
|
|
@@ -1,34 +1,27 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const getDescription = require('./getDescription');
|
|
3
3
|
|
|
4
|
-
module.exports = function
|
|
5
|
-
|
|
4
|
+
module.exports = function createOrderInfo(input) {
|
|
5
|
+
let order = {};
|
|
6
6
|
let expectedPosition = 0;
|
|
7
7
|
|
|
8
8
|
input.forEach(item => {
|
|
9
9
|
expectedPosition += 1;
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
// Convert 'rules' into extended pattern
|
|
12
|
+
if (item === 'rules') {
|
|
13
|
+
item = {
|
|
14
|
+
type: 'rule',
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
if (item
|
|
19
|
-
// Convert 'rules' into extended pattern
|
|
20
|
-
if (item === 'rules') {
|
|
21
|
-
item = {
|
|
22
|
-
type: 'rule',
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
18
|
+
if (item.type === 'rule') {
|
|
26
19
|
// It there are no nodes like that create array for them
|
|
27
20
|
if (!order[item.type]) {
|
|
28
21
|
order[item.type] = [];
|
|
29
22
|
}
|
|
30
23
|
|
|
31
|
-
|
|
24
|
+
let nodeData = {
|
|
32
25
|
expectedPosition,
|
|
33
26
|
description: getDescription(item),
|
|
34
27
|
};
|
|
@@ -44,20 +37,20 @@ module.exports = function createExpectedOrder(input) {
|
|
|
44
37
|
order[item.type].push(nodeData);
|
|
45
38
|
}
|
|
46
39
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
40
|
+
// Convert 'at-rules' into extended pattern
|
|
41
|
+
if (item === 'at-rules') {
|
|
42
|
+
item = {
|
|
43
|
+
type: 'at-rule',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
54
46
|
|
|
47
|
+
if (item.type === 'at-rule') {
|
|
55
48
|
// It there are no nodes like that create array for them
|
|
56
49
|
if (!order[item.type]) {
|
|
57
50
|
order[item.type] = [];
|
|
58
51
|
}
|
|
59
52
|
|
|
60
|
-
|
|
53
|
+
let nodeData = {
|
|
61
54
|
expectedPosition,
|
|
62
55
|
description: getDescription(item),
|
|
63
56
|
};
|
|
@@ -80,6 +73,13 @@ module.exports = function createExpectedOrder(input) {
|
|
|
80
73
|
|
|
81
74
|
order[item.type].push(nodeData);
|
|
82
75
|
}
|
|
76
|
+
|
|
77
|
+
if (_.isString(item)) {
|
|
78
|
+
order[item] = {
|
|
79
|
+
expectedPosition,
|
|
80
|
+
description: getDescription(item),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
83
|
});
|
|
84
84
|
|
|
85
85
|
return order;
|
|
@@ -3,7 +3,7 @@ const calcAtRulePatternPriority = require('./calcAtRulePatternPriority');
|
|
|
3
3
|
const calcRulePatternPriority = require('./calcRulePatternPriority');
|
|
4
4
|
const getDescription = require('./getDescription');
|
|
5
5
|
|
|
6
|
-
module.exports = function getOrderData(
|
|
6
|
+
module.exports = function getOrderData(orderInfo, node) {
|
|
7
7
|
let nodeType;
|
|
8
8
|
|
|
9
9
|
if (utils.isAtVariable(node)) {
|
|
@@ -24,7 +24,7 @@ module.exports = function getOrderData(expectedOrder, node) {
|
|
|
24
24
|
selector: node.selector,
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
const rules =
|
|
27
|
+
const rules = orderInfo.rule;
|
|
28
28
|
|
|
29
29
|
// Looking for most specified pattern, because it can match many patterns
|
|
30
30
|
if (rules && rules.length) {
|
|
@@ -59,7 +59,7 @@ module.exports = function getOrderData(expectedOrder, node) {
|
|
|
59
59
|
nodeType.parameter = node.params;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
const atRules =
|
|
62
|
+
const atRules = orderInfo['at-rule'];
|
|
63
63
|
|
|
64
64
|
// Looking for most specified pattern, because it can match many patterns
|
|
65
65
|
if (atRules && atRules.length) {
|
|
@@ -81,8 +81,8 @@ module.exports = function getOrderData(expectedOrder, node) {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
if (
|
|
85
|
-
return
|
|
84
|
+
if (orderInfo[nodeType]) {
|
|
85
|
+
return orderInfo[nodeType];
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
// Return only description if there no patterns for that node
|
package/rules/order/index.js
CHANGED
|
@@ -1,26 +1,24 @@
|
|
|
1
1
|
const stylelint = require('stylelint');
|
|
2
2
|
const _ = require('lodash');
|
|
3
3
|
const postcssSorting = require('postcss-sorting');
|
|
4
|
-
const
|
|
4
|
+
const { namespace, getContainingNode, isRuleWithNodes } = require('../../utils');
|
|
5
5
|
const checkNode = require('./checkNode');
|
|
6
|
-
const
|
|
6
|
+
const createOrderInfo = require('./createOrderInfo');
|
|
7
7
|
const validatePrimaryOption = require('./validatePrimaryOption');
|
|
8
8
|
|
|
9
|
-
const ruleName =
|
|
9
|
+
const ruleName = namespace('order');
|
|
10
10
|
|
|
11
11
|
const messages = stylelint.utils.ruleMessages(ruleName, {
|
|
12
12
|
expected: (first, second) => `Expected ${first} to come before ${second}`,
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
function rule(
|
|
16
|
-
context = context || {};
|
|
17
|
-
|
|
15
|
+
function rule(primaryOption, options = {}, context = {}) {
|
|
18
16
|
return function(root, result) {
|
|
19
|
-
|
|
17
|
+
let validOptions = stylelint.utils.validateOptions(
|
|
20
18
|
result,
|
|
21
19
|
ruleName,
|
|
22
20
|
{
|
|
23
|
-
actual:
|
|
21
|
+
actual: primaryOption,
|
|
24
22
|
possible: validatePrimaryOption,
|
|
25
23
|
},
|
|
26
24
|
{
|
|
@@ -37,17 +35,13 @@ function rule(expectation, options, context) {
|
|
|
37
35
|
return;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const expectedOrder = createExpectedOrder(expectation);
|
|
44
|
-
|
|
45
|
-
// By default, ignore unspecified properties
|
|
46
|
-
const unspecified = _.get(options, 'unspecified', 'ignore');
|
|
38
|
+
let disableFix = options.disableFix || false;
|
|
39
|
+
let isFixEnabled = context.fix && !disableFix;
|
|
47
40
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
// Contains information which will be shared in many files and this info would be mutated to share state between them
|
|
42
|
+
let sharedInfo = {
|
|
43
|
+
orderInfo: createOrderInfo(primaryOption),
|
|
44
|
+
unspecified: options.unspecified || 'ignore',
|
|
51
45
|
messages,
|
|
52
46
|
ruleName,
|
|
53
47
|
result,
|
|
@@ -55,7 +49,7 @@ function rule(expectation, options, context) {
|
|
|
55
49
|
shouldFix: false,
|
|
56
50
|
};
|
|
57
51
|
|
|
58
|
-
|
|
52
|
+
let processedParents = [];
|
|
59
53
|
|
|
60
54
|
// Check all rules and at-rules recursively
|
|
61
55
|
root.walk(function processRulesAndAtrules(input) {
|
|
@@ -64,28 +58,28 @@ function rule(expectation, options, context) {
|
|
|
64
58
|
return;
|
|
65
59
|
}
|
|
66
60
|
|
|
67
|
-
|
|
61
|
+
let node = getContainingNode(input);
|
|
68
62
|
|
|
69
|
-
// Avoid warnings duplication, caused by interfering in `root.walk()` algorigthm with `
|
|
63
|
+
// Avoid warnings duplication, caused by interfering in `root.walk()` algorigthm with `getContainingNode()`
|
|
70
64
|
if (processedParents.includes(node)) {
|
|
71
65
|
return;
|
|
72
66
|
}
|
|
73
67
|
|
|
74
68
|
processedParents.push(node);
|
|
75
69
|
|
|
76
|
-
if (
|
|
70
|
+
if (isRuleWithNodes(node)) {
|
|
77
71
|
checkNode(node, sharedInfo);
|
|
78
72
|
}
|
|
79
73
|
});
|
|
80
74
|
|
|
81
75
|
if (sharedInfo.shouldFix) {
|
|
82
|
-
postcssSorting({ order:
|
|
76
|
+
postcssSorting({ order: primaryOption })(root);
|
|
83
77
|
}
|
|
84
78
|
};
|
|
85
79
|
}
|
|
86
80
|
|
|
81
|
+
rule.ruleName = ruleName;
|
|
82
|
+
rule.messages = messages;
|
|
87
83
|
rule.primaryOptionArray = true;
|
|
88
84
|
|
|
89
85
|
module.exports = rule;
|
|
90
|
-
module.exports.ruleName = ruleName;
|
|
91
|
-
module.exports.messages = messages;
|
|
@@ -11,18 +11,15 @@ module.exports = function validatePrimaryOption(actualOptions) {
|
|
|
11
11
|
if (
|
|
12
12
|
!actualOptions.every(item => {
|
|
13
13
|
if (_.isString(item)) {
|
|
14
|
-
return
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
],
|
|
24
|
-
item
|
|
25
|
-
);
|
|
14
|
+
return [
|
|
15
|
+
'custom-properties',
|
|
16
|
+
'dollar-variables',
|
|
17
|
+
'at-variables',
|
|
18
|
+
'declarations',
|
|
19
|
+
'rules',
|
|
20
|
+
'at-rules',
|
|
21
|
+
'less-mixins',
|
|
22
|
+
].includes(item);
|
|
26
23
|
}
|
|
27
24
|
|
|
28
25
|
return _.isPlainObject(item) && !_.isUndefined(item.type);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
let stylelint = require('stylelint');
|
|
2
|
+
let _ = require('lodash');
|
|
3
|
+
let postcss = require('postcss');
|
|
4
|
+
let checkAlphabeticalOrder = require('../checkAlphabeticalOrder');
|
|
5
|
+
let { isStandardSyntaxProperty, isCustomProperty } = require('../../utils');
|
|
6
|
+
|
|
7
|
+
module.exports = function checkNode(node, result, ruleName, messages) {
|
|
8
|
+
let allPropData = [];
|
|
9
|
+
|
|
10
|
+
node.each(function processEveryNode(child) {
|
|
11
|
+
if (child.type !== 'decl') {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let { prop } = child;
|
|
16
|
+
|
|
17
|
+
if (!isStandardSyntaxProperty(prop)) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (isCustomProperty(prop)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let unprefixedPropName = postcss.vendor.unprefixed(prop);
|
|
26
|
+
|
|
27
|
+
// Hack to allow -moz-osx-font-smoothing to be understood
|
|
28
|
+
// just like -webkit-font-smoothing
|
|
29
|
+
if (unprefixedPropName.startsWith('osx-')) {
|
|
30
|
+
unprefixedPropName = unprefixedPropName.slice(4);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let propData = {
|
|
34
|
+
name: prop,
|
|
35
|
+
unprefixedName: unprefixedPropName,
|
|
36
|
+
index: allPropData.length,
|
|
37
|
+
node: child,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
let previousPropData = _.last(allPropData);
|
|
41
|
+
|
|
42
|
+
allPropData.push(propData);
|
|
43
|
+
|
|
44
|
+
// Skip first decl
|
|
45
|
+
if (!previousPropData) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let isCorrectOrder = checkAlphabeticalOrder(previousPropData, propData);
|
|
50
|
+
|
|
51
|
+
if (isCorrectOrder) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
stylelint.utils.report({
|
|
56
|
+
message: messages.expected(propData.name, previousPropData.name),
|
|
57
|
+
node: child,
|
|
58
|
+
result,
|
|
59
|
+
ruleName,
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
};
|
|
@@ -1,31 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const checkAlphabeticalOrder = require('../checkAlphabeticalOrder');
|
|
6
|
-
const utils = require('../../utils');
|
|
1
|
+
let stylelint = require('stylelint');
|
|
2
|
+
let postcssSorting = require('postcss-sorting');
|
|
3
|
+
let { namespace, getContainingNode, isRuleWithNodes } = require('../../utils');
|
|
4
|
+
let checkNode = require('./checkNode');
|
|
7
5
|
|
|
8
|
-
|
|
6
|
+
let ruleName = namespace('properties-alphabetical-order');
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
let messages = stylelint.utils.ruleMessages(ruleName, {
|
|
11
9
|
expected: (first, second) => `Expected ${first} to come before ${second}`,
|
|
12
10
|
});
|
|
13
11
|
|
|
14
|
-
function rule(actual, options, context) {
|
|
15
|
-
context = context || {};
|
|
16
|
-
|
|
12
|
+
function rule(actual, options = {}, context = {}) {
|
|
17
13
|
return function(root, result) {
|
|
18
|
-
|
|
14
|
+
let validOptions = stylelint.utils.validateOptions(
|
|
19
15
|
result,
|
|
20
16
|
ruleName,
|
|
21
17
|
{
|
|
22
18
|
actual,
|
|
23
|
-
possible:
|
|
19
|
+
possible: Boolean,
|
|
24
20
|
},
|
|
25
21
|
{
|
|
26
22
|
actual: options,
|
|
27
23
|
possible: {
|
|
28
|
-
disableFix:
|
|
24
|
+
disableFix: Boolean,
|
|
29
25
|
},
|
|
30
26
|
optional: true,
|
|
31
27
|
}
|
|
@@ -35,7 +31,7 @@ function rule(actual, options, context) {
|
|
|
35
31
|
return;
|
|
36
32
|
}
|
|
37
33
|
|
|
38
|
-
|
|
34
|
+
let disableFix = options.disableFix || false;
|
|
39
35
|
|
|
40
36
|
if (context.fix && !disableFix) {
|
|
41
37
|
postcssSorting({ 'properties-order': 'alphabetical' })(root);
|
|
@@ -43,82 +39,26 @@ function rule(actual, options, context) {
|
|
|
43
39
|
return;
|
|
44
40
|
}
|
|
45
41
|
|
|
46
|
-
|
|
42
|
+
let processedParents = [];
|
|
47
43
|
|
|
48
44
|
root.walk(function processRulesAndAtrules(input) {
|
|
49
|
-
|
|
45
|
+
let node = getContainingNode(input);
|
|
50
46
|
|
|
51
|
-
// Avoid warnings duplication, caused by interfering in `root.walk()` algorigthm with `
|
|
47
|
+
// Avoid warnings duplication, caused by interfering in `root.walk()` algorigthm with `getContainingNode()`
|
|
52
48
|
if (processedParents.includes(node)) {
|
|
53
49
|
return;
|
|
54
50
|
}
|
|
55
51
|
|
|
56
52
|
processedParents.push(node);
|
|
57
53
|
|
|
58
|
-
if (
|
|
59
|
-
checkNode(node, result);
|
|
54
|
+
if (isRuleWithNodes(node)) {
|
|
55
|
+
checkNode(node, result, ruleName, messages);
|
|
60
56
|
}
|
|
61
57
|
});
|
|
62
58
|
};
|
|
63
59
|
}
|
|
64
60
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
module.exports.messages = messages;
|
|
68
|
-
|
|
69
|
-
function checkNode(node, result) {
|
|
70
|
-
const allPropData = [];
|
|
71
|
-
|
|
72
|
-
node.each(function processEveryNode(child) {
|
|
73
|
-
if (child.type !== 'decl') {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const { prop } = child;
|
|
78
|
-
|
|
79
|
-
if (!utils.isStandardSyntaxProperty(prop)) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (utils.isCustomProperty(prop)) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
let unprefixedPropName = postcss.vendor.unprefixed(prop);
|
|
88
|
-
|
|
89
|
-
// Hack to allow -moz-osx-font-smoothing to be understood
|
|
90
|
-
// just like -webkit-font-smoothing
|
|
91
|
-
if (unprefixedPropName.indexOf('osx-') === 0) {
|
|
92
|
-
unprefixedPropName = unprefixedPropName.slice(4);
|
|
93
|
-
}
|
|
61
|
+
rule.ruleName = ruleName;
|
|
62
|
+
rule.messages = messages;
|
|
94
63
|
|
|
95
|
-
|
|
96
|
-
name: prop,
|
|
97
|
-
unprefixedName: unprefixedPropName,
|
|
98
|
-
index: allPropData.length,
|
|
99
|
-
node: child,
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const previousPropData = _.last(allPropData);
|
|
103
|
-
|
|
104
|
-
allPropData.push(propData);
|
|
105
|
-
|
|
106
|
-
// Skip first decl
|
|
107
|
-
if (!previousPropData) {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const isCorrectOrder = checkAlphabeticalOrder(previousPropData, propData);
|
|
112
|
-
|
|
113
|
-
if (isCorrectOrder) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
stylelint.utils.report({
|
|
118
|
-
message: messages.expected(propData.name, previousPropData.name),
|
|
119
|
-
node: child,
|
|
120
|
-
result,
|
|
121
|
-
ruleName,
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
}
|
|
64
|
+
module.exports = rule;
|
|
@@ -35,7 +35,14 @@ Within an order array, you can include
|
|
|
35
35
|
* group objects with these properties:
|
|
36
36
|
* `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.
|
|
37
37
|
* `properties (array of strings)`: The properties in this group.
|
|
38
|
-
* `emptyLineBefore ("always"|"never"|"threshold")`: 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.
|
|
38
|
+
* `emptyLineBefore ("always"|"never"|"threshold")`: 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.
|
|
39
|
+
|
|
40
|
+
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.
|
|
41
|
+
|
|
42
|
+
If `emptyLineBefore` specified, regardless of it's value, the first property in a rule would be forced to not have an empty line before it.
|
|
43
|
+
|
|
44
|
+
For `threshold`, refer to the [`emptyLineMinimumPropertyThreshold` documentation](#emptylineminimumpropertythreshold-number).
|
|
45
|
+
|
|
39
46
|
* `noEmptyLineBetween`: If `true`, properties within group should not have empty lines between them.
|
|
40
47
|
* `groupName`: An optional name for the group. This will be used in error messages.
|
|
41
48
|
|
|
@@ -600,11 +607,13 @@ a {
|
|
|
600
607
|
|
|
601
608
|
Default behavior does not enforce the presence of an empty line before an unspecified block of properties.
|
|
602
609
|
|
|
603
|
-
If `"always"`, the unspecified group must be separated from other properties by an empty newline.
|
|
610
|
+
If `"always"`, the unspecified group must be separated from other properties by an empty newline.
|
|
604
611
|
If `"never"`, the unspecified group must have no empty lines separating it from other properties.
|
|
605
612
|
|
|
606
613
|
For `"threshold"`, see the [`emptyLineMinimumPropertyThreshold` documentation](#emptylineminimumpropertythreshold-number) for more information.
|
|
607
614
|
|
|
615
|
+
If `emptyLineBeforeUnspecified` specified, regardless of it's value, if the first property in a rule is target of this option, that property would be forced to not have an empty line before it.
|
|
616
|
+
|
|
608
617
|
Given:
|
|
609
618
|
|
|
610
619
|
```js
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
let stylelint = require('stylelint');
|
|
2
|
+
let _ = require('lodash');
|
|
3
|
+
let addEmptyLineBefore = require('./addEmptyLineBefore');
|
|
4
|
+
let hasEmptyLineBefore = require('./hasEmptyLineBefore');
|
|
5
|
+
let removeEmptyLinesBefore = require('./removeEmptyLinesBefore');
|
|
6
6
|
|
|
7
7
|
module.exports = function checkEmptyLineBefore(
|
|
8
8
|
firstPropData,
|
|
@@ -10,45 +10,40 @@ module.exports = function checkEmptyLineBefore(
|
|
|
10
10
|
sharedInfo,
|
|
11
11
|
propsCount
|
|
12
12
|
) {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
let firstPropIsSpecified = Boolean(firstPropData.orderData);
|
|
14
|
+
let secondPropIsSpecified = Boolean(secondPropData.orderData);
|
|
15
15
|
|
|
16
16
|
// Check newlines between groups
|
|
17
|
-
|
|
17
|
+
let firstPropGroup = firstPropIsSpecified
|
|
18
18
|
? firstPropData.orderData.separatedGroup
|
|
19
19
|
: sharedInfo.lastKnownSeparatedGroup;
|
|
20
|
-
|
|
20
|
+
let secondPropGroup = secondPropIsSpecified
|
|
21
21
|
? secondPropData.orderData.separatedGroup
|
|
22
22
|
: sharedInfo.lastKnownSeparatedGroup;
|
|
23
23
|
|
|
24
|
-
sharedInfo.lastKnownSeparatedGroup =
|
|
24
|
+
sharedInfo.lastKnownSeparatedGroup = secondPropGroup;
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const startOfUnspecifiedGroup = !firstPropIsUnspecified && secondPropIsUnspecified;
|
|
26
|
+
let startOfSpecifiedGroup = secondPropIsSpecified && firstPropGroup !== secondPropGroup;
|
|
27
|
+
let startOfUnspecifiedGroup = firstPropIsSpecified && !secondPropIsSpecified;
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
const belowEmptyLineThreshold = propsCount < sharedInfo.emptyLineMinimumPropertyThreshold;
|
|
32
|
-
|
|
33
|
-
if (betweenGroupsInSpecified || startOfUnspecifiedGroup) {
|
|
29
|
+
if (startOfSpecifiedGroup || startOfUnspecifiedGroup) {
|
|
34
30
|
// Get an array of just the property groups, remove any solo properties
|
|
35
|
-
|
|
31
|
+
let groups = _.reject(sharedInfo.primaryOption, _.isString);
|
|
32
|
+
|
|
33
|
+
let emptyLineBefore = _.get(groups[secondPropGroup - 2], 'emptyLineBefore');
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
_.get(groups[secondPropSeparatedGroup - 2], 'emptyLineBefore')
|
|
41
|
-
: sharedInfo.emptyLineBeforeUnspecified;
|
|
35
|
+
if (startOfUnspecifiedGroup) {
|
|
36
|
+
emptyLineBefore = sharedInfo.emptyLineBeforeUnspecified;
|
|
37
|
+
}
|
|
42
38
|
|
|
43
39
|
// Threshold logic
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
belowEmptyLineThreshold && emptyLineBefore === 'threshold';
|
|
40
|
+
let belowEmptyLineThreshold = propsCount < sharedInfo.emptyLineMinimumPropertyThreshold;
|
|
41
|
+
let emptyLineThresholdInsertLines = emptyLineBefore === 'threshold' && !belowEmptyLineThreshold;
|
|
42
|
+
let emptyLineThresholdRemoveLines = emptyLineBefore === 'threshold' && belowEmptyLineThreshold;
|
|
48
43
|
|
|
49
44
|
if (
|
|
50
|
-
|
|
51
|
-
(
|
|
45
|
+
(emptyLineBefore === 'always' || emptyLineThresholdInsertLines) &&
|
|
46
|
+
!hasEmptyLineBefore(secondPropData.node)
|
|
52
47
|
) {
|
|
53
48
|
if (sharedInfo.isFixEnabled) {
|
|
54
49
|
addEmptyLineBefore(secondPropData.node, sharedInfo.context.newline);
|
|
@@ -61,8 +56,8 @@ module.exports = function checkEmptyLineBefore(
|
|
|
61
56
|
});
|
|
62
57
|
}
|
|
63
58
|
} else if (
|
|
64
|
-
|
|
65
|
-
(
|
|
59
|
+
(emptyLineBefore === 'never' || emptyLineThresholdRemoveLines) &&
|
|
60
|
+
hasEmptyLineBefore(secondPropData.node)
|
|
66
61
|
) {
|
|
67
62
|
if (sharedInfo.isFixEnabled) {
|
|
68
63
|
removeEmptyLinesBefore(secondPropData.node, sharedInfo.context.newline);
|
|
@@ -79,13 +74,14 @@ module.exports = function checkEmptyLineBefore(
|
|
|
79
74
|
|
|
80
75
|
// Check newlines between properties inside a group
|
|
81
76
|
if (
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
firstPropIsSpecified &&
|
|
78
|
+
secondPropIsSpecified &&
|
|
84
79
|
firstPropData.orderData.groupPosition === secondPropData.orderData.groupPosition
|
|
85
80
|
) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
81
|
+
if (
|
|
82
|
+
secondPropData.orderData.noEmptyLineBeforeInsideGroup &&
|
|
83
|
+
hasEmptyLineBefore(secondPropData.node)
|
|
84
|
+
) {
|
|
89
85
|
if (sharedInfo.isFixEnabled) {
|
|
90
86
|
removeEmptyLinesBefore(secondPropData.node, sharedInfo.context.newline);
|
|
91
87
|
} else {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const stylelint = require('stylelint');
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
const hasEmptyLineBefore = require('./hasEmptyLineBefore');
|
|
4
|
+
const removeEmptyLinesBefore = require('./removeEmptyLinesBefore');
|
|
5
|
+
|
|
6
|
+
module.exports = function checkEmptyLineBeforeFirstProp(propData, sharedInfo) {
|
|
7
|
+
let emptyLineBefore = false;
|
|
8
|
+
|
|
9
|
+
if (propData.orderData) {
|
|
10
|
+
// Get an array of just the property groups, remove any solo properties
|
|
11
|
+
let groups = _.reject(sharedInfo.primaryOption, _.isString);
|
|
12
|
+
|
|
13
|
+
emptyLineBefore = _.get(groups[propData.orderData.separatedGroup - 2], 'emptyLineBefore');
|
|
14
|
+
} else if (sharedInfo.emptyLineBeforeUnspecified) {
|
|
15
|
+
emptyLineBefore = true;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (emptyLineBefore && hasEmptyLineBefore(propData.node)) {
|
|
19
|
+
if (sharedInfo.isFixEnabled) {
|
|
20
|
+
removeEmptyLinesBefore(propData.node, sharedInfo.context.newline);
|
|
21
|
+
} else {
|
|
22
|
+
stylelint.utils.report({
|
|
23
|
+
message: sharedInfo.messages.rejectedEmptyLineBefore(propData.name),
|
|
24
|
+
node: propData.node,
|
|
25
|
+
result: sharedInfo.result,
|
|
26
|
+
ruleName: sharedInfo.ruleName,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
const stylelint = require('stylelint');
|
|
2
2
|
const postcss = require('postcss');
|
|
3
3
|
const postcssSorting = require('postcss-sorting');
|
|
4
|
-
const
|
|
4
|
+
const { isProperty } = require('../../utils');
|
|
5
5
|
const checkEmptyLineBefore = require('./checkEmptyLineBefore');
|
|
6
|
+
const checkEmptyLineBeforeFirstProp = require('./checkEmptyLineBeforeFirstProp');
|
|
6
7
|
const checkOrder = require('./checkOrder');
|
|
7
8
|
const getNodeData = require('./getNodeData');
|
|
8
9
|
const createFlatOrder = require('./createFlatOrder');
|
|
9
10
|
|
|
10
11
|
module.exports = function checkNode(node, sharedInfo, originalNode) {
|
|
11
12
|
// First, check order
|
|
12
|
-
|
|
13
|
-
const propsCount = node.nodes.filter(item => utils.isProperty(item)).length;
|
|
13
|
+
let allPropData = getAllPropData(node);
|
|
14
14
|
|
|
15
15
|
if (!sharedInfo.isFixEnabled) {
|
|
16
16
|
allPropData.forEach(checkEveryPropForOrder);
|
|
@@ -19,6 +19,7 @@ module.exports = function checkNode(node, sharedInfo, originalNode) {
|
|
|
19
19
|
if (sharedInfo.isFixEnabled) {
|
|
20
20
|
let shouldFixOrder = false;
|
|
21
21
|
|
|
22
|
+
// Check if there order violation to avoid running re-ordering unnecessery
|
|
22
23
|
allPropData.forEach(function checkEveryPropForOrder2(propData, index) {
|
|
23
24
|
// Skip first decl
|
|
24
25
|
if (index === 0) {
|
|
@@ -30,9 +31,9 @@ module.exports = function checkNode(node, sharedInfo, originalNode) {
|
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
let previousPropData = allPropData[index - 1];
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
let checkedOrder = checkOrder({
|
|
36
37
|
firstPropData: previousPropData,
|
|
37
38
|
secondPropData: propData,
|
|
38
39
|
unspecified: sharedInfo.unspecified,
|
|
@@ -45,19 +46,19 @@ module.exports = function checkNode(node, sharedInfo, originalNode) {
|
|
|
45
46
|
});
|
|
46
47
|
|
|
47
48
|
if (shouldFixOrder) {
|
|
48
|
-
|
|
49
|
-
'properties-order': createFlatOrder(sharedInfo.
|
|
49
|
+
let sortingOptions = {
|
|
50
|
+
'properties-order': createFlatOrder(sharedInfo.primaryOption),
|
|
50
51
|
'unspecified-properties-position':
|
|
51
52
|
sharedInfo.unspecified === 'ignore' ? 'bottom' : sharedInfo.unspecified,
|
|
52
53
|
};
|
|
53
54
|
|
|
54
55
|
// creating PostCSS Root node with current node as a child,
|
|
55
56
|
// so PostCSS Sorting can process it
|
|
56
|
-
|
|
57
|
+
let tempRoot = postcss.root({ nodes: [originalNode] });
|
|
57
58
|
|
|
58
59
|
postcssSorting(sortingOptions)(tempRoot);
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
let allPropData2 = getAllPropData(node);
|
|
61
62
|
|
|
62
63
|
allPropData2.forEach(checkEveryPropForOrder);
|
|
63
64
|
}
|
|
@@ -66,7 +67,8 @@ module.exports = function checkNode(node, sharedInfo, originalNode) {
|
|
|
66
67
|
// Second, check emptyLineBefore
|
|
67
68
|
sharedInfo.lastKnownSeparatedGroup = 1;
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
let propsCount = node.nodes.filter(item => isProperty(item)).length;
|
|
71
|
+
let allNodesData = node.nodes.map(function collectDataForEveryNode(child) {
|
|
70
72
|
return getNodeData(child, sharedInfo.expectedOrder);
|
|
71
73
|
});
|
|
72
74
|
|
|
@@ -77,7 +79,7 @@ module.exports = function checkNode(node, sharedInfo, originalNode) {
|
|
|
77
79
|
if (
|
|
78
80
|
previousNodeData &&
|
|
79
81
|
previousNodeData.node.type === 'comment' &&
|
|
80
|
-
previousNodeData.node.raw('before').
|
|
82
|
+
!previousNodeData.node.raw('before').includes('\n')
|
|
81
83
|
) {
|
|
82
84
|
previousNodeData = allNodesData[index - 2];
|
|
83
85
|
}
|
|
@@ -87,14 +89,19 @@ module.exports = function checkNode(node, sharedInfo, originalNode) {
|
|
|
87
89
|
return;
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
//
|
|
91
|
-
if (!
|
|
92
|
+
// Nodes should be standard declarations
|
|
93
|
+
if (!isProperty(previousNodeData.node) || !isProperty(nodeData.node)) {
|
|
92
94
|
return;
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
checkEmptyLineBefore(previousNodeData, nodeData, sharedInfo, propsCount);
|
|
96
98
|
});
|
|
97
99
|
|
|
100
|
+
// Check if empty line before first prop should be removed
|
|
101
|
+
if (isProperty(allNodesData[0].node)) {
|
|
102
|
+
checkEmptyLineBeforeFirstProp(allNodesData[0], sharedInfo);
|
|
103
|
+
}
|
|
104
|
+
|
|
98
105
|
function checkEveryPropForOrder(propData, index, listOfProps) {
|
|
99
106
|
// Skip first decl
|
|
100
107
|
if (index === 0) {
|
|
@@ -128,7 +135,7 @@ module.exports = function checkNode(node, sharedInfo, originalNode) {
|
|
|
128
135
|
|
|
129
136
|
function getAllPropData(inputNode) {
|
|
130
137
|
return inputNode.nodes
|
|
131
|
-
.filter(item =>
|
|
138
|
+
.filter(item => isProperty(item))
|
|
132
139
|
.map(item => getNodeData(item, sharedInfo.expectedOrder));
|
|
133
140
|
}
|
|
134
141
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
|
|
3
|
-
module.exports = function
|
|
4
|
-
|
|
3
|
+
module.exports = function createOrderInfo(input) {
|
|
4
|
+
let order = {};
|
|
5
5
|
let expectedPosition = 0;
|
|
6
6
|
let separatedGroup = 1;
|
|
7
7
|
let groupPosition = -1;
|
|
@@ -38,7 +38,7 @@ module.exports = function createExpectedOrder(input) {
|
|
|
38
38
|
separatedGroup += 1;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
if (item.order
|
|
41
|
+
if (item.order === 'flexible') {
|
|
42
42
|
expectedPosition += 1;
|
|
43
43
|
groupPosition += 1;
|
|
44
44
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
const postcss = require('postcss');
|
|
2
|
-
const
|
|
2
|
+
const { isProperty } = require('../../utils');
|
|
3
3
|
|
|
4
4
|
module.exports = function getNodeData(node, expectedOrder) {
|
|
5
|
-
|
|
5
|
+
let nodeData = {
|
|
6
6
|
node,
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
if (
|
|
10
|
-
|
|
9
|
+
if (isProperty(node)) {
|
|
10
|
+
let { prop } = node;
|
|
11
11
|
let unprefixedPropName = postcss.vendor.unprefixed(prop);
|
|
12
12
|
|
|
13
13
|
// Hack to allow -moz-osx-font-smoothing to be understood
|
|
14
14
|
// just like -webkit-font-smoothing
|
|
15
|
-
if (unprefixedPropName.
|
|
15
|
+
if (unprefixedPropName.startsWith('osx-')) {
|
|
16
16
|
unprefixedPropName = unprefixedPropName.slice(4);
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
const stylelint = require('stylelint');
|
|
2
2
|
const _ = require('lodash');
|
|
3
|
-
const
|
|
3
|
+
const { namespace, getContainingNode, isRuleWithNodes } = require('../../utils');
|
|
4
4
|
const checkNode = require('./checkNode');
|
|
5
|
-
const
|
|
5
|
+
const createOrderInfo = require('./createOrderInfo');
|
|
6
6
|
const validatePrimaryOption = require('./validatePrimaryOption');
|
|
7
7
|
|
|
8
|
-
const ruleName =
|
|
8
|
+
const ruleName = namespace('properties-order');
|
|
9
9
|
|
|
10
10
|
const messages = stylelint.utils.ruleMessages(ruleName, {
|
|
11
11
|
expected: (first, second, groupName) =>
|
|
@@ -14,13 +14,13 @@ const messages = stylelint.utils.ruleMessages(ruleName, {
|
|
|
14
14
|
rejectedEmptyLineBefore: property => `Unexpected empty line before property "${property}"`,
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
function rule(primaryOption, options = {}, context = {}) {
|
|
18
18
|
return function(root, result) {
|
|
19
|
-
|
|
19
|
+
let validOptions = stylelint.utils.validateOptions(
|
|
20
20
|
result,
|
|
21
21
|
ruleName,
|
|
22
22
|
{
|
|
23
|
-
actual:
|
|
23
|
+
actual: primaryOption,
|
|
24
24
|
possible: validatePrimaryOption,
|
|
25
25
|
},
|
|
26
26
|
{
|
|
@@ -39,54 +39,43 @@ const rule = function(expectation, options, context = {}) {
|
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
const unspecified = _.get(options, 'unspecified', 'ignore');
|
|
44
|
-
const emptyLineBeforeUnspecified = _.get(options, 'emptyLineBeforeUnspecified', '');
|
|
45
|
-
const emptyLineMinimumPropertyThreshold = _.get(
|
|
46
|
-
options,
|
|
47
|
-
'emptyLineMinimumPropertyThreshold',
|
|
48
|
-
0
|
|
49
|
-
);
|
|
50
|
-
const disableFix = _.get(options, 'disableFix', false);
|
|
51
|
-
const isFixEnabled = context.fix && !disableFix;
|
|
52
|
-
|
|
53
|
-
const expectedOrder = createExpectedOrder(expectation);
|
|
42
|
+
let disableFix = options.disableFix || false;
|
|
54
43
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
44
|
+
let sharedInfo = {
|
|
45
|
+
context,
|
|
46
|
+
emptyLineBeforeUnspecified: options.emptyLineBeforeUnspecified,
|
|
47
|
+
emptyLineMinimumPropertyThreshold: options.emptyLineMinimumPropertyThreshold || 0,
|
|
48
|
+
expectedOrder: createOrderInfo(primaryOption),
|
|
49
|
+
isFixEnabled: context.fix && !disableFix,
|
|
61
50
|
messages,
|
|
62
|
-
|
|
51
|
+
primaryOption,
|
|
63
52
|
result,
|
|
64
|
-
|
|
65
|
-
|
|
53
|
+
ruleName,
|
|
54
|
+
unspecified: options.unspecified || 'ignore',
|
|
66
55
|
};
|
|
67
56
|
|
|
68
|
-
|
|
57
|
+
let processedParents = [];
|
|
69
58
|
|
|
70
59
|
// Check all rules and at-rules recursively
|
|
71
60
|
root.walk(function processRulesAndAtrules(input) {
|
|
72
|
-
|
|
61
|
+
let node = getContainingNode(input);
|
|
73
62
|
|
|
74
|
-
// Avoid warnings duplication, caused by interfering in `root.walk()` algorigthm with `
|
|
63
|
+
// Avoid warnings duplication, caused by interfering in `root.walk()` algorigthm with `getContainingNode()`
|
|
75
64
|
if (processedParents.includes(node)) {
|
|
76
65
|
return;
|
|
77
66
|
}
|
|
78
67
|
|
|
79
68
|
processedParents.push(node);
|
|
80
69
|
|
|
81
|
-
if (
|
|
70
|
+
if (isRuleWithNodes(node)) {
|
|
82
71
|
checkNode(node, sharedInfo, input);
|
|
83
72
|
}
|
|
84
73
|
});
|
|
85
74
|
};
|
|
86
|
-
}
|
|
75
|
+
}
|
|
87
76
|
|
|
88
77
|
rule.primaryOptionArray = true;
|
|
89
|
-
|
|
90
78
|
rule.ruleName = ruleName;
|
|
91
79
|
rule.messages = messages;
|
|
80
|
+
|
|
92
81
|
module.exports = rule;
|
|
@@ -42,7 +42,7 @@ module.exports = function validatePrimaryOption(actualOptions) {
|
|
|
42
42
|
return true;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
return
|
|
45
|
+
return ['always', 'never', 'threshold'].includes(item.emptyLineBefore);
|
|
46
46
|
})
|
|
47
47
|
) {
|
|
48
48
|
return false;
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
module.exports = function isStandardSyntaxProperty(property) {
|
|
9
9
|
// SCSS var (e.g. $var: x), list (e.g. $list: (x)) or map (e.g. $map: (key:value))
|
|
10
|
-
if (property
|
|
10
|
+
if (property.startsWith('$')) {
|
|
11
11
|
return false;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
// Less var (e.g. @var: x)
|
|
15
|
-
if (property
|
|
15
|
+
if (property.startsWith('@')) {
|
|
16
16
|
return false;
|
|
17
17
|
}
|
|
18
18
|
|