Commit 7404bcde authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'tz-gl-stylelint' into 'master'

Stylelints for duplicate selectors and using BS4 classes

See merge request gitlab-org/gitlab-ce!26409
parents b4de23a6 5b05d2b9
{
"plugins":[
"stylelint-scss"
"./scripts/frontend/stylelint/stylelint-duplicate-selectors.js",
"./scripts/frontend/stylelint/stylelint-utility-classes.js",
"stylelint-scss",
],
"rules":{
"at-rule-blacklist":[
......@@ -95,13 +97,15 @@
},
],
"selector-list-comma-newline-after":"always",
"selector-max-compound-selectors":[5, { "severity": "warning" }],
"selector-max-compound-selectors":[3, { "severity": "warning" }],
"selector-max-id":1,
"selector-no-vendor-prefix":true,
"selector-pseudo-element-colon-notation":"double",
"selector-pseudo-element-no-unknown":true,
"shorthand-property-no-redundant-values":true,
"string-quotes":"single",
"value-no-vendor-prefix":[true, { ignoreValues: ["sticky"] }]
"value-no-vendor-prefix":[true, { ignoreValues: ["sticky"] }],
"stylelint-gitlab/duplicate-selectors":[true,{ "severity": "warning" }],
"stylelint-gitlab/utility-classes":[true,{ "severity": "warning" }],
}
}
@import "framework/variables";
@import 'framework/variables';
img {
max-width: 100%;
......
......@@ -17,6 +17,8 @@
"prettier-all": "node ./scripts/frontend/prettier.js check-all",
"prettier-all-save": "node ./scripts/frontend/prettier.js save-all",
"stylelint": "node node_modules/stylelint/bin/stylelint.js app/assets/stylesheets/**/*.* --custom-formatter node_modules/stylelint-error-string-formatter",
"stylelint-file": "node node_modules/stylelint/bin/stylelint.js",
"stylelint-create-utility-map": "node scripts/frontend/stylelint/stylelint-utility-map.js",
"test": "yarn jest && yarn karma",
"webpack": "webpack --config config/webpack.config.js",
"webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js"
......@@ -169,13 +171,15 @@
"karma-mocha-reporter": "^2.2.5",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^4.0.0-beta.0",
"md5": "^2.2.1",
"node-sass": "^4.11.0",
"nodemon": "^1.18.9",
"pixelmatch": "^4.0.2",
"postcss": "^7.0.14",
"prettier": "1.16.4",
"stylelint": "^9.10.1",
"stylelint-config-recommended": "^2.1.0",
"stylelint-scss": "^3.5.3",
"stylelint-scss": "^3.5.4",
"vue-jest": "^4.0.0-beta.2",
"webpack-dev-server": "^3.1.14",
"yarn-deduplicate": "^1.1.1"
......
const stylelint = require('stylelint');
const utils = require('./stylelint-utils');
const ruleName = 'stylelint-gitlab/duplicate-selectors';
const messages = stylelint.utils.ruleMessages(ruleName, {
expected: (selector1, selector2) => {
return `"${selector1}" and "${selector2}" have the same properties.`;
},
});
module.exports = stylelint.createPlugin(ruleName, function(enabled) {
if (!enabled) {
return;
}
return function(root, result) {
const selectorGroups = {};
utils.createPropertiesHashmap(root, result, ruleName, messages, selectorGroups, true);
};
});
module.exports.ruleName = ruleName;
module.exports.messages = messages;
const stylelint = require('stylelint');
const utils = require('./stylelint-utils');
const utilityClasses = require('./utility-classes-map.js');
const ruleName = 'stylelint-gitlab/utility-classes';
const messages = stylelint.utils.ruleMessages(ruleName, {
expected: (selector1, selector2) => {
return `"${selector1}" has the same properties as our BS4 utility class "${selector2}" so please use that instead.`;
},
});
module.exports = stylelint.createPlugin(ruleName, function(enabled) {
if (!enabled) {
return;
}
return function(root, result) {
utils.createPropertiesHashmap(root, result, ruleName, messages, utilityClasses, false);
};
});
module.exports.ruleName = ruleName;
module.exports.messages = messages;
const sass = require('node-sass');
const postcss = require('postcss');
const fs = require('fs');
const path = require('path');
const prettier = require('prettier');
const utils = require('./stylelint-utils');
const ROOT_PATH = path.resolve(__dirname, '../../..');
const hashMapPath = path.resolve(__dirname, './utility-classes-map.js');
//
// This creates a JS based hash map (saved in utility-classes-map.js) of the different values in the utility classes
//
sass.render(
{
data: `
@import './functions';
@import './variables';
@import './mixins';
@import './utilities';
`,
includePaths: [path.resolve(ROOT_PATH, 'node_modules/bootstrap/scss')],
},
(err, result) => {
if (err) console.error('Error ', err);
const cssResult = result.css.toString();
// We just use postcss to create a CSS tree
postcss([])
.process(cssResult, {
// This supresses a postcss warning
from: undefined,
})
.then(result => {
const selectorGroups = {};
utils.createPropertiesHashmap(result.root, result, null, null, selectorGroups, true);
const prettierOptions = prettier.resolveConfig.sync(hashMapPath);
const prettyHashmap = prettier.format(
`module.exports = ${JSON.stringify(selectorGroups)};`,
prettierOptions,
);
fs.writeFile(hashMapPath, prettyHashmap, function(err) {
if (err) {
return console.log(err);
}
console.log('The file was saved!');
});
});
},
);
const stylelint = require('stylelint');
const md5 = require('md5');
module.exports.createPropertiesHashmap = (
ruleRoot,
result,
ruleName,
messages,
selectorGroups,
addSelectors,
) => {
ruleRoot.walkRules(rule => {
const selector = rule.selector.replace(/(?:\r\n|\r|\n)/g, ' ');
if (
rule &&
rule.parent &&
rule.parent.type != 'atrule' &&
!(
selector.includes('-webkit-') ||
selector.includes('-moz-') ||
selector.includes('-o-') ||
selector.includes('-ms-') ||
selector.includes(':')
)
) {
let cssArray = [];
rule.nodes.forEach(function(property) {
const { prop, value } = property;
if (property && value) {
const propval = `${prop}${value}${property.important ? '!important' : ''}`;
cssArray.push(propval);
}
});
cssArray = cssArray.sort();
const cssContent = cssArray.toString();
if (cssContent) {
const hashValue = md5(cssContent);
const selObj = selectorGroups[hashValue];
const selectorLine = `${selector} (${
rule.source.input.file ? rule.source.input.file + ' -' : ''
}${rule.source.start.line}:${rule.source.start.column})`;
if (selObj) {
if (selectorGroups[hashValue].selectors.indexOf(selector) == -1) {
let lastSelector =
selectorGroups[hashValue].selectors[selectorGroups[hashValue].selectors.length - 1];
// So we have nicer formatting if it is the same file, we remove the filename
lastSelector = lastSelector.replace(`${rule.source.input.file} - `, '');
if (messages) {
stylelint.utils.report({
result,
ruleName,
message: messages.expected(selector, lastSelector),
node: rule,
word: rule.node,
});
}
if (addSelectors) {
selectorGroups[hashValue].selectors.push(selectorLine);
}
}
} else if (addSelectors) {
selectorGroups[hashValue] = {
selectors: [selectorLine],
};
}
}
}
});
};
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment