no-extraneous-dependencies
Forbid the use of extraneous packages not listed in package.json
š¼ This rule is enabled in the following configs: recommended, typescript.
š” This rule is manually fixable by editor suggestions.
Rule Details
Reports a runtime import of a package the nearest manifest does not declare. Such an import works only by accident (a hoisted transitive dependency) and breaks for anyone who installs the package.
The manifest is the nearest package.json that declares a package (a name
or any dependency field); a nameless {"type":"module"} scope marker in a
sub-folder is skipped. packageJsonPath or packageJson replace the lookup.
A package is allowed when it is listed in dependencies, or in
devDependencies, optionalDependencies, peerDependencies,
bundledDependencies / bundleDependencies (npm accepts both spellings)
while the matching option is true (the default). With
devDependencies: false, a package listed only in devDependencies is
reported as a dev dependency used in production. The package's own name is
always allowed (Node self-reference).
Never checked: Node builtins (fs/promises, node:fs), relative and
absolute paths, #imports, URL schemes (virtual:x), and names matched by
ignore (exact) or allowPatterns (regular expressions).
Type-only imports are erased at compile time and never reported:
import type, export type ... from, import type x = require(), and an
import or re-export whose every specifier is an inline type, unless
verbatimModuleSyntax is true, under which tsc keeps it as
import {} from 'pkg'.
Every runtime spelling is checked: import, side-effect imports,
export { a } from, export * from, export * as ns from, require(),
import() and TS import x = require().
resolutionStrategy: 'workspace' (alias 'monorepo') additionally allows the
literal @workspace/ and @company/ scopes and reads no other manifest; for
your own workspace scopes use allowPatterns.
{ "name": "app", "dependencies": { "react": "^19.0.0" } }// ā Incorrect
import axios from 'axios';
export * from 'lodash';
// ā
Correct
import React from 'react';
import type { Schema } from 'zod';
import { readFile } from 'node:fs/promises';
import { feature } from 'app/feature';Options
[
{
"type": "object",
"properties": {
"devDependencies": {
"type": "boolean",
"default": true,
"description": "Allow imports from devDependencies."
},
"optionalDependencies": {
"type": "boolean",
"default": true,
"description": "Allow imports from optionalDependencies."
},
"peerDependencies": {
"type": "boolean",
"default": true,
"description": "Allow imports from peerDependencies."
},
"bundledDependencies": {
"type": "boolean",
"default": true,
"description": "Allow imports from bundledDependencies."
},
"packageJsonPath": {
"type": "string",
"description": "Path to package.json file to use."
},
"packageJson": {
"type": "object",
"description": "Direct package.json content for testing."
},
"resolutionStrategy": {
"type": "string",
"enum": [
"strict",
"workspace",
"monorepo"
],
"default": "strict",
"description": "Dependency resolution strategy. 'strict' allows only packages declared in the nearest package.json. 'workspace' and 'monorepo' are aliases of each other: they additionally allow the literal '@workspace/' and '@company/' scopes, and do NOT read workspace-root or sibling manifests; for real monorepo scopes use allowPatterns."
},
"allowPatterns": {
"type": "array",
"items": {
"type": "string"
},
"description": "Regex patterns for packages to allow even if not in package.json."
},
"ignore": {
"type": "array",
"items": {
"type": "string"
},
"description": "Specific package names to ignore (don't report as missing)."
},
"verbatimModuleSyntax": {
"type": "boolean",
"default": false,
"description": "Check inline-type-only imports (`import { type X } from 'pkg'`) as runtime imports. Enable when the project compiles with TypeScript's `verbatimModuleSyntax`, under which tsc emits `import {} from 'pkg'` for them. Statement-level `import type` is always ignored."
}
},
"additionalProperties": false
}
]Implementation
OWASP Foundation
- Category: A00:2021 - General Security
Use this rule
npm install -D oxlint-plugin-import-next{ "jsPlugins": ["oxlint-plugin-import-next"], "rules": { "import-next/no-extraneous-dependencies": "error" }}import { defineConfig } from 'vite-plus';export default defineConfig({ lint: { jsPlugins: ['oxlint-plugin-import-next'], rules: { 'import-next/no-extraneous-dependencies': 'error', }, },});oxlint-plugin-import-next re-exports eslint-plugin-import-next/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.
Did this rule catch something? Star the repo to get new CWE coverage as we ship it ā or follow the AI-code-security benchmarks behind these rules.