From 3be04626fdd4ddbc8cd00039c075b556d1baf0e6 Mon Sep 17 00:00:00 2001 From: Weronika Olejniczak Date: Tue, 10 Dec 2024 19:15:48 +0100 Subject: [PATCH] feat(eslint-plugin): add no-restricted-eui-imports rule --- packages/eslint-plugin/README.md | 18 ++++-- packages/eslint-plugin/index.js | 2 + .../rules/no_restricted_eui_imports.js | 62 +++++++++++++++++++ .../rules/no_restricted_eui_imports.test.js | 50 +++++++++++++++ 4 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 packages/eslint-plugin/rules/no_restricted_eui_imports.js create mode 100644 packages/eslint-plugin/rules/no_restricted_eui_imports.test.js diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 0c1b45ab495..42bf0ddfa87 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -4,8 +4,8 @@ This package contains an eslint plugin that enforces some default rules for usin ## Setup -1. install `@elastic/eslint-plugin-eui` as a dev dependency -2. extend `plugin:@elastic/eui/recommended` in your eslint config +1. Install `@elastic/eslint-plugin-eui` as a dev dependency. +2. Extend `plugin:@elastic/eui/recommended` in your eslint config. ## Rules @@ -15,11 +15,19 @@ This package contains an eslint plugin that enforces some default rules for usin In some cases it makes sense to disable this rule locally, such as when cmd+click should open the link in a new tab, but a standard click should use the `history.pushState()` API to change the URL without triggering a full page load. +### `@elastic/eui/no-restricted-eui-imports` + +At times, we deprecate features that may need more highlighting and/or that are not possible to annotate with JSDoc `@deprecated`, e.g. JSON token imports: `@elastic/eui/dist/eui_theme_*.json` (for context: https://github.com/elastic/kibana/issues/199715#json-tokens). + +We don't use `no-restricted-imports` because ESLint doesn't allow multiple error levels at once and it may conflict with the consumer's existing ESLint configuration for that rule. We need to assure that our rule will produce a warning (as a recommendation). + +All deprecations still must follow our [deprecation process](../../wiki/eui-team-processes/deprecations.md). + ## Publishing This package is published separately from the rest of EUI, as required by eslint. The code is not transpiled, so make sure to use `require()` statements rather than `import`, and once the code is updated run: 1. `npm version patch|minor|major` -2. commit version bump -3. `npm publish` in this directory -4. push the version bump upstream +2. Commit version bump. +3. `npm publish` in this directory. +4. Push the version bump upstream. diff --git a/packages/eslint-plugin/index.js b/packages/eslint-plugin/index.js index 9f0f55cc85f..4f43ddcc2cb 100644 --- a/packages/eslint-plugin/index.js +++ b/packages/eslint-plugin/index.js @@ -20,12 +20,14 @@ module.exports = { rules: { 'href-or-on-click': require('./rules/href_or_on_click'), + 'no-restricted-eui-imports': require('./rules/no_restricted_eui_imports'), }, configs: { recommended: { plugins: ['@elastic/eslint-plugin-eui'], rules: { '@elastic/eui/href-or-on-click': 'warn', + '@elastic/eui/no-restricted-eui-imports': 'warn', }, }, }, diff --git a/packages/eslint-plugin/rules/no_restricted_eui_imports.js b/packages/eslint-plugin/rules/no_restricted_eui_imports.js new file mode 100644 index 00000000000..af21085d42f --- /dev/null +++ b/packages/eslint-plugin/rules/no_restricted_eui_imports.js @@ -0,0 +1,62 @@ +const DEFAULT_RESTRICTED_IMPORT_PATTERNS = [ + { + pattern: '@elastic/eui/dist/eui_theme_*.json', + message: + 'Please use `useEuiTheme` instead. Direct JSON token imports will be removed as per the EUI Deprecation schedule: https://github.com/elastic/eui/issues/1469.', + }, +]; + +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'Disallow deprecated EUI imports.', + category: 'Possible Errors', + recommended: false, + }, + schema: [ + { + type: 'object', + properties: { + patterns: { + type: 'array', + items: { + type: 'object', + properties: { + pattern: { type: 'string' }, + message: { type: 'string' }, + }, + required: ['pattern'], + additionalProperties: false, + }, + uniqueItems: true, + }, + }, + additionalProperties: false, + }, + ], + }, + + create(context) { + const options = context.options[0] || {}; + const userPatterns = options.patterns || []; + + // Combine the default patterns with the user-defined patterns + const allPatterns = [DEFAULT_RESTRICTED_IMPORT_PATTERNS, ...userPatterns]; + + return { + ImportDeclaration(node) { + allPatterns.forEach(({ pattern, message }) => { + const regex = new RegExp(pattern.replace('*', '.*')); + if (regex.test(node.source.value)) { + context.report({ + node, + message: + message || `Importing "${node.source.value}" is restricted.`, + }); + } + }); + }, + }; + }, +}; diff --git a/packages/eslint-plugin/rules/no_restricted_eui_imports.test.js b/packages/eslint-plugin/rules/no_restricted_eui_imports.test.js new file mode 100644 index 00000000000..86ec207756c --- /dev/null +++ b/packages/eslint-plugin/rules/no_restricted_eui_imports.test.js @@ -0,0 +1,50 @@ +const { RuleTester } = require('eslint'); +const rule = require('./no_restricted_eui_imports'); + +const ruleTester = new RuleTester({ + parser: require.resolve('babel-eslint'), + parserOptions: { + ecmaVersion: 2018, + }, +}); + +ruleTester.run('@elastic/eui/no-restricted-eui-imports', rule, { + valid: [ + { + code: "import { EuiButton } from '@elastic/eui';", + }, + { + code: "import theme from '@kbn/ui-theme';", + }, + ], + + invalid: [ + { + code: "import theme from '@elastic/eui/dist/eui_theme_light.json';", + errors: [ + { + message: + 'Please use `useEuiTheme` instead. Direct JSON token imports will be removed as per the EUI Deprecation schedule: https://github.com/elastic/eui/issues/1469.', + }, + ], + }, + { + code: "import theme from '@kbn/ui-theme';", + options: [ + { + patterns: [ + { + pattern: '@kbn/ui-theme', + message: 'Please use `useEuiTheme` from `@elastic/eui` instead.', + }, + ], + }, + ], + errors: [ + { + message: 'Please use `useEuiTheme` from `@elastic/eui` instead.', + }, + ], + }, + ], +});