From c325629dac0c68a7f07313fef1c707f1ea3aab02 Mon Sep 17 00:00:00 2001 From: Sebastian Forman Date: Wed, 9 Jul 2025 12:47:03 +0200 Subject: [PATCH 1/2] feat(no-extraneous-dependencies): add exclude option to rule --- CHANGELOG.md | 3 ++ docs/rules/no-extraneous-dependencies.md | 15 ++++++++- src/rules/no-extraneous-dependencies.js | 17 ++++++++++ tests/src/rules/no-extraneous-dependencies.js | 31 +++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb12deb24d..492c9ef67e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## [Unreleased] +### Added +- add `exclude` option to `import/no-extraneous-dependencies` to allow excluding dependencies from rule. ([#2317], thanks [@sf0rman]) + ## [2.32.0] - 2025-06-20 ### Added diff --git a/docs/rules/no-extraneous-dependencies.md b/docs/rules/no-extraneous-dependencies.md index 848d5bb0da..ce9defb35d 100644 --- a/docs/rules/no-extraneous-dependencies.md +++ b/docs/rules/no-extraneous-dependencies.md @@ -20,6 +20,8 @@ Type imports are ignored by default. `bundledDependencies`: If set to `false`, then the rule will show an error when `bundledDependencies` are imported. Defaults to `true`. +`exclude`: If set, then the rule will exclude errors for the matched patterns. Defaults to `undefined`. + You can set the options like this: ```js @@ -57,6 +59,12 @@ folder layouts: "import/no-extraneous-dependencies": ["error", {"packageDir": ['./some-dir/', './root-pkg']}] ``` +You can also exclude errors for specific import paths to support packages that provide its components as nested dependencies. + +```js +"import/no-extraneous-dependencies": ["error", {"exclude": ['@scope/package']}] +``` + ## Rule Details Given the following `package.json`: @@ -69,7 +77,8 @@ Given the following `package.json`: "builtin-modules": "^1.1.1", "lodash.cond": "^4.2.0", "lodash.find": "^4.2.0", - "pkg-up": "^1.0.0" + "pkg-up": "^1.0.0", + "radix-ui": "^1.4.2", }, "devDependencies": { "ava": "^0.13.0", @@ -132,6 +141,10 @@ import type { MyType } from 'foo'; /* eslint import/no-extraneous-dependencies: ["error", {"peerDependencies": true}] */ import react from 'react'; + +/* eslint import/no-extraneous-dependencies: ["error", {"exclude": ['@radix-ui/react-*']}] */ +import { Alert } from "@radix-ui/react-alert-dialog"; +import { Button } from "@radix-ui/react-button"; ``` ## When Not To Use It diff --git a/src/rules/no-extraneous-dependencies.js b/src/rules/no-extraneous-dependencies.js index bf0a1ed477..a060714bee 100644 --- a/src/rules/no-extraneous-dependencies.js +++ b/src/rules/no-extraneous-dependencies.js @@ -177,6 +177,17 @@ function checkDependencyDeclaration(deps, packageName, declarationStatus) { }), newDeclarationStatus); } +function isInExcludeList(packageName, exclude) { + if (!exclude) { + return false; + } + + if (Array.isArray(exclude)) { + return exclude.some((pattern) => minimatch(packageName, pattern)); + } + return minimatch(packageName, exclude); +} + function reportIfMissing(context, deps, depsOptions, node, name) { // Do not report when importing types unless option is enabled if ( @@ -200,6 +211,10 @@ function reportIfMissing(context, deps, depsOptions, node, name) { return; } + if (isInExcludeList(name, depsOptions.exclude)) { + return; + } + const resolved = resolve(name, context); if (!resolved) { return; } @@ -277,6 +292,7 @@ module.exports = { packageDir: { type: ['string', 'array'] }, includeInternal: { type: ['boolean'] }, includeTypes: { type: ['boolean'] }, + exclude: { type: ['string', 'array'] }, }, additionalProperties: false, }, @@ -295,6 +311,7 @@ module.exports = { allowBundledDeps: testConfig(options.bundledDependencies, filename) !== false, verifyInternalDeps: !!options.includeInternal, verifyTypeImports: !!options.includeTypes, + exclude: options.exclude, }; return moduleVisitor((source, node) => { diff --git a/tests/src/rules/no-extraneous-dependencies.js b/tests/src/rules/no-extraneous-dependencies.js index 4a465eb39d..9b8b2e370b 100644 --- a/tests/src/rules/no-extraneous-dependencies.js +++ b/tests/src/rules/no-extraneous-dependencies.js @@ -188,6 +188,37 @@ ruleTester.run('no-extraneous-dependencies', rule, { }, }, }), + + test({ + code: `import "excluded-package";`, + options: [{ exclude: 'excluded-package' }], + }), + + test({ + code: ` + import "excluded-package"; + import x from "another-package"; + `, + options: [{ exclude: ['excluded-package', 'another-package'] }], + }), + + test({ + code: `import "@scope/excluded-package";`, + options: [{ exclude: '@scope/excluded-*' }], + }), + + test({ + code: `import { item } from "@scope/excluded-package";`, + options: [{ exclude: '@scope/excluded-*' }], + }), + + test({ + code: ` + import { item } from "@scope/some-package"; + import { a, b } from "@scope/another-package" + `, + options: [{ exclude: '@scope/*' }], + }), ], invalid: [ test({ From 369c265007de83412ec05ebfbbb036bce7140362 Mon Sep 17 00:00:00 2001 From: Sebastian Forman Date: Wed, 9 Jul 2025 13:00:46 +0200 Subject: [PATCH 2/2] chore: point to actual PR --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 492c9ef67e..82241fca07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## [Unreleased] ### Added -- add `exclude` option to `import/no-extraneous-dependencies` to allow excluding dependencies from rule. ([#2317], thanks [@sf0rman]) +- add `exclude` option to `import/no-extraneous-dependencies` to allow excluding dependencies from rule. ([#3198], thanks [@sf0rman]) ## [2.32.0] - 2025-06-20