stylelint-order 0.5.0 → 0.8.1
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 +17 -0
- package/LICENSE.md +1 -1
- package/README.md +9 -1
- package/index.js +3 -5
- package/package.json +25 -9
- package/rules/.DS_Store +0 -0
- package/rules/checkAlphabeticalOrder.js +4 -1
- package/rules/index.js +1 -1
- package/rules/order/.DS_Store +0 -0
- package/rules/order/calcAtRulePatternPriority.js +11 -4
- package/rules/order/calcRulePatternPriority.js +0 -2
- package/rules/order/checkNode.js +7 -2
- package/rules/order/checkOrder.js +20 -7
- package/rules/order/createExpectedOrder.js +4 -19
- package/rules/order/getDescription.js +1 -3
- package/rules/order/getOrderData.js +3 -9
- package/rules/order/index.js +15 -11
- package/rules/order/validatePrimaryOption.js +46 -32
- package/rules/properties-alphabetical-order/.DS_Store +0 -0
- package/rules/properties-alphabetical-order/index.js +2 -4
- package/rules/properties-order/.DS_Store +0 -0
- package/rules/properties-order/README.md +0 -39
- package/rules/properties-order/addEmptyLineBefore.js +3 -5
- package/rules/properties-order/checkEmptyLineBefore.js +7 -12
- package/rules/properties-order/checkNode.js +65 -74
- package/rules/properties-order/checkOrder.js +38 -42
- package/rules/properties-order/createExpectedOrder.js +2 -4
- package/rules/properties-order/createFlatOrder.js +1 -3
- package/rules/properties-order/getNodeData.js +25 -0
- package/rules/properties-order/hasEmptyLineBefore.js +0 -2
- package/rules/properties-order/index.js +10 -34
- package/rules/properties-order/removeEmptyLinesBefore.js +0 -2
- package/rules/properties-order/validatePrimaryOption.js +27 -23
- package/utils/index.js +2 -0
- package/utils/isCustomProperty.js +1 -1
- package/utils/isProperty.js +9 -0
- package/utils/isRuleWithNodes.js +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
|
3
3
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 0.8.1
|
|
6
|
+
|
|
7
|
+
* Add `stylelint@9.0.0` as a peer dependency.
|
|
8
|
+
|
|
9
|
+
## 0.8.0
|
|
10
|
+
|
|
11
|
+
* Breaking change: Dropped Node.js 4 support. Use Node.js 6 or newer.
|
|
12
|
+
* Changed: `order` and `properties-order` will no longer autofix proactively. If there no violations would be reported with autofix disabled, then nothing will be changed with autofix enabled. Previously, there were changes to `flexible` properties order ([#49](https://github.com/hudochenkov/stylelint-order/issues/49)) or to the order of content within declaration blocks ([#51](https://github.com/hudochenkov/stylelint-order/issues/51)).
|
|
13
|
+
|
|
14
|
+
## 0.7.0
|
|
15
|
+
|
|
16
|
+
* Specified `stylelint` in `peerDependencies` rather in `dependencies`. Following [stylelint's plugin guide](https://github.com/stylelint/stylelint/blob/master/docs/developer-guide/plugins.md#peer-dependencies).
|
|
17
|
+
|
|
18
|
+
## 0.6.0
|
|
19
|
+
|
|
20
|
+
* Migrated to `stylelint@8.0.0`.
|
|
21
|
+
|
|
5
22
|
## 0.5.0
|
|
6
23
|
* Added autofixing for every rule! Please read docs before using this feature, because each rule has some caveats. stylelint 7.11+ is required for this feature.
|
|
7
24
|
* Removed SCSS nested properties support.
|
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright 2016–
|
|
3
|
+
Copyright 2016–present Aleks Hudochenkov <aleks@hudochenkov.com>
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
6
|
this software and associated documentation files (the "Software"), to deal in
|
package/README.md
CHANGED
|
@@ -4,8 +4,16 @@ A plugin pack of order related linting rules for [stylelint]. Every rule support
|
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
+
First, install [stylelint]:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm install stylelint --save-dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Then install plugin:
|
|
14
|
+
|
|
7
15
|
```
|
|
8
|
-
npm install stylelint-order
|
|
16
|
+
npm install stylelint-order --save-dev
|
|
9
17
|
```
|
|
10
18
|
|
|
11
19
|
## Usage
|
package/index.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const createPlugin = require('stylelint').createPlugin;
|
|
4
|
-
const namespace = require('./utils').namespace;
|
|
1
|
+
const { createPlugin } = require('stylelint');
|
|
2
|
+
const { namespace } = require('./utils');
|
|
5
3
|
const rules = require('./rules');
|
|
6
4
|
|
|
7
|
-
const rulesPlugins = Object.keys(rules).map(
|
|
5
|
+
const rulesPlugins = Object.keys(rules).map(ruleName => {
|
|
8
6
|
return createPlugin(namespace(ruleName), rules[ruleName]);
|
|
9
7
|
});
|
|
10
8
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stylelint-order",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "A collection of order related linting rules for stylelint.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"stylelint-plugin",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"homepage": "https://github.com/hudochenkov/stylelint-order",
|
|
19
19
|
"engines": {
|
|
20
|
-
"node": ">=
|
|
20
|
+
"node": ">=6"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"rules",
|
|
@@ -28,21 +28,37 @@
|
|
|
28
28
|
"main": "index.js",
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"lodash": "^4.17.4",
|
|
31
|
-
"postcss": "^6.0.
|
|
32
|
-
"postcss-sorting": "^3.
|
|
33
|
-
|
|
31
|
+
"postcss": "^6.0.14",
|
|
32
|
+
"postcss-sorting": "^3.1.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"stylelint": "^8.0.0 || ^9.0.0"
|
|
34
36
|
},
|
|
35
37
|
"devDependencies": {
|
|
36
|
-
"eslint": "
|
|
37
|
-
"eslint-config-hudochenkov": "
|
|
38
|
-
"
|
|
38
|
+
"eslint": "~4.12.1",
|
|
39
|
+
"eslint-config-hudochenkov": "~2.0.0",
|
|
40
|
+
"eslint-config-prettier": "~2.9.0",
|
|
41
|
+
"eslint-plugin-prettier": "~2.3.1",
|
|
42
|
+
"husky": "^0.14.3",
|
|
43
|
+
"jest": "^21.2.1",
|
|
44
|
+
"lint-staged": "^6.0.0",
|
|
45
|
+
"prettier": "~1.8.2",
|
|
46
|
+
"stylelint": "^8.3.1"
|
|
39
47
|
},
|
|
40
48
|
"scripts": {
|
|
49
|
+
"precommit": "lint-staged",
|
|
41
50
|
"pretest": "eslint .",
|
|
42
51
|
"test": "jest",
|
|
43
52
|
"jest": "jest",
|
|
44
53
|
"watch": "jest --watch",
|
|
45
|
-
"coverage": "jest --coverage"
|
|
54
|
+
"coverage": "jest --coverage",
|
|
55
|
+
"fix": "npm run pretest -- --fix"
|
|
56
|
+
},
|
|
57
|
+
"lint-staged": {
|
|
58
|
+
"*.js": [
|
|
59
|
+
"npm run fix",
|
|
60
|
+
"git add"
|
|
61
|
+
]
|
|
46
62
|
},
|
|
47
63
|
"jest": {
|
|
48
64
|
"setupFiles": [
|
package/rules/.DS_Store
ADDED
|
Binary file
|
|
@@ -4,7 +4,10 @@ module.exports = function checkAlphabeticalOrder(firstPropData, secondPropData)
|
|
|
4
4
|
// If unprefixed prop names are the same, compare the prefixed versions
|
|
5
5
|
if (firstPropData.unprefixedName === secondPropData.unprefixedName) {
|
|
6
6
|
// If first property has no prefix and second property has prefix
|
|
7
|
-
if (
|
|
7
|
+
if (
|
|
8
|
+
!postcss.vendor.prefix(firstPropData.name).length &&
|
|
9
|
+
postcss.vendor.prefix(secondPropData.name).length
|
|
10
|
+
) {
|
|
8
11
|
return false;
|
|
9
12
|
}
|
|
10
13
|
|
package/rules/index.js
CHANGED
|
Binary file
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
module.exports = function calcAtRulePatternPriority(pattern, node) {
|
|
4
2
|
// 0 — it pattern doesn't match
|
|
5
3
|
// 1 — pattern without `name` and `hasBlock`
|
|
@@ -30,7 +28,11 @@ module.exports = function calcAtRulePatternPriority(pattern, node) {
|
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
// doesn't have `name` and `hasBlock`
|
|
33
|
-
if (
|
|
31
|
+
if (
|
|
32
|
+
!pattern.hasOwnProperty('hasBlock') &&
|
|
33
|
+
!pattern.hasOwnProperty('name') &&
|
|
34
|
+
!pattern.hasOwnProperty('paremeter')
|
|
35
|
+
) {
|
|
34
36
|
priority = 1;
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -45,7 +47,12 @@ module.exports = function calcAtRulePatternPriority(pattern, node) {
|
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
// patter has `name`, `parameter`, and `hasBlock`, but it doesn't match all properties
|
|
48
|
-
if (
|
|
50
|
+
if (
|
|
51
|
+
pattern.hasOwnProperty('name') &&
|
|
52
|
+
pattern.hasOwnProperty('parameter') &&
|
|
53
|
+
pattern.hasOwnProperty('hasBlock') &&
|
|
54
|
+
priority < 30000
|
|
55
|
+
) {
|
|
49
56
|
priority = 0;
|
|
50
57
|
}
|
|
51
58
|
|
package/rules/order/checkNode.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const stylelint = require('stylelint');
|
|
4
2
|
const _ = require('lodash');
|
|
5
3
|
const checkOrder = require('./checkOrder');
|
|
@@ -46,6 +44,13 @@ module.exports = function checkNode(node, sharedInfo) {
|
|
|
46
44
|
return;
|
|
47
45
|
}
|
|
48
46
|
|
|
47
|
+
if (sharedInfo.isFixEnabled) {
|
|
48
|
+
sharedInfo.shouldFix = true;
|
|
49
|
+
|
|
50
|
+
// Don't go further, fix will be applied
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
stylelint.utils.report({
|
|
50
55
|
message: sharedInfo.messages.expected(nodeData.description, previousNodeData.description),
|
|
51
56
|
node: child,
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const stylelint = require('stylelint');
|
|
4
2
|
const _ = require('lodash');
|
|
5
3
|
|
|
@@ -15,14 +13,27 @@ module.exports = function checkOrder(firstNodeData, secondNodeData, allNodesData
|
|
|
15
13
|
if (firstNodeIsUnspecified && !secondNodeIsUnspecified) {
|
|
16
14
|
// If first node is unspecified, look for a specified node before it to
|
|
17
15
|
// compare to the current node
|
|
18
|
-
const priorSpecifiedNodeData = _.findLast(allNodesData.slice(0, -1),
|
|
16
|
+
const priorSpecifiedNodeData = _.findLast(allNodesData.slice(0, -1), d =>
|
|
17
|
+
Boolean(d.expectedPosition)
|
|
18
|
+
);
|
|
19
19
|
|
|
20
20
|
if (
|
|
21
|
-
priorSpecifiedNodeData &&
|
|
22
|
-
|
|
21
|
+
priorSpecifiedNodeData &&
|
|
22
|
+
priorSpecifiedNodeData.expectedPosition &&
|
|
23
|
+
priorSpecifiedNodeData.expectedPosition > secondNodeData.expectedPosition
|
|
23
24
|
) {
|
|
25
|
+
if (sharedInfo.isFixEnabled) {
|
|
26
|
+
sharedInfo.shouldFix = true;
|
|
27
|
+
|
|
28
|
+
// Don't go further, fix will be applied
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
stylelint.utils.report({
|
|
25
|
-
message: sharedInfo.messages.expected(
|
|
33
|
+
message: sharedInfo.messages.expected(
|
|
34
|
+
secondNodeData.description,
|
|
35
|
+
priorSpecifiedNodeData.description
|
|
36
|
+
),
|
|
26
37
|
node: secondNodeData.node,
|
|
27
38
|
result: sharedInfo.result,
|
|
28
39
|
ruleName: sharedInfo.ruleName,
|
|
@@ -36,7 +47,7 @@ module.exports = function checkOrder(firstNodeData, secondNodeData, allNodesData
|
|
|
36
47
|
return true;
|
|
37
48
|
}
|
|
38
49
|
|
|
39
|
-
const unspecified = sharedInfo
|
|
50
|
+
const { unspecified } = sharedInfo;
|
|
40
51
|
|
|
41
52
|
if (unspecified === 'ignore' && (firstNodeIsUnspecified || secondNodeIsUnspecified)) {
|
|
42
53
|
return true;
|
|
@@ -45,6 +56,7 @@ module.exports = function checkOrder(firstNodeData, secondNodeData, allNodesData
|
|
|
45
56
|
if (unspecified === 'top' && firstNodeIsUnspecified) {
|
|
46
57
|
return true;
|
|
47
58
|
}
|
|
59
|
+
|
|
48
60
|
if (unspecified === 'top' && secondNodeIsUnspecified) {
|
|
49
61
|
return false;
|
|
50
62
|
}
|
|
@@ -52,6 +64,7 @@ module.exports = function checkOrder(firstNodeData, secondNodeData, allNodesData
|
|
|
52
64
|
if (unspecified === 'bottom' && secondNodeIsUnspecified) {
|
|
53
65
|
return true;
|
|
54
66
|
}
|
|
67
|
+
|
|
55
68
|
if (unspecified === 'bottom' && firstNodeIsUnspecified) {
|
|
56
69
|
return false;
|
|
57
70
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const _ = require('lodash');
|
|
4
2
|
const getDescription = require('./getDescription');
|
|
5
3
|
|
|
@@ -7,27 +5,17 @@ module.exports = function createExpectedOrder(input) {
|
|
|
7
5
|
const order = {};
|
|
8
6
|
let expectedPosition = 0;
|
|
9
7
|
|
|
10
|
-
input.forEach(
|
|
8
|
+
input.forEach(item => {
|
|
11
9
|
expectedPosition += 1;
|
|
12
10
|
|
|
13
|
-
if (
|
|
14
|
-
(
|
|
15
|
-
_.isString(item)
|
|
16
|
-
&& item !== 'at-rules'
|
|
17
|
-
&& item !== 'rules'
|
|
18
|
-
)
|
|
19
|
-
|| item === 'less-mixins'
|
|
20
|
-
) {
|
|
11
|
+
if ((_.isString(item) && item !== 'at-rules' && item !== 'rules') || item === 'less-mixins') {
|
|
21
12
|
order[item] = {
|
|
22
13
|
expectedPosition,
|
|
23
14
|
description: getDescription(item),
|
|
24
15
|
};
|
|
25
16
|
}
|
|
26
17
|
|
|
27
|
-
if (
|
|
28
|
-
item === 'rules'
|
|
29
|
-
|| item.type === 'rule'
|
|
30
|
-
) {
|
|
18
|
+
if (item === 'rules' || item.type === 'rule') {
|
|
31
19
|
// Convert 'rules' into extended pattern
|
|
32
20
|
if (item === 'rules') {
|
|
33
21
|
item = {
|
|
@@ -56,10 +44,7 @@ module.exports = function createExpectedOrder(input) {
|
|
|
56
44
|
order[item.type].push(nodeData);
|
|
57
45
|
}
|
|
58
46
|
|
|
59
|
-
if (
|
|
60
|
-
item === 'at-rules'
|
|
61
|
-
|| item.type === 'at-rule'
|
|
62
|
-
) {
|
|
47
|
+
if (item === 'at-rules' || item.type === 'at-rule') {
|
|
63
48
|
// Convert 'at-rules' into extended pattern
|
|
64
49
|
if (item === 'at-rules') {
|
|
65
50
|
item = {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const _ = require('lodash');
|
|
4
2
|
|
|
5
3
|
module.exports = function getDescription(item) {
|
|
@@ -8,7 +6,7 @@ module.exports = function getDescription(item) {
|
|
|
8
6
|
'dollar-variables': '$-variable',
|
|
9
7
|
'at-variables': '@-variable',
|
|
10
8
|
'less-mixins': 'Less mixin',
|
|
11
|
-
|
|
9
|
+
declarations: 'declaration',
|
|
12
10
|
};
|
|
13
11
|
|
|
14
12
|
if (_.isPlainObject(item)) {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const utils = require('../../utils');
|
|
4
2
|
const calcAtRulePatternPriority = require('./calcAtRulePatternPriority');
|
|
5
3
|
const calcRulePatternPriority = require('./calcRulePatternPriority');
|
|
@@ -18,11 +16,7 @@ module.exports = function getOrderData(expectedOrder, node) {
|
|
|
18
16
|
} else if (utils.isStandardSyntaxProperty(node.prop)) {
|
|
19
17
|
nodeType = 'declarations';
|
|
20
18
|
}
|
|
21
|
-
} else if (
|
|
22
|
-
node.type === 'rule'
|
|
23
|
-
&& node.ruleWithoutBody
|
|
24
|
-
&& !node.extendRule
|
|
25
|
-
) {
|
|
19
|
+
} else if (node.type === 'rule' && node.empty && !node.extend) {
|
|
26
20
|
nodeType = 'less-mixins';
|
|
27
21
|
} else if (node.type === 'rule') {
|
|
28
22
|
nodeType = {
|
|
@@ -37,7 +31,7 @@ module.exports = function getOrderData(expectedOrder, node) {
|
|
|
37
31
|
let prioritizedPattern;
|
|
38
32
|
let max = 0;
|
|
39
33
|
|
|
40
|
-
rules.forEach(function
|
|
34
|
+
rules.forEach(function(pattern) {
|
|
41
35
|
const priority = calcRulePatternPriority(pattern, nodeType);
|
|
42
36
|
|
|
43
37
|
if (priority > max) {
|
|
@@ -72,7 +66,7 @@ module.exports = function getOrderData(expectedOrder, node) {
|
|
|
72
66
|
let prioritizedPattern;
|
|
73
67
|
let max = 0;
|
|
74
68
|
|
|
75
|
-
atRules.forEach(function
|
|
69
|
+
atRules.forEach(function(pattern) {
|
|
76
70
|
const priority = calcAtRulePatternPriority(pattern, nodeType);
|
|
77
71
|
|
|
78
72
|
if (priority > max) {
|
package/rules/order/index.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const stylelint = require('stylelint');
|
|
4
2
|
const _ = require('lodash');
|
|
5
3
|
const postcssSorting = require('postcss-sorting');
|
|
@@ -17,7 +15,7 @@ const messages = stylelint.utils.ruleMessages(ruleName, {
|
|
|
17
15
|
function rule(expectation, options, context) {
|
|
18
16
|
context = context || {};
|
|
19
17
|
|
|
20
|
-
return function
|
|
18
|
+
return function(root, result) {
|
|
21
19
|
const validOptions = stylelint.utils.validateOptions(
|
|
22
20
|
result,
|
|
23
21
|
ruleName,
|
|
@@ -39,18 +37,13 @@ function rule(expectation, options, context) {
|
|
|
39
37
|
return;
|
|
40
38
|
}
|
|
41
39
|
|
|
42
|
-
const disableFix = _.get(options,
|
|
43
|
-
|
|
44
|
-
if (context.fix && !disableFix) {
|
|
45
|
-
postcssSorting({ order: expectation })(root);
|
|
46
|
-
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
40
|
+
const disableFix = _.get(options, 'disableFix', false);
|
|
41
|
+
const isFixEnabled = context.fix && !disableFix;
|
|
49
42
|
|
|
50
43
|
const expectedOrder = createExpectedOrder(expectation);
|
|
51
44
|
|
|
52
45
|
// By default, ignore unspecified properties
|
|
53
|
-
const unspecified = _.get(options,
|
|
46
|
+
const unspecified = _.get(options, 'unspecified', 'ignore');
|
|
54
47
|
|
|
55
48
|
const sharedInfo = {
|
|
56
49
|
expectedOrder,
|
|
@@ -58,14 +51,25 @@ function rule(expectation, options, context) {
|
|
|
58
51
|
messages,
|
|
59
52
|
ruleName,
|
|
60
53
|
result,
|
|
54
|
+
isFixEnabled,
|
|
55
|
+
shouldFix: false,
|
|
61
56
|
};
|
|
62
57
|
|
|
63
58
|
// Check all rules and at-rules recursively
|
|
64
59
|
root.walk(function processRulesAndAtrules(node) {
|
|
60
|
+
// return early if we know there is a violation and auto fix should be applied
|
|
61
|
+
if (isFixEnabled && sharedInfo.shouldFix) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
65
|
if (node.type === 'rule' || node.type === 'atrule') {
|
|
66
66
|
checkNode(node, sharedInfo);
|
|
67
67
|
}
|
|
68
68
|
});
|
|
69
|
+
|
|
70
|
+
if (sharedInfo.shouldFix) {
|
|
71
|
+
postcssSorting({ order: expectation })(root);
|
|
72
|
+
}
|
|
69
73
|
};
|
|
70
74
|
}
|
|
71
75
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const _ = require('lodash');
|
|
4
2
|
|
|
5
3
|
module.exports = function validatePrimaryOption(actualOptions) {
|
|
@@ -10,52 +8,68 @@ module.exports = function validatePrimaryOption(actualOptions) {
|
|
|
10
8
|
|
|
11
9
|
// Every item in the array must be a certain string or an object
|
|
12
10
|
// with a "type" property
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
if (
|
|
12
|
+
!actualOptions.every(item => {
|
|
13
|
+
if (_.isString(item)) {
|
|
14
|
+
return _.includes(
|
|
15
|
+
[
|
|
16
|
+
'custom-properties',
|
|
17
|
+
'dollar-variables',
|
|
18
|
+
'at-variables',
|
|
19
|
+
'declarations',
|
|
20
|
+
'rules',
|
|
21
|
+
'at-rules',
|
|
22
|
+
'less-mixins',
|
|
23
|
+
],
|
|
24
|
+
item
|
|
25
|
+
);
|
|
26
|
+
}
|
|
17
27
|
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
return _.isPlainObject(item) && !_.isUndefined(item.type);
|
|
29
|
+
})
|
|
30
|
+
) {
|
|
20
31
|
return false;
|
|
21
32
|
}
|
|
22
33
|
|
|
23
34
|
const objectItems = actualOptions.filter(_.isPlainObject);
|
|
24
35
|
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (item.type !== 'at-rule' && item.type !== 'rule') {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
36
|
+
if (
|
|
37
|
+
!objectItems.every(item => {
|
|
38
|
+
let result = true;
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
// if parameter is specified, name should be specified also
|
|
34
|
-
if (!_.isUndefined(item.parameter) && _.isUndefined(item.name)) {
|
|
40
|
+
if (item.type !== 'at-rule' && item.type !== 'rule') {
|
|
35
41
|
return false;
|
|
36
42
|
}
|
|
37
43
|
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
if (item.type === 'at-rule') {
|
|
45
|
+
// if parameter is specified, name should be specified also
|
|
46
|
+
if (!_.isUndefined(item.parameter) && _.isUndefined(item.name)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
41
49
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
if (!_.isUndefined(item.hasBlock)) {
|
|
51
|
+
result = item.hasBlock === true || item.hasBlock === false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!_.isUndefined(item.name)) {
|
|
55
|
+
result = _.isString(item.name) && item.name.length;
|
|
56
|
+
}
|
|
45
57
|
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
if (!_.isUndefined(item.parameter)) {
|
|
59
|
+
result =
|
|
60
|
+
(_.isString(item.parameter) && item.parameter.length) || _.isRegExp(item.parameter);
|
|
61
|
+
}
|
|
48
62
|
}
|
|
49
|
-
}
|
|
50
63
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
if (item.type === 'rule') {
|
|
65
|
+
if (!_.isUndefined(item.selector)) {
|
|
66
|
+
result = (_.isString(item.selector) && item.selector.length) || _.isRegExp(item.selector);
|
|
67
|
+
}
|
|
54
68
|
}
|
|
55
|
-
}
|
|
56
69
|
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
return result;
|
|
71
|
+
})
|
|
72
|
+
) {
|
|
59
73
|
return false;
|
|
60
74
|
}
|
|
61
75
|
|
|
Binary file
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const stylelint = require('stylelint');
|
|
4
2
|
const _ = require('lodash');
|
|
5
3
|
const postcss = require('postcss');
|
|
@@ -16,7 +14,7 @@ const messages = stylelint.utils.ruleMessages(ruleName, {
|
|
|
16
14
|
function rule(actual, options, context) {
|
|
17
15
|
context = context || {};
|
|
18
16
|
|
|
19
|
-
return function
|
|
17
|
+
return function(root, result) {
|
|
20
18
|
const validOptions = stylelint.utils.validateOptions(
|
|
21
19
|
result,
|
|
22
20
|
ruleName,
|
|
@@ -71,7 +69,7 @@ function checkNode(node, result) {
|
|
|
71
69
|
return;
|
|
72
70
|
}
|
|
73
71
|
|
|
74
|
-
const prop = child
|
|
72
|
+
const { prop } = child;
|
|
75
73
|
|
|
76
74
|
if (!utils.isStandardSyntaxProperty(prop)) {
|
|
77
75
|
return;
|
|
Binary file
|
|
@@ -568,42 +568,3 @@ a {
|
|
|
568
568
|
```
|
|
569
569
|
|
|
570
570
|
If `unspecified` secondary option was set to `ignore`, it will be re-set to `bottom`.
|
|
571
|
-
|
|
572
|
-
If `order: "flexible"` is using, for sorting it will be treated as `strict`. It might change properties order, but linting will not be broken.
|
|
573
|
-
|
|
574
|
-
Given:
|
|
575
|
-
|
|
576
|
-
```json
|
|
577
|
-
{
|
|
578
|
-
"properties-order": [
|
|
579
|
-
{
|
|
580
|
-
"order": "flexible",
|
|
581
|
-
"properties": [
|
|
582
|
-
"color",
|
|
583
|
-
"font-size",
|
|
584
|
-
"font-weight"
|
|
585
|
-
]
|
|
586
|
-
}
|
|
587
|
-
]
|
|
588
|
-
}
|
|
589
|
-
```
|
|
590
|
-
|
|
591
|
-
Before:
|
|
592
|
-
|
|
593
|
-
```css
|
|
594
|
-
a {
|
|
595
|
-
font-size: 1em;
|
|
596
|
-
color: pink;
|
|
597
|
-
font-weight: normal;
|
|
598
|
-
}
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
After:
|
|
602
|
-
|
|
603
|
-
```css
|
|
604
|
-
a {
|
|
605
|
-
color: pink;
|
|
606
|
-
font-size: 1em;
|
|
607
|
-
font-weight: normal;
|
|
608
|
-
}
|
|
609
|
-
```
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const _ = require('lodash');
|
|
4
2
|
|
|
5
3
|
// Add an empty line before a node. Mutates the node.
|
|
6
4
|
module.exports = function addEmptyLineBefore(node, newline) {
|
|
7
|
-
if (
|
|
5
|
+
if (!/\r?\n/.test(node.raws.before)) {
|
|
8
6
|
node.raws.before = _.repeat(newline, 2) + node.raws.before;
|
|
9
|
-
} else if (
|
|
7
|
+
} else if (/^\r?\n/.test(node.raws.before)) {
|
|
10
8
|
node.raws.before = newline + node.raws.before;
|
|
11
|
-
} else if (
|
|
9
|
+
} else if (/\r?\n$/.test(node.raws.before)) {
|
|
12
10
|
node.raws.before = node.raws.before + newline;
|
|
13
11
|
} else {
|
|
14
12
|
node.raws.before = node.raws.before.replace(/(\r?\n)/, `${newline}$1`);
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const stylelint = require('stylelint');
|
|
4
2
|
const _ = require('lodash');
|
|
5
3
|
const addEmptyLineBefore = require('./addEmptyLineBefore');
|
|
@@ -11,17 +9,16 @@ module.exports = function checkEmptyLineBefore(firstPropData, secondPropData, sh
|
|
|
11
9
|
const secondPropIsUnspecified = !secondPropData.orderData;
|
|
12
10
|
|
|
13
11
|
// Now check newlines between ...
|
|
14
|
-
const firstPropSeparatedGroup =
|
|
12
|
+
const firstPropSeparatedGroup = !firstPropIsUnspecified
|
|
15
13
|
? firstPropData.orderData.separatedGroup
|
|
16
14
|
: sharedInfo.lastKnownSeparatedGroup;
|
|
17
|
-
const secondPropSeparatedGroup =
|
|
15
|
+
const secondPropSeparatedGroup = !secondPropIsUnspecified
|
|
18
16
|
? secondPropData.orderData.separatedGroup
|
|
19
17
|
: sharedInfo.lastKnownSeparatedGroup;
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
) {
|
|
19
|
+
sharedInfo.lastKnownSeparatedGroup = secondPropSeparatedGroup;
|
|
20
|
+
|
|
21
|
+
if (firstPropSeparatedGroup !== secondPropSeparatedGroup && !secondPropIsUnspecified) {
|
|
25
22
|
// Get an array of just the property groups, remove any solo properties
|
|
26
23
|
const groups = _.reject(sharedInfo.expectation, _.isString);
|
|
27
24
|
|
|
@@ -30,7 +27,7 @@ module.exports = function checkEmptyLineBefore(firstPropData, secondPropData, sh
|
|
|
30
27
|
const emptyLineBefore = _.get(groups[secondPropSeparatedGroup - 2], 'emptyLineBefore');
|
|
31
28
|
|
|
32
29
|
if (!hasEmptyLineBefore(secondPropData.node) && emptyLineBefore === 'always') {
|
|
33
|
-
if (sharedInfo.
|
|
30
|
+
if (sharedInfo.isFixEnabled) {
|
|
34
31
|
addEmptyLineBefore(secondPropData.node, sharedInfo.context.newline);
|
|
35
32
|
} else {
|
|
36
33
|
stylelint.utils.report({
|
|
@@ -41,7 +38,7 @@ module.exports = function checkEmptyLineBefore(firstPropData, secondPropData, sh
|
|
|
41
38
|
});
|
|
42
39
|
}
|
|
43
40
|
} else if (hasEmptyLineBefore(secondPropData.node) && emptyLineBefore === 'never') {
|
|
44
|
-
if (sharedInfo.
|
|
41
|
+
if (sharedInfo.isFixEnabled) {
|
|
45
42
|
removeEmptyLinesBefore(secondPropData.node, sharedInfo.context.newline);
|
|
46
43
|
} else {
|
|
47
44
|
stylelint.utils.report({
|
|
@@ -53,6 +50,4 @@ module.exports = function checkEmptyLineBefore(firstPropData, secondPropData, sh
|
|
|
53
50
|
}
|
|
54
51
|
}
|
|
55
52
|
}
|
|
56
|
-
|
|
57
|
-
sharedInfo.lastKnownSeparatedGroup = secondPropSeparatedGroup;
|
|
58
53
|
};
|
|
@@ -1,95 +1,93 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const stylelint = require('stylelint');
|
|
4
2
|
const postcss = require('postcss');
|
|
5
|
-
const
|
|
3
|
+
const postcssSorting = require('postcss-sorting');
|
|
6
4
|
const utils = require('../../utils');
|
|
7
5
|
const checkEmptyLineBefore = require('./checkEmptyLineBefore');
|
|
8
6
|
const checkOrder = require('./checkOrder');
|
|
7
|
+
const getNodeData = require('./getNodeData');
|
|
8
|
+
const createFlatOrder = require('./createFlatOrder');
|
|
9
9
|
|
|
10
10
|
module.exports = function checkNode(node, sharedInfo) {
|
|
11
|
-
// Skip if it's an empty rule
|
|
12
|
-
if (!node.nodes || !node.nodes.length) {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const allPropData = [];
|
|
17
11
|
const allNodesData = [];
|
|
18
12
|
|
|
19
|
-
|
|
13
|
+
// Collect order data for all nodes
|
|
14
|
+
node.each(function collectDataForEveryNode(child) {
|
|
15
|
+
const nodeData = getNodeData(child, sharedInfo.expectedOrder);
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
node: child,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
if (child.type === 'decl') {
|
|
27
|
-
const prop = child.prop;
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
-
utils.isStandardSyntaxProperty(prop)
|
|
31
|
-
&& !utils.isCustomProperty(prop)
|
|
32
|
-
) {
|
|
33
|
-
let unprefixedPropName = postcss.vendor.unprefixed(prop);
|
|
34
|
-
|
|
35
|
-
// Hack to allow -moz-osx-font-smoothing to be understood
|
|
36
|
-
// just like -webkit-font-smoothing
|
|
37
|
-
if (unprefixedPropName.indexOf('osx-') === 0) {
|
|
38
|
-
unprefixedPropName = unprefixedPropName.slice(4);
|
|
39
|
-
}
|
|
17
|
+
allNodesData.push(nodeData);
|
|
18
|
+
});
|
|
40
19
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
unprefixedName: unprefixedPropName,
|
|
44
|
-
orderData: sharedInfo.expectedOrder[unprefixedPropName],
|
|
45
|
-
node: child,
|
|
46
|
-
};
|
|
20
|
+
const allPropData = allNodesData.filter(item => item.name);
|
|
21
|
+
let shouldFixOrder = false;
|
|
47
22
|
|
|
48
|
-
|
|
49
|
-
|
|
23
|
+
// First, check order
|
|
24
|
+
allPropData.forEach(function checkEveryPropForOrder(propData, index) {
|
|
25
|
+
// return early if we know there is a violation and auto fix should be applied
|
|
26
|
+
if (shouldFixOrder && sharedInfo.isFixEnabled) {
|
|
27
|
+
return;
|
|
50
28
|
}
|
|
51
29
|
|
|
52
|
-
allNodesData.push(nodeData);
|
|
53
|
-
|
|
54
30
|
// current node should be a standard declaration
|
|
55
|
-
if (
|
|
56
|
-
child.type !== 'decl'
|
|
57
|
-
|| !utils.isStandardSyntaxProperty(nodeData.node.prop)
|
|
58
|
-
|| utils.isCustomProperty(nodeData.node.prop)
|
|
59
|
-
) {
|
|
31
|
+
if (!utils.isProperty(propData.node)) {
|
|
60
32
|
return;
|
|
61
33
|
}
|
|
62
34
|
|
|
63
|
-
|
|
64
|
-
const previousPropData = _.nth(allPropData, -2);
|
|
35
|
+
const previousPropData = allPropData[index - 1];
|
|
65
36
|
|
|
66
37
|
// Skip first decl
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
38
|
+
if (previousPropData) {
|
|
39
|
+
const checkedOrder = checkOrder({
|
|
40
|
+
firstPropData: previousPropData,
|
|
41
|
+
secondPropData: propData,
|
|
42
|
+
unspecified: sharedInfo.unspecified,
|
|
43
|
+
allPropData: allPropData.slice(0, index),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (!checkedOrder.result) {
|
|
47
|
+
if (sharedInfo.isFixEnabled) {
|
|
48
|
+
shouldFixOrder = true;
|
|
49
|
+
} else {
|
|
50
|
+
stylelint.utils.report({
|
|
51
|
+
message: sharedInfo.messages.expected(
|
|
52
|
+
checkedOrder.secondNode.name,
|
|
53
|
+
checkedOrder.firstNode.name
|
|
54
|
+
),
|
|
55
|
+
node: checkedOrder.secondNode.node,
|
|
56
|
+
result: sharedInfo.result,
|
|
57
|
+
ruleName: sharedInfo.ruleName,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
80
60
|
}
|
|
81
61
|
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (shouldFixOrder && sharedInfo.isFixEnabled) {
|
|
65
|
+
const sortingOptions = {
|
|
66
|
+
'properties-order': createFlatOrder(sharedInfo.expectation),
|
|
67
|
+
'unspecified-properties-position':
|
|
68
|
+
sharedInfo.unspecified === 'ignore' ? 'bottom' : sharedInfo.unspecified,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// creating PostCSS Root node with current node as a child,
|
|
72
|
+
// so PostCSS Sorting can process it
|
|
73
|
+
const tempRoot = postcss.root({ nodes: [node] });
|
|
74
|
+
|
|
75
|
+
postcssSorting(sortingOptions)(tempRoot);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
sharedInfo.lastKnownSeparatedGroup = 1;
|
|
82
79
|
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
// Second, check emptyLineBefore
|
|
81
|
+
allNodesData.forEach(function checkEveryPropForEmptyLine(nodeData, index) {
|
|
82
|
+
let previousNodeData = allNodesData[index - 1];
|
|
85
83
|
|
|
86
84
|
// if previous node is shared-line comment, use second previous node
|
|
87
85
|
if (
|
|
88
|
-
previousNodeData
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
previousNodeData &&
|
|
87
|
+
previousNodeData.node.type === 'comment' &&
|
|
88
|
+
previousNodeData.node.raw('before').indexOf('\n') === -1
|
|
91
89
|
) {
|
|
92
|
-
previousNodeData =
|
|
90
|
+
previousNodeData = allNodesData[index - 2];
|
|
93
91
|
}
|
|
94
92
|
|
|
95
93
|
// skip first decl
|
|
@@ -97,18 +95,11 @@ module.exports = function checkNode(node, sharedInfo) {
|
|
|
97
95
|
return;
|
|
98
96
|
}
|
|
99
97
|
|
|
100
|
-
if (previousNodeData.node.type !== 'decl') {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
98
|
// previous node should be a standard declaration
|
|
105
|
-
if (
|
|
106
|
-
!utils.isStandardSyntaxProperty(previousNodeData.node.prop)
|
|
107
|
-
|| utils.isCustomProperty(previousNodeData.node.prop)
|
|
108
|
-
) {
|
|
99
|
+
if (!utils.isProperty(previousNodeData.node)) {
|
|
109
100
|
return;
|
|
110
101
|
}
|
|
111
102
|
|
|
112
|
-
checkEmptyLineBefore(
|
|
103
|
+
checkEmptyLineBefore(previousNodeData, nodeData, sharedInfo);
|
|
113
104
|
});
|
|
114
105
|
};
|
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const stylelint = require('stylelint');
|
|
4
1
|
const postcss = require('postcss');
|
|
5
2
|
const _ = require('lodash');
|
|
6
3
|
const checkAlphabeticalOrder = require('../checkAlphabeticalOrder');
|
|
7
4
|
|
|
8
|
-
module.exports = function checkOrder(firstPropData, secondPropData, allPropData,
|
|
5
|
+
module.exports = function checkOrder({ firstPropData, secondPropData, allPropData, unspecified }) {
|
|
6
|
+
function report(result, firstNode = firstPropData, secondNode = secondPropData) {
|
|
7
|
+
return {
|
|
8
|
+
result,
|
|
9
|
+
firstNode,
|
|
10
|
+
secondNode,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
9
14
|
if (firstPropData.unprefixedName === secondPropData.unprefixedName) {
|
|
10
15
|
// If first property has no prefix and second property has prefix
|
|
11
|
-
if (
|
|
12
|
-
|
|
16
|
+
if (
|
|
17
|
+
!postcss.vendor.prefix(firstPropData.name).length &&
|
|
18
|
+
postcss.vendor.prefix(secondPropData.name).length
|
|
19
|
+
) {
|
|
20
|
+
return report(false);
|
|
13
21
|
}
|
|
14
22
|
|
|
15
|
-
return true;
|
|
23
|
+
return report(true);
|
|
16
24
|
}
|
|
17
25
|
|
|
18
26
|
const firstPropIsUnspecified = !firstPropData.orderData;
|
|
@@ -20,76 +28,64 @@ module.exports = function checkOrder(firstPropData, secondPropData, allPropData,
|
|
|
20
28
|
|
|
21
29
|
// Check actual known properties
|
|
22
30
|
if (!firstPropIsUnspecified && !secondPropIsUnspecified) {
|
|
23
|
-
return
|
|
31
|
+
return report(
|
|
32
|
+
firstPropData.orderData.expectedPosition <= secondPropData.orderData.expectedPosition
|
|
33
|
+
);
|
|
24
34
|
}
|
|
25
35
|
|
|
26
36
|
if (firstPropIsUnspecified && !secondPropIsUnspecified) {
|
|
27
37
|
// If first prop is unspecified, look for a specified prop before it to
|
|
28
38
|
// compare to the current prop
|
|
29
|
-
const priorSpecifiedPropData = _.findLast(allPropData.slice(0, -1),
|
|
39
|
+
const priorSpecifiedPropData = _.findLast(allPropData.slice(0, -1), d => Boolean(d.orderData));
|
|
30
40
|
|
|
31
41
|
if (
|
|
32
|
-
priorSpecifiedPropData
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
priorSpecifiedPropData &&
|
|
43
|
+
priorSpecifiedPropData.orderData &&
|
|
44
|
+
priorSpecifiedPropData.orderData.expectedPosition > secondPropData.orderData.expectedPosition
|
|
35
45
|
) {
|
|
36
|
-
|
|
37
|
-
message: sharedInfo.messages.expected(secondPropData.name, priorSpecifiedPropData.name),
|
|
38
|
-
node: secondPropData.node,
|
|
39
|
-
result: sharedInfo.result,
|
|
40
|
-
ruleName: sharedInfo.ruleName,
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
return true; // avoid logging another warning
|
|
46
|
+
return report(false, priorSpecifiedPropData, secondPropData);
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
49
|
|
|
47
|
-
const unspecified = sharedInfo.unspecified;
|
|
48
|
-
|
|
49
50
|
// Now deal with unspecified props
|
|
50
51
|
// Starting with bottomAlphabetical as it requires more specific conditionals
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
&& !firstPropIsUnspecified
|
|
54
|
-
&& secondPropIsUnspecified
|
|
55
|
-
) {
|
|
56
|
-
return true;
|
|
52
|
+
if (unspecified === 'bottomAlphabetical' && !firstPropIsUnspecified && secondPropIsUnspecified) {
|
|
53
|
+
return report(true);
|
|
57
54
|
}
|
|
58
55
|
|
|
59
|
-
if (
|
|
60
|
-
unspecified === 'bottomAlphabetical'
|
|
61
|
-
&& secondPropIsUnspecified
|
|
62
|
-
&& firstPropIsUnspecified
|
|
63
|
-
) {
|
|
56
|
+
if (unspecified === 'bottomAlphabetical' && firstPropIsUnspecified && secondPropIsUnspecified) {
|
|
64
57
|
if (checkAlphabeticalOrder(firstPropData, secondPropData)) {
|
|
65
|
-
return true;
|
|
58
|
+
return report(true);
|
|
66
59
|
}
|
|
67
60
|
|
|
68
|
-
return false;
|
|
61
|
+
return report(false);
|
|
69
62
|
}
|
|
63
|
+
|
|
70
64
|
if (unspecified === 'bottomAlphabetical' && firstPropIsUnspecified) {
|
|
71
|
-
return false;
|
|
65
|
+
return report(false);
|
|
72
66
|
}
|
|
73
67
|
|
|
74
68
|
if (firstPropIsUnspecified && secondPropIsUnspecified) {
|
|
75
|
-
return true;
|
|
69
|
+
return report(true);
|
|
76
70
|
}
|
|
77
71
|
|
|
78
72
|
if (unspecified === 'ignore' && (firstPropIsUnspecified || secondPropIsUnspecified)) {
|
|
79
|
-
return true;
|
|
73
|
+
return report(true);
|
|
80
74
|
}
|
|
81
75
|
|
|
82
76
|
if (unspecified === 'top' && firstPropIsUnspecified) {
|
|
83
|
-
return true;
|
|
77
|
+
return report(true);
|
|
84
78
|
}
|
|
79
|
+
|
|
85
80
|
if (unspecified === 'top' && secondPropIsUnspecified) {
|
|
86
|
-
return false;
|
|
81
|
+
return report(false);
|
|
87
82
|
}
|
|
88
83
|
|
|
89
84
|
if (unspecified === 'bottom' && secondPropIsUnspecified) {
|
|
90
|
-
return true;
|
|
85
|
+
return report(true);
|
|
91
86
|
}
|
|
87
|
+
|
|
92
88
|
if (unspecified === 'bottom' && firstPropIsUnspecified) {
|
|
93
|
-
return false;
|
|
89
|
+
return report(false);
|
|
94
90
|
}
|
|
95
91
|
};
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const _ = require('lodash');
|
|
4
2
|
|
|
5
3
|
module.exports = function createExpectedOrder(input) {
|
|
@@ -10,7 +8,7 @@ module.exports = function createExpectedOrder(input) {
|
|
|
10
8
|
appendGroup(input);
|
|
11
9
|
|
|
12
10
|
function appendGroup(items) {
|
|
13
|
-
items.forEach(
|
|
11
|
+
items.forEach(item => appendItem(item, false));
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
function appendItem(item, inFlexibleGroup) {
|
|
@@ -35,7 +33,7 @@ module.exports = function createExpectedOrder(input) {
|
|
|
35
33
|
if (item.order && item.order === 'flexible') {
|
|
36
34
|
expectedPosition += 1;
|
|
37
35
|
|
|
38
|
-
item.properties.forEach(
|
|
36
|
+
item.properties.forEach(property => {
|
|
39
37
|
appendItem(property, true);
|
|
40
38
|
});
|
|
41
39
|
} else {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const _ = require('lodash');
|
|
4
2
|
|
|
5
3
|
module.exports = function createFlatOrder(order) {
|
|
@@ -8,7 +6,7 @@ module.exports = function createFlatOrder(order) {
|
|
|
8
6
|
appendGroup(order);
|
|
9
7
|
|
|
10
8
|
function appendGroup(items) {
|
|
11
|
-
items.forEach(
|
|
9
|
+
items.forEach(item => appendItem(item));
|
|
12
10
|
}
|
|
13
11
|
|
|
14
12
|
function appendItem(item) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const postcss = require('postcss');
|
|
2
|
+
const utils = require('../../utils');
|
|
3
|
+
|
|
4
|
+
module.exports = function getNodeData(node, expectedOrder) {
|
|
5
|
+
const nodeData = {
|
|
6
|
+
node,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
if (utils.isProperty(node)) {
|
|
10
|
+
const { prop } = node;
|
|
11
|
+
let unprefixedPropName = postcss.vendor.unprefixed(prop);
|
|
12
|
+
|
|
13
|
+
// Hack to allow -moz-osx-font-smoothing to be understood
|
|
14
|
+
// just like -webkit-font-smoothing
|
|
15
|
+
if (unprefixedPropName.indexOf('osx-') === 0) {
|
|
16
|
+
unprefixedPropName = unprefixedPropName.slice(4);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
nodeData.name = prop;
|
|
20
|
+
nodeData.unprefixedName = unprefixedPropName;
|
|
21
|
+
nodeData.orderData = expectedOrder[unprefixedPropName];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return nodeData;
|
|
25
|
+
};
|
|
@@ -1,26 +1,20 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const stylelint = require('stylelint');
|
|
4
2
|
const _ = require('lodash');
|
|
5
|
-
const postcssSorting = require('postcss-sorting');
|
|
6
3
|
const utils = require('../../utils');
|
|
7
4
|
const checkNode = require('./checkNode');
|
|
8
5
|
const createExpectedOrder = require('./createExpectedOrder');
|
|
9
|
-
const createFlatOrder = require('./createFlatOrder');
|
|
10
6
|
const validatePrimaryOption = require('./validatePrimaryOption');
|
|
11
7
|
|
|
12
8
|
const ruleName = utils.namespace('properties-order');
|
|
13
9
|
|
|
14
10
|
const messages = stylelint.utils.ruleMessages(ruleName, {
|
|
15
11
|
expected: (first, second) => `Expected "${first}" to come before "${second}"`,
|
|
16
|
-
expectedEmptyLineBefore:
|
|
17
|
-
rejectedEmptyLineBefore:
|
|
12
|
+
expectedEmptyLineBefore: property => `Expected an empty line before property "${property}"`,
|
|
13
|
+
rejectedEmptyLineBefore: property => `Unexpected an empty line before property "${property}"`,
|
|
18
14
|
});
|
|
19
15
|
|
|
20
|
-
const rule = function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return function (root, result) {
|
|
16
|
+
const rule = function(expectation, options, context = {}) {
|
|
17
|
+
return function(root, result) {
|
|
24
18
|
const validOptions = stylelint.utils.validateOptions(
|
|
25
19
|
result,
|
|
26
20
|
ruleName,
|
|
@@ -31,12 +25,7 @@ const rule = function (expectation, options, context) {
|
|
|
31
25
|
{
|
|
32
26
|
actual: options,
|
|
33
27
|
possible: {
|
|
34
|
-
unspecified: [
|
|
35
|
-
'top',
|
|
36
|
-
'bottom',
|
|
37
|
-
'ignore',
|
|
38
|
-
'bottomAlphabetical',
|
|
39
|
-
],
|
|
28
|
+
unspecified: ['top', 'bottom', 'ignore', 'bottomAlphabetical'],
|
|
40
29
|
disableFix: _.isBoolean,
|
|
41
30
|
},
|
|
42
31
|
optional: true,
|
|
@@ -48,22 +37,9 @@ const rule = function (expectation, options, context) {
|
|
|
48
37
|
}
|
|
49
38
|
|
|
50
39
|
// By default, ignore unspecified properties
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
if (context.fix && !disableFix) {
|
|
56
|
-
if (unspecified === 'ignore') {
|
|
57
|
-
unspecified = 'bottom';
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const sortingOptions = {
|
|
61
|
-
'properties-order': createFlatOrder(expectation),
|
|
62
|
-
'unspecified-properties-position': unspecified,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
postcssSorting(sortingOptions)(root);
|
|
66
|
-
}
|
|
40
|
+
const unspecified = _.get(options, 'unspecified', 'ignore');
|
|
41
|
+
const disableFix = _.get(options, 'disableFix', false);
|
|
42
|
+
const isFixEnabled = context.fix && !disableFix;
|
|
67
43
|
|
|
68
44
|
const expectedOrder = createExpectedOrder(expectation);
|
|
69
45
|
|
|
@@ -75,12 +51,12 @@ const rule = function (expectation, options, context) {
|
|
|
75
51
|
ruleName,
|
|
76
52
|
result,
|
|
77
53
|
context,
|
|
78
|
-
|
|
54
|
+
isFixEnabled,
|
|
79
55
|
};
|
|
80
56
|
|
|
81
57
|
// Check all rules and at-rules recursively
|
|
82
58
|
root.walk(function processRulesAndAtrules(node) {
|
|
83
|
-
if (
|
|
59
|
+
if (utils.isRuleWithNodes(node)) {
|
|
84
60
|
checkNode(node, sharedInfo);
|
|
85
61
|
}
|
|
86
62
|
});
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const _ = require('lodash');
|
|
4
2
|
|
|
5
3
|
module.exports = function validatePrimaryOption(actualOptions) {
|
|
@@ -10,37 +8,43 @@ module.exports = function validatePrimaryOption(actualOptions) {
|
|
|
10
8
|
|
|
11
9
|
// Every item in the array must be a string or an object
|
|
12
10
|
// with a "properties" property
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
if (
|
|
12
|
+
!actualOptions.every(item => {
|
|
13
|
+
if (_.isString(item)) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return _.isPlainObject(item) && !_.isUndefined(item.properties);
|
|
18
|
+
})
|
|
19
|
+
) {
|
|
20
20
|
return false;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const objectItems = actualOptions.filter(_.isPlainObject);
|
|
24
24
|
|
|
25
25
|
// Every object-item's "properties" should be an array with no items, or with strings
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
if (
|
|
27
|
+
!objectItems.every(item => {
|
|
28
|
+
if (!Array.isArray(item.properties)) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return item.properties.every(property => _.isString(property));
|
|
33
|
+
})
|
|
34
|
+
) {
|
|
33
35
|
return false;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
// Every object-item's "emptyLineBefore" must be "always" or "never"
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
if (
|
|
40
|
+
!objectItems.every(item => {
|
|
41
|
+
if (_.isUndefined(item.emptyLineBefore)) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return _.includes(['always', 'never'], item.emptyLineBefore);
|
|
46
|
+
})
|
|
47
|
+
) {
|
|
44
48
|
return false;
|
|
45
49
|
}
|
|
46
50
|
|
package/utils/index.js
CHANGED
|
@@ -2,6 +2,8 @@ module.exports = {
|
|
|
2
2
|
namespace: require('./namespace'),
|
|
3
3
|
isCustomProperty: require('./isCustomProperty'),
|
|
4
4
|
isStandardSyntaxProperty: require('./isStandardSyntaxProperty'),
|
|
5
|
+
isProperty: require('./isProperty'),
|
|
5
6
|
isDollarVariable: require('./isDollarVariable'),
|
|
6
7
|
isAtVariable: require('./isAtVariable'),
|
|
8
|
+
isRuleWithNodes: require('./isRuleWithNodes'),
|
|
7
9
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Check whether a property is a CSS property
|
|
2
|
+
const isCustomProperty = require('./isCustomProperty');
|
|
3
|
+
const isStandardSyntaxProperty = require('./isStandardSyntaxProperty');
|
|
4
|
+
|
|
5
|
+
module.exports = function isProperty(node) {
|
|
6
|
+
return (
|
|
7
|
+
node.type === 'decl' && isStandardSyntaxProperty(node.prop) && !isCustomProperty(node.prop)
|
|
8
|
+
);
|
|
9
|
+
};
|