diff --git a/.stylelintrc.json b/.stylelintrc.json index 167db4f56..66388882c 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -3,7 +3,8 @@ "plugins": [ "stylelint-stylistic", "./stylelint_plugins/align-colons.js", - "./stylelint_plugins/declaration-block-multi-line-min-declarations" + "./stylelint_plugins/declaration-block-multi-line-min-declarations", + "./stylelint_plugins/declaration-colon-min-space-before" ], "customSyntax": "postcss-less", "rules": { @@ -20,7 +21,7 @@ "stylistic/selector-attribute-brackets-space-inside": "never", "stylistic/block-opening-brace-space-before": "always", "stylistic/declaration-block-trailing-semicolon": "always", - "stylistic/declaration-colon-space-before": "always", + "naturalcrit/declaration-colon-min-space-before": 1, "stylistic/declaration-colon-space-after": "always", "stylistic/number-leading-zero": "always", "function-url-quotes": "always", diff --git a/stylelint_plugins/declaration-colon-min-space-before.js b/stylelint_plugins/declaration-colon-min-space-before.js new file mode 100644 index 000000000..a758d5f26 --- /dev/null +++ b/stylelint_plugins/declaration-colon-min-space-before.js @@ -0,0 +1,52 @@ +const stylelint = require('stylelint'); +const { isNumber } = require('stylelint/lib/utils/validateTypes'); + +const { report, ruleMessages, validateOptions } = stylelint.utils; +const ruleName = 'naturalcrit/declaration-colon-min-space-before'; +const messages = ruleMessages(ruleName, { + expected: (num) => `Expected at least ${num} space${num == 1 ? '' : 's'} before ":"` +}); + + +module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOption, secondaryOptionObject, context) { + return function lint(postcssRoot, postcssResult) { + + const validOptions = validateOptions( + postcssResult, + ruleName, + { + actual: primaryOption, + possible: [isNumber], + } + ); + + if (!validOptions) { //If the options are invalid, don't lint + return; + } + const isAutoFixing = Boolean(context.fix); + + postcssRoot.walkDecls(decl => { //Iterate CSS declarations + + let between = decl.raws.between; + const colonIndex = between.indexOf(":"); + + if (between.slice(0, colonIndex).length >= primaryOption) { + return; + } + if (isAutoFixing) { //We are in “fix” mode + decl.raws.between = between.slice(0, colonIndex).replace(/\s*$/, ' '.repeat(primaryOption)) + between.slice(colonIndex) + } else { + report({ + ruleName, + result: postcssResult, + message: messages.expected(primaryOption), // Build the reported message + node: decl, // Specify the reported node + word: ":", // Which exact word caused the error? This positions the error properly + }); + } + }); + }; +}); + +module.exports.ruleName = ruleName; +module.exports.messages = messages;