diff --git a/.eslintrc.js b/.eslintrc.js index 98febd5adcc..83f92e71c3e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,3 +1,5 @@ +const path = require('path'); + module.exports = { root: true, parser: '@typescript-eslint/parser', @@ -9,7 +11,15 @@ module.exports = { jsx: true, }, }, - plugins: ['import', '@typescript-eslint', 'react-hooks', 'jest', 'chai-friendly', 'react'], + plugins: [ + 'import', + '@typescript-eslint', + 'react-hooks', + 'jest', + 'chai-friendly', + 'react', + 'local-rules', + ], extends: [ 'plugin:@typescript-eslint/recommended', 'plugin:react/recommended', @@ -21,6 +31,11 @@ module.exports = { version: 'detect', }, 'import/ignore': ['node_modules', '\\.(coffee|scss|css|less|hbs|svg|json)$'], + 'import/resolver': { + node: { + paths: [path.resolve(__dirname, 'eslint-rules')], + }, + }, }, env: { jest: true, @@ -40,6 +55,7 @@ module.exports = { 'packages/address-validator', 'packages/connect-examples', 'ci/', + 'eslint-local-rules/*', ], rules: { '@typescript-eslint/prefer-ts-expect-error': 'error', @@ -279,6 +295,7 @@ module.exports = { 'error', { blankLine: 'always', prev: '*', next: 'return' }, ], + 'local-rules/no-override-ds-component': ['warn', { packageName: '@trezor/components' }], }, overrides: [ { diff --git a/eslint-local-rules/index.js b/eslint-local-rules/index.js new file mode 100644 index 00000000000..a29608dd3ab --- /dev/null +++ b/eslint-local-rules/index.js @@ -0,0 +1,8 @@ +require('ts-node').register({ + transpileOnly: true, + compilerOptions: { + module: 'commonjs', + }, +}); + +module.exports = require('./rules').default; diff --git a/eslint-local-rules/rules.ts b/eslint-local-rules/rules.ts new file mode 100644 index 00000000000..c3b0d811265 --- /dev/null +++ b/eslint-local-rules/rules.ts @@ -0,0 +1,70 @@ +import type { Rule } from 'eslint'; + +export default { + 'no-override-ds-component': { + meta: { + type: 'problem', + docs: { + description: + 'Disallows overriding components imported from a specific package using styled-components', + + category: 'Best Practices', + recommended: false, + }, + messages: { + avoidStyledComponent: + "Please do not override components imported from '{{packageName}}'. Use wrapper component or ask Usability team for help.", + }, + schema: [ + { + type: 'object', + properties: { + packageName: { + type: 'string', + }, + }, + additionalProperties: false, + }, + ], + }, + create(context) { + const packageName = context.options[0] && context.options[0].packageName; + if (!packageName) { + return {}; + } + + const importedComponents = new Set(); + + return { + ImportDeclaration(node) { + if (node.source.value === packageName) { + node.specifiers.forEach(specifier => { + if ( + specifier.type === 'ImportSpecifier' || + specifier.type === 'ImportDefaultSpecifier' + ) { + importedComponents.add(specifier.local.name); + } + }); + } + }, + TaggedTemplateExpression(node) { + if ( + node.tag.type === 'CallExpression' && + node.tag.callee.name === 'styled' && + node.tag.arguments[0].type === 'Identifier' && + importedComponents.has(node.tag.arguments[0].name) + ) { + context.report({ + node, + messageId: 'avoidStyledComponent', + data: { + packageName, + }, + }); + } + }, + }; + }, + }, +} satisfies Record; diff --git a/package.json b/package.json index 5d3d1f8b1b4..5aa3ef5bee8 100644 --- a/package.json +++ b/package.json @@ -137,6 +137,7 @@ "eslint-plugin-import": "^2.29.1", "eslint-plugin-jest": "^27.6.3", "eslint-plugin-jsx-a11y": "^6.8.0", + "eslint-plugin-local-rules": "^3.0.2", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "jest": "29.7.0", diff --git a/yarn.lock b/yarn.lock index 073b0243290..b340de84b0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20936,6 +20936,13 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-local-rules@npm:^3.0.2": + version: 3.0.2 + resolution: "eslint-plugin-local-rules@npm:3.0.2" + checksum: 10/6ec5a9e6d5a0a8f56ed22307761d1bc2bebee75ebfc9518e0eeed0d334c45c84713aff1969eefa5ac0fdeca63e427fee53b3912333caa9e70cc1f87e4c198f11 + languageName: node + linkType: hard + "eslint-plugin-markdown@npm:^3.0.1": version: 3.0.1 resolution: "eslint-plugin-markdown@npm:3.0.1" @@ -39703,6 +39710,7 @@ __metadata: eslint-plugin-import: "npm:^2.29.1" eslint-plugin-jest: "npm:^27.6.3" eslint-plugin-jsx-a11y: "npm:^6.8.0" + eslint-plugin-local-rules: "npm:^3.0.2" eslint-plugin-react: "npm:^7.33.2" eslint-plugin-react-hooks: "npm:^4.6.0" jest: "npm:29.7.0"