Open JounQin opened 6 months ago
Why not:
{
resolver: "webpack",
resolverOptions: {/* resolver-specific options */},
}
I don't like this idea of resolver: 'webpack' | 'typescript'
.
Let's do resolver: require('@eslint-import/webpack')
and resolver: import('@eslint-import/webpack')
.
Let's do resolver: require('@eslint-import/webpack') and resolver: import('@eslint-import/webpack').
.eslintrc
won't work is this case? Maybe you mean resolver: '@eslint-import/webpack'
which is how eslint-plugin-import(-x)
work currently?
I don't quite want to support custom resolver actually, that's why I proposed webpack?: boolean
option, there should only a few resolvers for usage, ideally there should be no resolver concept except TypeScript, but webpack users may want to resolve webpack config automatically (I'm thinking if there are really eslint users using it nowadays because it does not work for webpack.cofig.ts
/webpack.config.mjs
, etc).
I don't quite want to support custom resolver actually, that's why I proposed
webpack?: boolean
option, there should only a few resolvers for usage, ideally there should be no resolver concept except TypeScript, but webpack users may want to resolve webpack config automatically (I'm thinking if there are really eslint users using it nowadays because it does not work forwebpack.cofig.ts
/webpack.config.mjs
, etc).
Since we have multiple resolvers, and each one of them will have its own unique set of options. Perhaps we could approach it like this instead:
{
webpack?: boolean | WebpackResolverOption | null
typescript?: boolean | TypeScriptResolverOption | null
node?: boolean | NodeResolverOption | null
}
Since we have multiple resolvers, and each one of them will have its own unique set of options. Perhaps we could approach it like this instead:
That's how it's working nowadays except it requires specific npm packages installed. 🥹
But if we are going to adopt using enhanced-resolve
instead, I think no node
resolver should remain because it should be the default behavior. Should'd it?
For resolver options, could there be any other options except enhanced-resolve
's?
I'm asking because I want to make a breaking change to drop support for previous resolvers instead of continuing.
I don't like this idea of
resolver: 'webpack' | 'typescript'
.Let's do
resolver: require('@eslint-import/webpack')
andresolver: import('@eslint-import/webpack')
.
That would work and be preferred in flat config format. Nonflat config has to support the package name as string.
For resolver options, could there be any other options except enhanced-resolve's?
Yeah, I want to do the same. There are many resolve libraries out there trying to mimic Node.js-like resolving algorithm:
I think the only modern resolution mechanisms one needs are these two (names based on tsconfig moduleResolution
):
node-16
: Node's resolution that requires file extensions. Likely should use import.meta.resolve and require.resolve, so should work without additional dependencies.bundler
: Similar to node, but does not require file extensions, maybe this could cover the webpack case as well.
node-16
: Node's resolution that requires file extensions. Likely should use import.meta.resolve and require.resolve, so should work without additional dependencies.
We will still need a library as long as we are publishing ESM/CJS dual packages and want to have a unified behavior across eslint.config.mjs
and eslint.config.cjs
.
Is CJS support still desired? CJS packages could just remain on using eslint-plugin-import
.
Non-flat configs load as CJS but if I recall correctly, import.meta.resolve
is also available there.
Non-flat configs load as CJS but if I recall correctly,
import.meta.resolve
is also available there.
Flat config supports both CJS and ESM, so there is no reason to make eslint-plugin-import-x
a pure ESM module.
Non-flat configs load as CJS but if I recall correctly,
import.meta.resolve
is also available there.Flat config supports both CJS and ESM, so there is no reason to make
eslint-plugin-import-x
a pure ESM module.
Ah, you are right. I wasn't aware that it's possible to support CJS in flat config as I head read this, so transpilation to CJS will be required if non-flat config is to be supported.
@JounQin Was reading this blog post: How we made Vite 4.3 faaaaster 🚀, and here I quote:
Vite 4.2 heavily depends on the resolve package to resolve the dependency's package.json, when we looked into the source code of resolve, there was much useless logic while resolving package.json. Vite 4.3 abandons resolve and follows the simpler resolve logic: directly checks whether package.json exists in the nested parents' directories.
IMHO should we build our own resolving algorithm as well?
Regarding typescript, it would be great if this could somehow piggyback off of typescript's own module resolution.
https://github.com/import-js/eslint-import-resolver-typescript mostly matches typescript, but I don't think it supports the project service, so you still have to tell it about all the relevant projects manually (in dependency order).
typescript-eslint does support the project service through EXPERIMENTAL_useProjectService
and they intend to stabilize it as projectService
for the next major release: https://github.com/typescript-eslint/typescript-eslint/pull/9084
eslint-import-resolver-vite
is used to resolve path alias that defined in vite.config.*
: https://github.com/pzmosquito/eslint-import-resolver-vite/issues/12
import-js/eslint-import-resolver-typescript mostly matches typescript, but I don't think it supports the project service, so you still have to tell it about all the relevant projects manually (in dependency order).
Here's a request for that: https://github.com/import-js/eslint-import-resolver-typescript/issues/282
So is there any official way currently to support projectService
? eslint-import-resolver-typescript
doesn't support it at the moment and I don't think so it will be implemented in short. Any alternative way that can make it work?
So is there any official way currently to support
projectService
?
eslint-import-resolver-typescript
performs isolated file parsing and deliberately disables project
and projectService
. Unless typescript-eslint
provides a document or a guide, it is impossible for a third-party library to use projectService
.
For anyone who wants to know, if you explicitly provide project
to the resolver configuration, then everything will still work, since it's not using projectService
at all. I didn't look into the code but I'm assuming it's trying to get the previous project
setting in the TSESlint configuration.
For anyone who wants to know, if you explicitly provide
project
to the resolver configuration, then everything will still work, since it's not usingprojectService
at all. I didn't look into the code but I'm assuming it's trying to get the previousproject
setting in the TSESlint configuration.
If you do so, the typescript-eslint will become 1.5x slower, see https://github.com/typescript-eslint/typescript-eslint/issues/8424
That's why project
and projectService
are deliberately disabled (as I mentioned) in eslint-plugin-import
and eslint-plugin-import-x
, see:
Yeah seems so, but currently I can't really find a way to make things work without specifying the project
option in eslint-import-resolver-typescript
, since the import/order
rule cannot recognise the paths
settings in tsconfig.json
.
I seems have some idea how the resolver works, but I'm not too sure. Anyway as a result, previously when I'm using the project
option for typescript-eslint
v7, it works all good, but once I switched to projectService
, it breaks.
I tried my best to read through those issues, but this is just a bit hard for someone have no insights to understand well😥.
At least for now I have a way to make it work, so I'm satisfied with it. I guess if one day typescript-eslint
provides the guide to take advantage of projectService
for third-party libs, things should be straight forward and easy to understand for everyone.
eslint-import-resolver-typescript
performs isolated file parsing and deliberately disablesproject
andprojectService
. Unlesstypescript-eslint
provides a document or a guide, it is impossible for a third-party library to useprojectService
.
Does this help at all? https://github.com/typescript-eslint/typescript-eslint/discussions/8030#discussioncomment-10675969
Has there been communications directly between eslint-plugin-import-x
and typescript-eslint
devs ?
I am going to share my ideas about new resolver implementations and settings:
// eslint.config.js
{
settings: {
// multiple resolvers
'import-x/resolver': [
nodeResolverObject,
createTsResolver(enhancedResolverOptions),
createOxcResolver(oxcOptions),
],
// single resolver:
'import-x/resolver': createOxcResolver(oxcOptions)
}
}
For the custom resolver author/maintainer, here is the signature of the resolver interface v3:
export interface ResolverV3<T = unknown, U = T> = {
interfaceVersion: 3,
name?: string, // This will only be included in the debug log
resolve: (modulePath: string, sourceFile: string) => ResolvedResult
resolveImport: (modulePath: string, sourceFile: string) => string | undefined,
}
// The `ResolvedResult` (returned resolved result) will remain the same
export type ResultNotFound = {
found: false
path?: undefined
}
export type ResultFound = {
found: true
path: string | null
}
export type ResolvedResult = ResultNotFound | ResultFound
Note that eslint-plugin-import-x
will no longer pass in the third argument (option) to the resolve
and resolveImport
callbacks. It is recommended to use the factory function pattern like this:
export const createResolver = (options) => {
// Create a re-usable resolver instance with the shared options
const resolver = new ResolverFactory(options);
return {
interfaceVersion: 3,
name: 'custom-resolver',
resolve: (modPath, sourcePath) => {
const resolved = resolver.sync(modPath, { path: path.dirname(sourcePath) });
},
resolveImport: (modPath, sourcePath) => resolver.sync(modPath, { path: path.dirname(sourcePath) })
}
};
The format of the ResolvedResult
will remain the same.
I will be adding import-x/resolversVersion3
for the current major version v4
, allowing users and custom resolver authors to adopt the new import resolver design early.
In the next major version, v5
, import-x/resolversVersion3
will become import-x/resolvers
, and the current import-x/resolvers
will become import-x/resolversLegacy
. We might remove import-x/resolversLegacy
when releasing v6 or v7, we shall see!
In the meantime, we will also export a compat utility for an easier transition from existing resolvers:
export const compatImportXResolverV3 = (options: T, legacyResolver: object) => {
return {
interfaceVersion: 3,
resolve: (modPath, sourcePath) => legacyResolver.resolve(modPath, sourcePath, options),
resolveImport: (modPath, sourcePath) => legacyResolver.resolveImport(modPath, sourcePath, options)
}
}
I chose to drop the object
and use an array since JavaScript is notorious for its object's key order. I don't want to drop support for multiple resolvers, but when order matters, an array is a better choice.
The ESLint flat config involves passing objects and functions directly, so we don't have to adhere to the idea of name
here.
Also, the current resolver interface ((modulePath: string, sourceFile: string, options: T) => ResolvedResult
), which passes the option through the third argument, is problematic. It prevents custom resolver authors from creating a reusable resolver instance, potentially hindering performance improvements. enhanced-resolve
has resolve.create({})
and ResolverFactory.createResolver({})
, while oxc-resolver
has new ResolverFactory({})
.
@JounQin @antfu @silverwind @9romise What do you think?
I haven't remove the
resolver
concept ineslint-plugin-import-x<=0.2
because I found that thewebpack
resolver may be still useful for those webpack users, and although we can useenhanced-resolve
directly, but the settings can not fit all projects, I need to figure out how to set the resolver correctly for specific projects.For example:
I'm thinking the interface of
import-x/resolver
setting should be:By default,
eslint-plugin-import-x
should useenhanced-resolve
directly to simulate nativenode
resolve algorithm.