mirror of https://github.com/apache/superset.git
chore: Eslint custom plugin to warn about hex and literal colors (#19239)
* wip * Add eslint custom plugin * Refactor * Clean up * Update superset-frontend/buildtools/eslint-plugin-theme-colors/index.js Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com> * Refactor * Update superset-frontend/buildtools/eslint-plugin-theme-colors/index.js Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com> * Clean up Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>
This commit is contained in:
parent
d46a550774
commit
6b9113a17b
|
@ -67,7 +67,7 @@ module.exports = {
|
|||
version: 'detect',
|
||||
},
|
||||
},
|
||||
plugins: ['prettier', 'react', 'file-progress'],
|
||||
plugins: ['prettier', 'react', 'file-progress', 'theme-colors'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
|
@ -181,10 +181,28 @@ module.exports = {
|
|||
],
|
||||
'no-only-tests/no-only-tests': 'error',
|
||||
'max-classes-per-file': 0,
|
||||
'theme-colors/no-literal-colors': 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'*.test.ts',
|
||||
'*.test.tsx',
|
||||
'*.test.js',
|
||||
'*.test.jsx',
|
||||
'*.stories.tsx',
|
||||
'*.stories.jsx',
|
||||
'fixtures.*',
|
||||
'cypress-base/cypress/**/*',
|
||||
'Stories.tsx',
|
||||
],
|
||||
rules: {
|
||||
'theme-colors/no-literal-colors': 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
'theme-colors/no-literal-colors': 1,
|
||||
camelcase: [
|
||||
'error',
|
||||
{
|
||||
|
|
|
@ -232,6 +232,7 @@
|
|||
"eslint-plugin-react": "^7.22.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-testing-library": "^3.10.1",
|
||||
"eslint-plugin-theme-colors": "file:tools/eslint-plugin-theme-colors",
|
||||
"exports-loader": "^0.7.0",
|
||||
"fetch-mock": "^7.7.3",
|
||||
"fork-ts-checker-webpack-plugin": "^6.3.3",
|
||||
|
@ -277,6 +278,18 @@
|
|||
"npm": "^7.5.4"
|
||||
}
|
||||
},
|
||||
"buildtools/eslint-plugin-theme-colors": {
|
||||
"version": "1.0.0",
|
||||
"extraneous": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.9.1",
|
||||
"npm": "^7.5.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/core": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz",
|
||||
|
@ -33122,6 +33135,10 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-theme-colors": {
|
||||
"resolved": "tools/eslint-plugin-theme-colors",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/eslint-scope": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||
|
@ -59990,6 +60007,18 @@
|
|||
"src": {
|
||||
"version": "0.0.1",
|
||||
"extraneous": true
|
||||
},
|
||||
"tools/eslint-plugin-theme-colors": {
|
||||
"version": "1.0.0",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.9.1",
|
||||
"npm": "^7.5.4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -86194,6 +86223,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"eslint-plugin-theme-colors": {
|
||||
"version": "file:tools/eslint-plugin-theme-colors",
|
||||
"requires": {
|
||||
"lodash": "^4.17.21"
|
||||
}
|
||||
},
|
||||
"eslint-scope": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||
|
|
|
@ -292,6 +292,7 @@
|
|||
"eslint-plugin-react": "^7.22.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-testing-library": "^3.10.1",
|
||||
"eslint-plugin-theme-colors": "file:tools/eslint-plugin-theme-colors",
|
||||
"exports-loader": "^0.7.0",
|
||||
"fetch-mock": "^7.7.3",
|
||||
"fork-ts-checker-webpack-plugin": "^6.3.3",
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
// https://www.w3.org/wiki/CSS/Properties/color/keywords
|
||||
module.exports = [
|
||||
'black',
|
||||
'silver',
|
||||
'gray',
|
||||
'grey',
|
||||
'white',
|
||||
'maroon',
|
||||
'red',
|
||||
'purple',
|
||||
'fuchsia',
|
||||
'green',
|
||||
'lime',
|
||||
'olive',
|
||||
'yellow',
|
||||
'navy',
|
||||
'blue',
|
||||
'teal',
|
||||
'aqua',
|
||||
'aliceblue',
|
||||
'antiquewhite',
|
||||
'aquamarine',
|
||||
'azure',
|
||||
'beige',
|
||||
'bisque',
|
||||
'blanchedalmond',
|
||||
'blueviolet',
|
||||
'brown',
|
||||
'burlywood',
|
||||
'cadetblue',
|
||||
'chartreuse',
|
||||
'chocolate',
|
||||
'coral',
|
||||
'cornflowerblue',
|
||||
'cornsilk',
|
||||
'crimson',
|
||||
'cyan',
|
||||
'darkblue',
|
||||
'darkcyan',
|
||||
'darkgoldenrod',
|
||||
'darkgray',
|
||||
'darkgreen',
|
||||
'darkgrey',
|
||||
'darkkhaki',
|
||||
'darkmagenta',
|
||||
'darkolivegreen',
|
||||
'darkorange',
|
||||
'darkorchid',
|
||||
'darkred',
|
||||
'darksalmon',
|
||||
'darkseagreen',
|
||||
'darkslateblue',
|
||||
'darkslategray',
|
||||
'darkslategrey',
|
||||
'darkturquoise',
|
||||
'darkviolet',
|
||||
'deeppink',
|
||||
'deepskyblue',
|
||||
'dimgray',
|
||||
'dimgrey',
|
||||
'dodgerblue',
|
||||
'firebrick',
|
||||
'floralwhite',
|
||||
'forestgreen',
|
||||
'fuchsia',
|
||||
'gainsboro',
|
||||
'ghostwhite',
|
||||
'gold',
|
||||
'goldenrod',
|
||||
'greenyellow',
|
||||
'honeydew',
|
||||
'hotpink',
|
||||
'indianred',
|
||||
'indigo',
|
||||
'ivory',
|
||||
'khaki',
|
||||
'lavender',
|
||||
'lavenderblush',
|
||||
'lawngreen',
|
||||
'lemonchiffon',
|
||||
'lightblue',
|
||||
'lightcoral',
|
||||
'lightcyan',
|
||||
'lightgoldenrodyellow',
|
||||
'lightgray',
|
||||
'lightgreen',
|
||||
'lightgrey',
|
||||
'lightpink',
|
||||
'lightsalmon',
|
||||
'lightseagreen',
|
||||
'lightskyblue',
|
||||
'lightslategray',
|
||||
'lightslategrey',
|
||||
'lightsteelblue',
|
||||
'lightyellow',
|
||||
'limegreen',
|
||||
'linen',
|
||||
'magenta',
|
||||
'maroon',
|
||||
'mediumaquamarine',
|
||||
'mediumblue',
|
||||
'mediumorchid',
|
||||
'mediumpurple',
|
||||
'mediumseagreen',
|
||||
'mediumslateblue',
|
||||
'mediumspringgreen',
|
||||
'mediumturquoise',
|
||||
'mediumvioletred',
|
||||
'midnightblue',
|
||||
'mintcream',
|
||||
'mistyrose',
|
||||
'moccasin',
|
||||
'navajowhite',
|
||||
'oldlace',
|
||||
'olivedrab',
|
||||
'orange',
|
||||
'orangered',
|
||||
'orchid',
|
||||
'palegoldenrod',
|
||||
'palegreen',
|
||||
'paleturquoise',
|
||||
'palevioletred',
|
||||
'papayawhip',
|
||||
'peachpuff',
|
||||
'peru',
|
||||
'pink',
|
||||
'plum',
|
||||
'powderblue',
|
||||
'rosybrown',
|
||||
'royalblue',
|
||||
'saddlebrown',
|
||||
'salmon',
|
||||
'sandybrown',
|
||||
'seagreen',
|
||||
'seashell',
|
||||
'sienna',
|
||||
'skyblue',
|
||||
'slateblue',
|
||||
'slategray',
|
||||
'slategrey',
|
||||
'snow',
|
||||
'springgreen',
|
||||
'steelblue',
|
||||
'tan',
|
||||
'teal',
|
||||
'thistle',
|
||||
'tomato',
|
||||
'turquoise',
|
||||
'violet',
|
||||
'wheat',
|
||||
'whitesmoke',
|
||||
'yellowgreen',
|
||||
];
|
|
@ -0,0 +1,114 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Rule to warn about literal colors
|
||||
* @author Apache
|
||||
*/
|
||||
|
||||
const COLOR_KEYWORDS = require('./colors');
|
||||
|
||||
function hasHexColor(quasi) {
|
||||
if (typeof quasi === 'string') {
|
||||
const regex = /#([a-f0-9]{3}|[a-f0-9]{4}(?:[a-f0-9]{2}){0,2})\b/gi;
|
||||
return !!quasi.match(regex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function hasRgbColor(quasi) {
|
||||
if (typeof quasi === 'string') {
|
||||
const regex = /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)/i;
|
||||
return !!quasi.match(regex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function hasLiteralColor(quasi, strict = false) {
|
||||
if (typeof quasi === 'string') {
|
||||
// matches literal colors at the start or end of a CSS prop
|
||||
return COLOR_KEYWORDS.some(color => {
|
||||
const regexColon = new RegExp(`: ${color}`);
|
||||
const regexSemicolon = new RegExp(` ${color};`);
|
||||
return (
|
||||
!!quasi.match(regexColon) ||
|
||||
!!quasi.match(regexSemicolon) ||
|
||||
(strict && quasi === color)
|
||||
);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const WARNING_MESSAGE =
|
||||
'Theme color variables are preferred over rgb(a)/hex/literal colors';
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
rules: {
|
||||
'no-literal-colors': {
|
||||
create(context) {
|
||||
const warned = [];
|
||||
return {
|
||||
TemplateElement(node) {
|
||||
const rawValue = node?.value?.raw;
|
||||
const isParentProperty =
|
||||
node?.parent?.parent?.type === 'TaggedTemplateExpression';
|
||||
const loc = node?.parent?.parent?.loc;
|
||||
const locId = loc && JSON.stringify(loc);
|
||||
const hasWarned = warned.includes(locId);
|
||||
if (
|
||||
!hasWarned &&
|
||||
isParentProperty &&
|
||||
rawValue &&
|
||||
(hasLiteralColor(rawValue) ||
|
||||
hasHexColor(rawValue) ||
|
||||
hasRgbColor(rawValue))
|
||||
) {
|
||||
context.report(node, loc, WARNING_MESSAGE);
|
||||
warned.push(locId);
|
||||
}
|
||||
},
|
||||
Literal(node) {
|
||||
const value = node?.value;
|
||||
const isParentProperty = node?.parent?.type === 'Property';
|
||||
const locId = JSON.stringify(node.loc);
|
||||
const hasWarned = warned.includes(locId);
|
||||
|
||||
if (
|
||||
!hasWarned &&
|
||||
isParentProperty &&
|
||||
value &&
|
||||
(hasLiteralColor(value, true) ||
|
||||
hasHexColor(value) ||
|
||||
hasRgbColor(value))
|
||||
) {
|
||||
context.report(node, node.loc, WARNING_MESSAGE);
|
||||
warned.push(locId);
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "eslint-plugin-theme-colors",
|
||||
"version": "1.0.0",
|
||||
"description": "Warns about rgb(a)/hex/literal colors",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"license": "Apache-2.0",
|
||||
"author": "Apache",
|
||||
"dependencies": {},
|
||||
"engines": {
|
||||
"node": "^16.9.1",
|
||||
"npm": "^7.5.4"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue