mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-07 22:52:39 +00:00
Merge pull request #2834 from naturalcrit/styleLint
Set up Stylelint and add custom plugins
This commit is contained in:
42
.stylelintrc.json
Normal file
42
.stylelintrc.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"extends": ["stylelint-config-recess-order"],
|
||||||
|
"plugins": [
|
||||||
|
"stylelint-stylistic",
|
||||||
|
"./stylelint_plugins/align-colons.js",
|
||||||
|
"./stylelint_plugins/declaration-block-multi-line-min-declarations",
|
||||||
|
"./stylelint_plugins/declaration-colon-min-space-before"
|
||||||
|
],
|
||||||
|
"customSyntax": "postcss-less",
|
||||||
|
"rules": {
|
||||||
|
"stylistic/indentation": "tab",
|
||||||
|
"stylistic/string-quotes": "double",
|
||||||
|
"no-duplicate-selectors": true,
|
||||||
|
"stylistic/color-hex-case": "upper",
|
||||||
|
"color-hex-length": "long",
|
||||||
|
"stylistic/selector-combinator-space-after": "always",
|
||||||
|
"stylistic/selector-combinator-space-before": "always",
|
||||||
|
"selector-attribute-quotes": "always",
|
||||||
|
"stylistic/selector-attribute-operator-space-before": "always",
|
||||||
|
"stylistic/selector-attribute-operator-space-after": "always",
|
||||||
|
"stylistic/selector-attribute-brackets-space-inside": "never",
|
||||||
|
"stylistic/block-opening-brace-space-before": "always",
|
||||||
|
"stylistic/declaration-block-trailing-semicolon": "always",
|
||||||
|
"naturalcrit/declaration-colon-min-space-before": 1,
|
||||||
|
"stylistic/declaration-colon-space-after": "always",
|
||||||
|
"stylistic/number-leading-zero": "always",
|
||||||
|
"function-url-quotes": "always",
|
||||||
|
"function-url-scheme-disallowed-list": ["data"],
|
||||||
|
"font-weight-notation": "named-where-possible",
|
||||||
|
"font-family-name-quotes": "always-unless-keyword",
|
||||||
|
"comment-whitespace-inside": "always",
|
||||||
|
"selector-pseudo-element-colon-notation": "double",
|
||||||
|
"stylistic/selector-pseudo-class-parentheses-space-inside": "never",
|
||||||
|
"stylistic/media-feature-range-operator-space-before": "always",
|
||||||
|
"stylistic/media-feature-range-operator-space-after": "always",
|
||||||
|
"stylistic/media-feature-parentheses-space-inside": "never",
|
||||||
|
"stylistic/media-feature-colon-space-before": "always",
|
||||||
|
"stylistic/media-feature-colon-space-after": "always",
|
||||||
|
"naturalcrit/align-colons": true,
|
||||||
|
"naturalcrit/declaration-block-multi-line-min-declarations" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
33935
package-lock.json
generated
33935
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,8 @@
|
|||||||
"builddev": "node scripts/buildHomebrew.js --dev",
|
"builddev": "node scripts/buildHomebrew.js --dev",
|
||||||
"lint": "eslint --fix **/*.{js,jsx}",
|
"lint": "eslint --fix **/*.{js,jsx}",
|
||||||
"lint:dry": "eslint **/*.{js,jsx}",
|
"lint:dry": "eslint **/*.{js,jsx}",
|
||||||
|
"stylelint": "stylelint --fix **/*.{less}",
|
||||||
|
"stylelint:dry": "stylelint **/*.less",
|
||||||
"circleci": "npm test && eslint **/*.{js,jsx} --max-warnings=0",
|
"circleci": "npm test && eslint **/*.{js,jsx} --max-warnings=0",
|
||||||
"verify": "npm run lint && npm test",
|
"verify": "npm run lint && npm test",
|
||||||
"test": "jest --runInBand",
|
"test": "jest --runInBand",
|
||||||
@@ -113,10 +115,15 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.40.0",
|
"eslint": "^8.40.0",
|
||||||
"eslint-plugin-react": "^7.32.2",
|
|
||||||
"eslint-plugin-jest": "^27.2.1",
|
"eslint-plugin-jest": "^27.2.1",
|
||||||
|
"eslint-plugin-react": "^7.32.2",
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"jest-expect-message": "^1.1.3",
|
"jest-expect-message": "^1.1.3",
|
||||||
|
"postcss-less": "^6.0.0",
|
||||||
|
"stylelint": "^15.6.2",
|
||||||
|
"stylelint-config-recess-order": "^4.0.0",
|
||||||
|
"stylelint-config-standard": "^33.0.0",
|
||||||
|
"stylelint-stylistic": "^0.4.2",
|
||||||
"supertest": "^6.3.3"
|
"supertest": "^6.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
68
stylelint_plugins/align-colons.js
Normal file
68
stylelint_plugins/align-colons.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
const stylelint = require('stylelint');
|
||||||
|
|
||||||
|
const { report, ruleMessages, validateOptions } = stylelint.utils;
|
||||||
|
const ruleName = 'naturalcrit/align-colons';
|
||||||
|
const messages = ruleMessages(ruleName, {
|
||||||
|
expected: (rule) => `Expected colons aligned within rule "${rule}"`,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOption, secondaryOptionObject, context) {
|
||||||
|
return function lint(postcssRoot, postcssResult) {
|
||||||
|
|
||||||
|
const validOptions = validateOptions(
|
||||||
|
postcssResult,
|
||||||
|
ruleName,
|
||||||
|
{
|
||||||
|
actual: primaryOption,
|
||||||
|
possible: [
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!validOptions) { //If the options are invalid, don't lint
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const isAutoFixing = Boolean(context.fix);
|
||||||
|
postcssRoot.walkRules(rule => { //Iterate CSS rules
|
||||||
|
|
||||||
|
let maxColonPos = 0;
|
||||||
|
let misaligned = false;
|
||||||
|
rule.each(declaration => {
|
||||||
|
|
||||||
|
if(declaration.type != "decl")
|
||||||
|
return;
|
||||||
|
|
||||||
|
let colonPos = declaration.prop.length + declaration.raws.between.indexOf(":");
|
||||||
|
if (maxColonPos > 0 && colonPos != maxColonPos) {
|
||||||
|
misaligned = true;
|
||||||
|
}
|
||||||
|
maxColonPos = Math.max(maxColonPos, colonPos);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(misaligned) {
|
||||||
|
if (isAutoFixing) { //We are in “fix” mode
|
||||||
|
rule.each(declaration => {
|
||||||
|
if(declaration.type != "decl")
|
||||||
|
return;
|
||||||
|
|
||||||
|
declaration.raws.between = " ".repeat(maxColonPos - declaration.prop.length) + ":" + declaration.raws.between.split(":")[1];
|
||||||
|
})
|
||||||
|
} else { //We are in “report only” mode
|
||||||
|
report({
|
||||||
|
ruleName,
|
||||||
|
result: postcssResult,
|
||||||
|
message: messages.expected(rule.selector), // Build the reported message
|
||||||
|
node: rule, // Specify the reported node
|
||||||
|
word: rule.selector, // Which exact word caused the error? This positions the error properly
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.ruleName = ruleName;
|
||||||
|
module.exports.messages = messages;
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
const stylelint = require('stylelint');
|
||||||
|
const { isNumber } = require('stylelint/lib/utils/validateTypes');
|
||||||
|
|
||||||
|
const { report, ruleMessages, validateOptions } = stylelint.utils;
|
||||||
|
const ruleName = 'naturalcrit/declaration-block-multi-line-min-declarations';
|
||||||
|
const messages = ruleMessages(ruleName, {
|
||||||
|
expected: (decls) => `Rule with ${decls} declaration${decls == 1 ? '' : 's'} should a single line`,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
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.walkRules(rule => { //Iterate CSS rules
|
||||||
|
|
||||||
|
//Apply rule only if all children are decls (no nested rules)
|
||||||
|
if (rule.nodes.length > primaryOption || !rule.nodes.every((node) => node.type === 'decl')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAutoFixing) { //We are in “fix” mode
|
||||||
|
rule.each((decl) => {
|
||||||
|
decl.raws.before = " ";
|
||||||
|
});
|
||||||
|
rule.raws.after = ' ';
|
||||||
|
} else {
|
||||||
|
report({
|
||||||
|
ruleName,
|
||||||
|
result: postcssResult,
|
||||||
|
message: messages.expected(rule.nodes.length), // Build the reported message
|
||||||
|
node: rule, // Specify the reported node
|
||||||
|
word: rule.selector, // Which exact word caused the error? This positions the error properly
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.ruleName = ruleName;
|
||||||
|
module.exports.messages = messages;
|
||||||
52
stylelint_plugins/declaration-colon-min-space-before.js
Normal file
52
stylelint_plugins/declaration-colon-min-space-before.js
Normal file
@@ -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;
|
||||||
Reference in New Issue
Block a user