Skip to content

Commit c53de7e

Browse files
committed
build: add stylelint rule to ban concrete CSS rules inside theme files
Adds a stylelint rule that will ensure that all of the CSS rules inside theme files are placed inside a mixin. This avoids style duplication whenever the file is imported. Relates to angular#9999.
1 parent 45399c6 commit c53de7e

File tree

2 files changed

+47
-4
lines changed

2 files changed

+47
-4
lines changed

stylelint-config.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"./tools/stylelint/no-prefixes/no-prefixes.js",
44
"./tools/stylelint/selector-nested-pattern-scoped/index.js",
55
"./tools/stylelint/selector-no-deep/index.js",
6-
"./tools/stylelint/no-nested-mixin/index.js"
6+
"./tools/stylelint/no-nested-mixin/index.js",
7+
"./tools/stylelint/no-concrete-rules/index.js"
78
],
89
"rules": {
910
"material/no-prefixes": [["last 2 versions", "not ie <= 10", "not ie_mob <= 10"]],
@@ -13,6 +14,9 @@
1314
"message": "The & operator is not allowed at the end of theme selectors.",
1415
"filePattern": "-theme\\.scss$"
1516
}],
17+
"material/no-concrete-rules": [true, {
18+
"filePattern": "-theme\\.scss$"
19+
}],
1620

1721
"color-hex-case": "lower",
1822
"color-no-invalid-hex": true,
@@ -43,7 +47,7 @@
4347

4448
"property-case": "lower",
4549

46-
"declaration-block-no-duplicate-properties": [ true, {
50+
"declaration-block-no-duplicate-properties": [true, {
4751
"ignore": ["consecutive-duplicates-with-different-values"]
4852
}],
4953
"declaration-block-trailing-semicolon": "always",
@@ -53,8 +57,8 @@
5357
"declaration-block-semicolon-newline-before": "never-multi-line",
5458
"declaration-block-semicolon-newline-after": "always-multi-line",
5559
"declaration-property-value-blacklist": [
56-
{ "/.*/": ["initial"] },
57-
{ "message": "The `initial` value is not supported in IE."}
60+
{"/.*/": ["initial"]},
61+
{"message": "The `initial` value is not supported in IE."}
5862
],
5963

6064
"block-closing-brace-newline-after": "always",
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const stylelint = require('stylelint');
2+
const path = require('path');
3+
const ruleName = 'material/no-concrete-rules';
4+
const messages = stylelint.utils.ruleMessages(ruleName, {
5+
expected: pattern => `CSS rules must be placed inside a mixin for files matching '${pattern}'.`
6+
});
7+
8+
/**
9+
* Stylelint plugin that will log a warning for all top-level CSS rules.
10+
* Can be used in theme files to ensure that everything is inside a mixin.
11+
*/
12+
const plugin = stylelint.createPlugin(ruleName, (isEnabled, options) => {
13+
return (root, result) => {
14+
if (!isEnabled) return;
15+
16+
const filePattern = new RegExp(options.filePattern);
17+
const fileName = path.basename(root.source.input.file);
18+
19+
if (!filePattern.test(fileName)) return;
20+
21+
// Go through all the nodes and report a warning for every CSS rule or mixin inclusion.
22+
// We use a regular `forEach`, instead of the PostCSS walker utils, because we only care
23+
// about the top-level nodes.
24+
root.nodes.forEach(node => {
25+
if (node.type === 'rule' || (node.type === 'atrule' && node.name === 'include')) {
26+
stylelint.utils.report({
27+
result,
28+
ruleName,
29+
node,
30+
message: messages.expected(filePattern)
31+
});
32+
}
33+
});
34+
};
35+
});
36+
37+
plugin.ruleName = ruleName;
38+
plugin.messages = messages;
39+
module.exports = plugin;

0 commit comments

Comments
 (0)