From b3f28a7338367805bb8ef253eb05b1c290fad667 Mon Sep 17 00:00:00 2001 From: Jean-Michel FRANCOIS Date: Fri, 5 Apr 2024 17:47:07 +0200 Subject: [PATCH] feat(eslint): warning on bootstrap class (#5255) --- .changeset/metal-horses-attend.md | 6 + .../src/rules/use-bootstrap-class.js | 155 ++++++++++++++++++ .../src/rules/use-bootstrap-class.test.js | 69 ++++++++ tools/scripts-config-eslint/.eslintrc.json | 1 + 4 files changed, 231 insertions(+) create mode 100644 .changeset/metal-horses-attend.md create mode 100644 tools/eslint-plugin/src/rules/use-bootstrap-class.js create mode 100644 tools/eslint-plugin/tests/src/rules/use-bootstrap-class.test.js diff --git a/.changeset/metal-horses-attend.md b/.changeset/metal-horses-attend.md new file mode 100644 index 00000000000..ce0d6089ad8 --- /dev/null +++ b/.changeset/metal-horses-attend.md @@ -0,0 +1,6 @@ +--- +"@talend/eslint-plugin": minor +"@talend/eslint-config": minor +--- + +feat: add warning on bootstrap class diff --git a/tools/eslint-plugin/src/rules/use-bootstrap-class.js b/tools/eslint-plugin/src/rules/use-bootstrap-class.js new file mode 100644 index 00000000000..eeef754c8f3 --- /dev/null +++ b/tools/eslint-plugin/src/rules/use-bootstrap-class.js @@ -0,0 +1,155 @@ +// https://getbootstrap.com/docs/3.3/css/ +const BOOTSTRAP_CLASS = [ + // status + 'disabled', + 'active', + 'success', + 'warning', + 'danger', + 'info', + 'blockquote-reverse', + // background + 'bg-primary', + 'bg-success', + 'bg-info', + 'bg-warning', + 'bg-danger', + // btn + 'btn', + 'btn-primary', + 'btn-default', + 'btn-success', + 'btn-info', + 'btn-warning', + 'btn-danger', + 'btn-link', + 'btn-lg', + 'btn-sm', + 'btn-xs', + 'btn-block', + + 'caret', + 'pull-left', + 'pull-right', + 'clearfix', + 'show', + 'hide', + 'sr-only', + + // grid + 'container', + 'row', + + // forms + 'checkbox', + 'control-label', + 'form-inline', + 'form-group', + 'form-control', + 'help-block', + 'input-group', + 'input-group-addon', + + //navbar + 'nav', + 'navbar-left', + 'navbar-right', + 'navbar-text', + 'navbar-btn', + 'navbar-form', + 'navbar-link', + + //list + 'list-unstyled', + 'list-inline', + 'dl-horizontal', + + //table + 'table', + 'table-condensed', + 'table-hover', + 'table-striped', + //text + 'text-muted', + 'text-primary', + 'text-sucess', + 'text-info', + 'text-warning', + 'text-danger', + 'text-hide', + + //responsive + 'hidden-xs', + 'hidden-sm', + 'hidden-md', + 'hidden-lg', +]; + +const message = 'bootstrap 3 class are deprecated'; + +module.exports = { + meta: { + docs: { + description: 'Check if any bootstrap class is used inside a className call', + category: 'Build', + recommended: false, + }, + fixable: null, + schema: {}, + messages: { + useBootstrapClass: "'{{className}}' should not be used", + }, + }, + + create: function create(context) { + let classNameName; + return { + ImportDeclaration: function (node) { + if (node.source.value === 'classnames') { + const spec = node.specifiers.find(s => s.type === 'ImportDefaultSpecifier'); + if (spec) { + classNameName = spec.local.name; + } + } + }, + CallExpression: function (node) { + if (!classNameName) { + return; + } + if (node.callee?.name === classNameName) { + node.arguments.forEach(value => { + if (value.type === 'Literal') { + const values = value.value.split(' '); + if (values.some(v => BOOTSTRAP_CLASS.includes(v))) { + context.report({ + node: value, + message, + }); + } + } else if (value.type === 'ObjectExpression') { + value.properties.forEach(props => { + if (BOOTSTRAP_CLASS.includes(props.key?.value)) { + context.report({ + node: props.key, + message, + }); + } + }); + } + }); + } + }, + JSXAttribute: function (node) { + if (node.value?.type === 'Literal') { + const values = node.value.value.split(' '); + if (values.some(v => BOOTSTRAP_CLASS.includes(v))) { + context.report({ + node, + message, + }); + } + } + }, + }; + }, +}; diff --git a/tools/eslint-plugin/tests/src/rules/use-bootstrap-class.test.js b/tools/eslint-plugin/tests/src/rules/use-bootstrap-class.test.js new file mode 100644 index 00000000000..77085a74f08 --- /dev/null +++ b/tools/eslint-plugin/tests/src/rules/use-bootstrap-class.test.js @@ -0,0 +1,69 @@ +/** + * @fileoverview Check if the import of d3 is not on d3-* + * @author Jean-Michel FRANCOIS + */ + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require('../../../src/rules/use-bootstrap-class'); +const RuleTester = require('eslint').RuleTester; +const parser = require.resolve('@babel/eslint-parser'); +const parserOptions = { + babelOptions: { + configFile: require.resolve('@talend/scripts-config-babel'), + }, +}; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +describe('test', () => {}); +const ruleTester = new RuleTester(); +ruleTester.run('talend-use-bootstrap-class', rule, { + valid: [ + { + code: `import classnames from 'classnames'; + classnames('foo', { 'bar': true })`, + parser, + parserOptions, + }, + ], + + invalid: [ + { + code: `import classnames from 'classnames'; + classnames('foo', 'btn')`, + parser, + parserOptions, + errors: [ + { + message: 'bootstrap 3 class are deprecated', + }, + ], + }, + { + code: `import classnames from 'classnames'; + classnames('foo', { 'btn-default': true })`, + parser, + parserOptions, + errors: [ + { + message: 'bootstrap 3 class are deprecated', + }, + ], + }, + { + code: ``, + parser, + parserOptions, + errors: [ + { + message: 'bootstrap 3 class are deprecated', + }, + ], + }, + ], +}); diff --git a/tools/scripts-config-eslint/.eslintrc.json b/tools/scripts-config-eslint/.eslintrc.json index d55f52c2186..4a92186602a 100644 --- a/tools/scripts-config-eslint/.eslintrc.json +++ b/tools/scripts-config-eslint/.eslintrc.json @@ -35,6 +35,7 @@ ], "rules": { "@talend/import-depth": 2, + "@talend/use-bootstrap-class": 1, "arrow-parens": [2, "as-needed"], "comma-dangle": ["error", "only-multiline"], "function-paren-newline": 0,