ESLint plugin that identifies and automatically removes unused ES6 module imports
npx @tessl/cli install tessl/npm-eslint-plugin-unused-imports@4.2.0ESLint Plugin Unused Imports identifies and automatically removes unused ES6 module imports from JavaScript and TypeScript codebases. It extends the standard no-unused-vars rule to specifically handle import statements in the Abstract Syntax Tree (AST), providing autofix capabilities to clean up unnecessary imports.
npm install eslint-plugin-unused-imports --save-devimport unusedImports from "eslint-plugin-unused-imports";For CommonJS:
const unusedImports = require("eslint-plugin-unused-imports");import unusedImports from "eslint-plugin-unused-imports";
export default [{
plugins: {
"unused-imports": unusedImports,
},
rules: {
"no-unused-vars": "off", // or "@typescript-eslint/no-unused-vars": "off",
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": [
"warn",
{
"vars": "all",
"varsIgnorePattern": "^_",
"args": "after-used",
"argsIgnorePattern": "^_",
},
]
}
}];The plugin uses a composable architecture built around several key components:
Main ESLint plugin export containing the two rules.
interface ESLintPlugin {
meta: {
name: string;
};
rules: {
"no-unused-vars": Rule.RuleModule;
"no-unused-imports": Rule.RuleModule;
};
}
const plugin: ESLintPlugin;
export default plugin;Detects and autofixes unused import statements with comprehensive autofix support.
/**
* ESLint rule that identifies unused import statements and provides autofix
* Handles various import scenarios including single imports, multiple imports,
* default + named combinations, and proper formatting preservation
*/
const noUnusedImportsRule: Rule.RuleModule;Rule Configuration:
"unused-imports/no-unused-imports": "error" to enable with autofixAutofix Examples:
// Single unused import - entire statement removed
import { unused } from 'module'; // ❌ Removed completely
// Multiple imports with some unused - only unused ones removed
import { used, unused1, unused2 } from 'module';
// ⬇️ Becomes:
import { used } from 'module';
// Default + named imports with unused named import
import React, { useState, unused } from 'react';
// ⬇️ Becomes:
import React, { useState } from 'react';
// All imports unused - entire statement removed with proper newline handling
import { unused1, unused2 } from 'module';
console.log('next line');
// ⬇️ Becomes:
console.log('next line'); // Proper spacing preservedEnhanced version of standard no-unused-vars rule that excludes imports to prevent duplicate reporting.
/**
* Enhanced no-unused-vars rule that filters out import-related problems
* Prevents duplicate reporting when used with no-unused-imports rule
* Supports all standard no-unused-vars configuration options
*/
const noUnusedVarsRule: Rule.RuleModule;Core system for creating filtered rules with predicate-based problem filtering.
/**
* Predicate function type for filtering rule problems
* Returns the problem to report or false to suppress it
*/
type Predicate = (
problem: Rule.ReportDescriptor,
context: Rule.RuleContext,
) => Rule.ReportDescriptor | false;
/**
* Creates a new rule by applying a predicate filter to a base rule
* Used to create both no-unused-imports and no-unused-vars rules
*/
function createRuleWithPredicate(
name: string,
baseRule: Rule.RuleModule,
predicate: Predicate,
): Rule.RuleModule;
/**
* Predicate that filters for unused import statements (includes autofix)
*/
const unusedImportsPredicate: Predicate;
/**
* Predicate that filters out import statements (excludes imports)
*/
const unusedVarsPredicate: Predicate;Predicate System Example:
// How predicates filter problems from the base no-unused-vars rule
const problem = {
node: someASTNode,
message: "someVariable is defined but never used",
// ... other problem properties
};
// unusedImportsPredicate: Returns problem ONLY if it's an import
// - Checks if problem.node.parent matches /^Import(|Default|Namespace)Specifier$/
// - If import: adds autofix logic and returns enhanced problem
// - If not import: returns false (suppresses the problem)
// unusedVarsPredicate: Returns problem ONLY if it's NOT an import
// - Checks if problem.node.parent matches /^Import(|Default|Namespace)Specifier$/
// - If import: returns false (suppresses the problem)
// - If not import: returns problem unchangedDynamic rule loading system that selects the appropriate no-unused-vars implementation.
/**
* Gets the appropriate base no-unused-vars rule using fallback strategy:
* 1. @typescript-eslint/eslint-plugin (if available)
* 2. typescript-eslint unified plugin (if available)
* 3. ESLint core no-unused-vars rule (fallback)
*/
function getBaseRule(): Rule.RuleModule;
/**
* Attempts to load no-unused-vars from @typescript-eslint/eslint-plugin
* Returns null if package is not available
*/
function getRuleFromTSLintPlugin(): Rule.RuleModule | null;
/**
* Attempts to load no-unused-vars from typescript-eslint unified package
* Returns null if package is not available
*/
function getRuleFromTSLint(): Rule.RuleModule | null;
/**
* Loads the core ESLint no-unused-vars rule as fallback
* Always succeeds as ESLint is a required dependency
*/
function getESLintBaseRule(): Rule.RuleModule;Rule Configuration: Standard ESLint no-unused-vars options:
"unused-imports/no-unused-vars": [
"warn",
{
"vars": "all" | "local",
"varsIgnorePattern": string, // regex pattern
"args": "after-used" | "all" | "none",
"argsIgnorePattern": string, // regex pattern
"caughtErrors": "none" | "all",
"caughtErrorsIgnorePattern": string, // regex pattern
"destructuredArrayIgnorePattern": string, // regex pattern
"ignoreRestSiblings": boolean
}
]/**
* ESLint Rule Module interface for both plugin rules
*/
interface Rule.RuleModule {
meta: {
type?: "problem" | "suggestion" | "layout";
docs?: {
description?: string;
category?: string;
recommended?: boolean;
url?: string;
};
fixable?: "code" | "whitespace";
schema: any[];
messages: Record<string, string>;
};
create(context: Rule.RuleContext): Rule.RuleListener;
}
/**
* ESLint Rule Context provided to rule implementations
*/
interface Rule.RuleContext {
id: string;
options: any[];
settings: any;
parserPath: string;
parserOptions: any;
sourceCode: SourceCode;
report(descriptor: Rule.ReportDescriptor): void;
}
/**
* ESLint Report Descriptor for rule violations
*/
interface Rule.ReportDescriptor {
node?: any;
loc?: any;
message: string;
data?: any;
fix?: (fixer: Rule.RuleFixer) => Rule.Fix | Rule.Fix[];
}Peer Dependencies:
eslint: ^8.0.0 || ^9.0.0@typescript-eslint/eslint-plugin: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 (optional)Version Compatibility:
Framework Integration:
eslint-plugin-react with react/jsx-uses-react and react/jsx-uses-vars rules enabled@typescript-eslint/eslint-plugin and @typescript-eslint/parser for TypeScript projects