eslint-plugin-import-x
Advanced tools
Changelog
4.6.0
#209 46d2360
Thanks @SukkaW! - When eslint-plugin-import-x
was forked from eslint-plugin-import
, we copied over the default resolver (which is eslint-import-resolver-node
) as well. However, this resolver doesn't supports exports
in the package.json
file, and the current maintainer of the eslint-import-resolver-node
(ljharb) doesn't have the time implementing this feature and he locked the issue https://github.com/import-js/eslint-plugin-import/issues/1810.
So we decided to implement our own resolver that "just works". The new resolver is built upon the enhanced-resolve
that implements the full Node.js Resolver Algorithm. The new resolver only implements the import resolver interface v3, which means you can only use it with ESLint Flat config. For more details about the import resolver interface v3, please check out #192.
In the next major version of eslint-plugin-import-x
, we will remove the eslint-import-resolver-node
and use this new resolver by default. In the meantime, you can try out this new resolver by setting the import-x/resolver-next
option in your eslint.config.js
file:
// eslint.config.js
const eslintPluginImportX = require('eslint-plugin-import-x');
const { createNodeResolver } = eslintPluginImportX;
module.exports = {
plugins: {
'import-x': eslintPluginImportX,
},
settings: {
'import-x/resolver-next': [
// This is the new resolver we are introducing
createNodeResolver({
/**
* The allowed extensions the resolver will attempt to find when resolving a module
* By default it uses a relaxed extension list to search for both ESM and CJS modules
* You can customize this list to fit your needs
*
* @default ['.mjs', '.cjs', '.js', '.json', '.node']
*/
extensions?: string[];
/**
* Optional, the import conditions the resolver will used when reading the exports map from "package.json"
* By default it uses a relaxed condition list to search for both ESM and CJS modules
* You can customize this list to fit your needs
*
* @default ['default', 'module', 'import', 'require']
*/
conditions: ['default', 'module', 'import', 'require'],
// You can pass more options here, see the enhanced-resolve documentation for more details
// https://github.com/webpack/enhanced-resolve/tree/v5.17.1?tab=readme-ov-file#resolver-options
}),
// you can add more resolvers down below
require('eslint-import-resolver-typescript').createTypeScriptImportResolver(
/** options of eslint-import-resolver-typescript */
)
],
},
};
We do not plan to implement reading baseUrl
and paths
from the tsconfig.json
file in this resolver. If you need this feature, please checkout eslint-import-resolver-typescript (also powered by enhanced-resolve
), eslint-import-resolver-oxc (powered by oxc-resolver
), eslint-import-resolver-next (also powered by oxc-resolver
), or other similar resolvers.
449738f
Thanks @privatenumber! - insert type prefix without new lineChangelog
4.5.0
#192 fbf639b
Thanks @SukkaW! - The PR implements the new resolver design proposed in https://github.com/un-ts/eslint-plugin-import-x/issues/40#issuecomment-2381444266
eslint-plugin-import-x
usersLike the ESLint flat config allows you to use js objects (e.g. import and require) as ESLint plugins, the new eslint-plugin-import-x
resolver settings allow you to use js objects as custom resolvers through the new setting import-x/resolver-next
:
// eslint.config.js
import { createTsResolver } from '#custom-resolver';
const { createOxcResolver } = require('path/to/a/custom/resolver');
const resolverInstance = new ResolverFactory({});
const customResolverObject = {
interfaceVersion: 3,
name: 'my-custom-eslint-import-resolver',
resolve(modPath, sourcePath) {
const path = resolverInstance.resolve(modPath, sourcePath);
if (path) {
return {
found: true,
path
};
}
return {
found: false,
path: null
}
};
};
module.exports = {
settings: {
// multiple resolvers
'import-x/resolver-next': [
customResolverObject,
createTsResolver(enhancedResolverOptions),
createOxcResolver(oxcOptions),
],
// single resolver:
'import-x/resolver-next': [createOxcResolver(oxcOptions)]
}
}
The new import-x/resolver-next
no longer accepts strings as the resolver, thus will not be compatible with the ESLint legacy config (a.k.a. .eslintrc
). Those who are still using the ESLint legacy config should stick with import-x/resolver
.
In the next major version of eslint-plugin-import-x
(v5), we will rename the currently existing import-x/resolver
to import-x/resolver-legacy
(which allows the existing ESLint legacy config users to use their existing resolver settings), and import-x/resolver-next
will become the new import-x/resolver
. When ESLint v9 (the last ESLint version with ESLint legacy config support) reaches EOL in the future, we will remove import-x/resolver-legacy
.
We have also made a few breaking changes to the new resolver API design, so you can't use existing custom resolvers directly with import-x/resolver-next
:
// When migrating to `import-x/resolver-next`, you CAN'T use legacy versions of resolvers directly:
module.exports = {
settings: {
// THIS WON'T WORK, the resolver interface required for `import-x/resolver-next` is different.
'import-x/resolver-next': [
require('eslint-import-resolver-node'),
require('eslint-import-resolver-webpack'),
require('some-custom-resolver')
];
}
}
For easier migration, the PR also introduces a compat utility importXResolverCompat
that you can use in your eslint.config.js
:
// eslint.config.js
import eslintPluginImportX, { importXResolverCompat } from 'eslint-plugin-import-x';
// or
const eslintPluginImportX = require('eslint-plugin-import-x');
const { importXResolverCompat } = eslintPluginImportX;
module.exports = {
settings: {
// THIS WILL WORK as you have wrapped the previous version of resolvers with the `importXResolverCompat`
'import-x/resolver-next': [
importXResolverCompat(require('eslint-import-resolver-node'), nodeResolveOptions),
importXResolverCompat(require('eslint-import-resolver-webpack'), webpackResolveOptions),
importXResolverCompat(require('some-custom-resolver'), { option1: true, option2: '' })
];
}
}
This is the new API design of the resolver interface:
export interface NewResolver {
interfaceVersion: 3;
name?: string; // This will be included in the debug log
resolve: (modulePath: string, sourceFile: string) => ResolvedResult;
}
// The `ResultNotFound` (returned when not resolved) is the same, no changes
export interface ResultNotFound {
found: false;
path?: undefined;
}
// The `ResultFound` (returned resolve result) is also the same, no changes
export interface ResultFound {
found: true;
path: string | null;
}
export type ResolvedResult = ResultNotFound | ResultFound;
You will be able to import NewResolver
from eslint-plugin-import-x/types
.
The most notable change is that eslint-plugin-import-x
no longer passes the third argument (options
) to the resolve
function.
We encourage custom resolvers' authors to consume the options outside the actual resolve
function implementation. You can export a factory function to accept the options, this factory function will then be called inside the eslint.config.js
to get the actual resolver:
// custom-resolver.js
exports.createCustomResolver = (options) => {
// The options are consumed outside the `resolve` function.
const resolverInstance = new ResolverFactory(options);
return {
name: 'custom-resolver',
interfaceVersion: 3,
resolve(mod, source) {
const found = resolverInstance.resolve(mod, {});
// Of course, you still have access to the `options` variable here inside
// the `resolve` function. That's the power of JavaScript Closures~
}
}
};
// eslint.config.js
const { createCustomResolver } = require('custom-resolver')
module.exports = {
settings: {
'import-x/resolver-next': [
createCustomResolver(options)
];
}
}
This allows you to create a reusable resolver instance to improve the performance. With the existing version of the resolver interface, because the options are passed to the resolver
function, you will have to create a resolver instance every time the resolve
function is called:
module.exports = {
interfaceVersion: 2,
resolve(mod, source) {
// every time the `resolve` function is called, a new instance is created
// This is very slow
const resolverInstance = ResolverFactory.createResolver({});
const found = resolverInstance.resolve(mod, {});
},
};
With the factory function pattern, you can create a resolver instance beforehand:
exports.createCustomResolver = (options) => {
// `enhance-resolve` allows you to create a reusable instance:
const resolverInstance = ResolverFactory.createResolver({});
const resolverInstance = enhanceResolve.create({});
// `oxc-resolver` also allows you to create a reusable instance:
const resolverInstance = new ResolverFactory({});
return {
name: "custom-resolver",
interfaceVersion: 3,
resolve(mod, source) {
// the same re-usable instance is shared across `resolve` invocations.
// more performant
const found = resolverInstance.resolve(mod, {});
},
};
};
#184 bc4de89
Thanks @marcalexiei! - fix(no-cycle): improves the type declaration of the rule no-cycle
’s maxDepth
option
#184 bc4de89
Thanks @marcalexiei! - fix(first): improves the type declaration of the rule first
's option
#184 bc4de89
Thanks @marcalexiei! - fix(no-unused-modules): improves the type declaration of the rule no-unused-modules
’s missingExports
option
#184 bc4de89
Thanks @marcalexiei! - fix(no-deprecated): improve error message when no description is available
Changelog
4.4.0
9c58269
Thanks @teidesu! - Add new rule option checkTypedImports
for extensions
, backports https://github.com/import-js/eslint-plugin-import/pull/2817#171 9715220
Thanks @SukkaW! - Perf: avoid regexp during parser choosing
#171 9715220
Thanks @SukkaW! - Add extra guard for rule no-named-as-default
. A few guards are borrowed from https://github.com/import-js/eslint-plugin-import/pull/3032, but we don't sync the rest of changes from upstream since we have already implemented a way more performant check.
#171 9715220
Thanks @SukkaW! - More test cases for no-named-export
and no-defualt-export
rule specifically with non-module sourceType
#171 9715220
Thanks @SukkaW! - Fix export
when there is only one TSDeclareFunction
(https://github.com/import-js/eslint-plugin-import/pull/3065)
#171 9715220
Thanks @SukkaW! - Prevent ExportMap
's cache is being tainted by incompatible parser (e.g. old babel-eslint
). The cache is now skipped w/ incompatible parsers, which might introduce performance impacts only for those who are using incompatible parsers. (https://github.com/import-js/eslint-plugin-import/pull/3062)
#171 9715220
Thanks @SukkaW! - Docs: fix a few typos here and there
#168 5de039c
Thanks @hyoban! - Fixes https://github.com/un-ts/eslint-plugin-import-x/issues/167, the no-duplicates
rule now allows co-existing inline type imports and namespace imports.
#171 9715220
Thanks @SukkaW! - Properly fix espree parser w/ ESLint Flat Config
Changelog
4.3.1
38d0081
Thanks @AaronMoat! - Fix issue where no-duplicates
rule with prefer-inline
incorrectly marks default type and named type imports as duplicatesChangelog
4.3.0
4da5388
Thanks @GoodbyeNJN! - feat: add support for using resolver object directly in settingsChangelog
4.2.1
#148 d228129
Thanks @SukkaW! - Fix newline-after-import
's considerComments
options when linting require
, backports https://github.com/import-js/eslint-plugin-import/pull/2952
#147 eca73ed
Thanks @nchevsky! - Fix regression in rule no-unused-modules
which would incorrectly initialize option src
to []
instead of [process.cwd()]
, breaking file discovery.
#148 d228129
Thanks @SukkaW! - Fix no-duplicates
for TypeScript, backports https://github.com/import-js/eslint-plugin-import/pull/3033