Skip to content
This repository has been archived by the owner on Feb 15, 2021. It is now read-only.

Commit

Permalink
Refactored code (#4)
Browse files Browse the repository at this point in the history
Polished code. ☕️ I can't sleep. I've also introduced **strict mode** we have been talking about it.
  • Loading branch information
jukben authored Mar 31, 2019
1 parent 97327d3 commit 4524fcf
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 132 deletions.
13 changes: 12 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors)
[![codecov](https://codecov.io/gh/productboardlabs/stylelint-pb/branch/master/graph/badge.svg)](https://codecov.io/gh/productboardlabs/stylelint-pb)


Set (TODO) of our custom made rules for Stylelint. 🚀

## Story
Expand Down Expand Up @@ -40,6 +39,18 @@ Example configuration. 👇
}
```

You can also run this rule in **strict mode** which means that there is no other color than variable allowed!

```js
{
rules: {
"@productboardlabs/smart-color-replacement": [{
"@snowWhite": "#f4f5e2"
}, "strictMode"]
}
}
```

## Contributors

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
Expand Down
6 changes: 4 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const { createPlugin } = require("stylelint");
const rule = require("./rules");
const rules = require("./rules");

module.exports = createPlugin("@productboard/smart-color-replacement", rule);
module.exports = Object.values(rules).map(rule =>
createPlugin(rule.ruleName, rule)
);
53 changes: 0 additions & 53 deletions src/rules/__tests__/index.test.js

This file was deleted.

100 changes: 100 additions & 0 deletions src/rules/__tests__/smart-color-replacement.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
const rule = require("../smart-color-replacement");

testRule(rule, {
ruleName: rule.ruleName,
config: [
{
"@snowWhite": "#f4f5e2"
}
],
fix: true,

accept: [
{
code: "",
description: "empty stylesheet"
},
{
code: "a {}",
description: "empty rule"
},
{
code: '@import "foo.css";',
description: "blockless statement"
},
{
code: ":global {}",
description: "CSS Modules global empty rule set"
},
{
code: "a { color: @snowWhite; }",
description: "Usage of correct variable"
},
{
code: "a { font-size: 3rem; }",
description: "No color usage should be ignored"
},
{
code: "a { color: #333333; }",
description: "Color out of the config should be ignored by default"
}
],

reject: [
{
code: "a { color: #f4f5e2; }",
fixed: "a { color: @snowWhite; }",
description: "Should use correct variable not hexadecimal notation",
message: rule.messages.expected([
{
used: "#f4f5e2",
suggested: "@snowWhite"
}
]),
line: 1
},
{
code: "a { background: rgb(244, 245, 226); }",
fixed: "a { background: @snowWhite; }",
description: "Should use correct variable not rgb notation",
message: rule.messages.expected([
{
used: "rgb(244, 245, 226)",
suggested: "@snowWhite"
}
]),
line: 1
}
]
});

testRule(rule, {
ruleName: rule.ruleName,
fix: true,

accept: [
{
code: "a { font-size: 3rem; }",
description: "Should do nothing without config"
}
]
});

testRule(rule, {
ruleName: rule.ruleName,
config: [
{
"@snowWhite": "#f4f5e2"
},
"strictMode"
],
fix: true,

reject: [
{
code: "a { color: #333333; }",
fixed: "a { color: #333333; }",
description: "Any color except variables is prohibited!"
}
]
});
79 changes: 3 additions & 76 deletions src/rules/index.js
Original file line number Diff line number Diff line change
@@ -1,78 +1,5 @@
const stylelint = require("stylelint");
const color = require("color");
const smartColorReplacement = require("./smart-color-replacement");

const ruleName = "@productboard/smart-color-replacement";

const messages = stylelint.utils.ruleMessages(ruleName, {
expected: failedColors =>
failedColors
.map(
({ used, suggested }) => `Colour "${used}" should be "${suggested}".`
)
.join(" ")
});

const rule = function(expectation, _, context) {
return (root, result) => {
const HEX_REGEX = /(#[a-f0-9]{3,6})/gi;
const RGB_REGEX = /(rgb|rgba)\([^\)]*\)/gi;
const HSL_REGEX = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/gi;

const patterns = [HEX_REGEX, RGB_REGEX, HSL_REGEX];

if (typeof expectation !== "object" || !expectation) return;

root.walkDecls(declaration => {
const { value } = declaration;
const colors = patterns.reduce((acc, c) => {
const matches = value.match(c);
if (matches && matches.length > 0) {
const colors = matches.map(m => ({
sanitized: color(m).hex(),
original: m
}));
acc.push(...colors);
}

return acc;
}, []);

const lookUpObject = Object.entries(expectation).reduce(
(acc, [key, value]) => {
acc[value.toUpperCase()] = key;

return acc;
},
{}
);

const results = colors.map(({ sanitized, original }) => ({
valid: !lookUpObject[sanitized],
used: original,
suggested: lookUpObject[sanitized]
}));

const failedColors = results.filter(res => !res.valid);

if (failedColors.length) {
stylelint.utils.report({
message: messages.expected(failedColors),
node: declaration,
result,
ruleName
});

if (context.fix) {
failedColors.forEach(({ used, suggested }) => {
declaration.value = declaration.value.replace(used, suggested);
});
}
}
});
};
module.exports = {
smartColorReplacement
};

rule.ruleName = ruleName;
rule.messages = messages;

module.exports = rule;
79 changes: 79 additions & 0 deletions src/rules/smart-color-replacement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const stylelint = require("stylelint");
const color = require("color");

const HEX_REGEX = /(#[a-f0-9]{3,6})/gi;
const RGB_REGEX = /(rgb|rgba)\([^\)]*\)/gi;
const HSL_REGEX = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/gi;

const ruleName = "@productboard/smart-color-replacement";

const messages = stylelint.utils.ruleMessages(ruleName, {
expected: failedColors =>
failedColors
.map(({ used, suggested }) => `Color "${used}" should be "${suggested}".`)
.join(" ")
});

const rule = function(configuration, strictMode, context) {
return (root, result) => {
if (typeof configuration !== "object" || !configuration) return;

const lookUpObject = Object.entries(configuration).reduce(
(acc, [key, value]) => {
acc[value.toUpperCase()] = key;

return acc;
},
{}
);

const patterns = [HEX_REGEX, RGB_REGEX, HSL_REGEX];

root.walkDecls(declaration => {
const { value } = declaration;
const colors = patterns.reduce((acc, c) => {
const matches = value.match(c);
if (matches && matches.length > 0) {
acc.push(...matches);
}

return acc;
}, []);

const results = colors.map(used => {
const sanitized = color(used).hex();
return {
valid: lookUpObject[sanitized]
? !lookUpObject[sanitized]
: strictMode !== "strictMode",
suggested: lookUpObject[sanitized],
used
};
});

const failedColors = results.filter(res => !res.valid);

if (failedColors.length) {
stylelint.utils.report({
message: messages.expected(failedColors),
node: declaration,
result,
ruleName
});

if (context.fix) {
failedColors
.filter(({ suggested }) => suggested)
.forEach(({ used, suggested }) => {
declaration.value = declaration.value.replace(used, suggested);
});
}
}
});
};
};

rule.ruleName = ruleName;
rule.messages = messages;

module.exports = rule;

0 comments on commit 4524fcf

Please sign in to comment.