Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement custom resolver interface v3 #192

Merged
merged 7 commits into from
Dec 3, 2024

Conversation

SukkaW
Copy link
Collaborator

@SukkaW SukkaW commented Dec 2, 2024

The PR implements the new resolver design I proposed in #40 (comment)


For eslint-plugin-import-x users

Like the ESLint flat config allows you to use any 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 nodeResolverObject = {
  interfaceVersion: 3,
  name: 'my-custom-eslint-import-resolver',
  resolve(modPath, sourcePath) {
  };
};

module.exports = {
  settings: {
    // multiple resolvers
    'import-x/resolver-next': [
      nodeResolverObject,
      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 still 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:

// An example of the current `import-x/resolver` settings
module.exports = {
  settings: {
    'import-x/resolver': {
      node: nodeResolverOpt
      webpack: webpackResolverOpt,
      'custom-resolver': customResolverOpt
    }
  }
}

// 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'), {})
    ];
  }
}

For custom import resolver developers

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, {});
    }
  }
};

You can make your resolver implements both legacy resolver interface and new resolver interface:

module.exports = {
  // interface version 2
  interfaceVersion: 2,
  resolve(mod, source) {
  },
  // interface version 3
  createCustomResolver(options) {
    // do something w/ options
    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, {});
      }
    }
  } 
}

@SukkaW SukkaW requested a review from JounQin December 2, 2024 13:30
Copy link

changeset-bot bot commented Dec 2, 2024

🦋 Changeset detected

Latest commit: e03e3de

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

codesandbox-ci bot commented Dec 2, 2024

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

@SukkaW
Copy link
Collaborator Author

SukkaW commented Dec 2, 2024

Custom resolvers adoption status:

I'd also like to here your ideas about new resolver design for eslint-plugin-import-x~

@SukkaW SukkaW marked this pull request as ready for review December 2, 2024 15:16
@romanlex
Copy link

romanlex commented Dec 2, 2024

@SukkaW
If I understood correctly
helljs/eslint-import-resolver-x#2

@SukkaW SukkaW merged commit fbf639b into un-ts:master Dec 3, 2024
21 checks passed
renovate bot added a commit to mmkal/eslint-plugin-mmkal that referenced this pull request Dec 3, 2024
##### [v4.5.0](https://github.com/un-ts/eslint-plugin-import-x/blob/HEAD/CHANGELOG.md#450)

##### Minor Changes

-   [#192](un-ts/eslint-plugin-import-x#192) [`fbf639b`](un-ts/eslint-plugin-import-x@fbf639b) Thanks [@SukkaW](https://github.com/SukkaW)! - The PR implements the new resolver design proposed in un-ts/eslint-plugin-import-x#40 (comment)

##### For `eslint-plugin-import-x` users

Like 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`:

```js
// 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`:

```js
// 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`:

```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: '' })
    ];
  }
}
```

##### For custom import resolver developers

This is the new API design of the resolver interface:

```ts
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:

```js
// 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:

```js
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:

```js
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, {});
    },
  };
};
```

##### Patch Changes

-   [#184](un-ts/eslint-plugin-import-x#184) [`bc4de89`](un-ts/eslint-plugin-import-x@bc4de89) Thanks [@marcalexiei](https://github.com/marcalexiei)! - fix(no-cycle): improves the type declaration of the rule `no-cycle`’s `maxDepth` option

-   [#184](un-ts/eslint-plugin-import-x#184) [`bc4de89`](un-ts/eslint-plugin-import-x@bc4de89) Thanks [@marcalexiei](https://github.com/marcalexiei)! - fix(first): improves the type declaration of the rule `first`'s option

-   [#184](un-ts/eslint-plugin-import-x#184) [`bc4de89`](un-ts/eslint-plugin-import-x@bc4de89) Thanks [@marcalexiei](https://github.com/marcalexiei)! - fix(no-unused-modules): improves the type declaration of the rule `no-unused-modules`’s `missingExports` option

-   [#184](un-ts/eslint-plugin-import-x#184) [`bc4de89`](un-ts/eslint-plugin-import-x@bc4de89) Thanks [@marcalexiei](https://github.com/marcalexiei)! - fix(no-deprecated): improve error message when no description is available
@romanlex
Copy link

romanlex commented Dec 4, 2024

@SukkaW done

@kuoruan
Copy link

kuoruan commented Dec 5, 2024

Hi @SukkaW Can the resolve function return a promise? so that I can use import() function in the resolver to read some config files.

@SukkaW
Copy link
Collaborator Author

SukkaW commented Dec 5, 2024

Hi @SukkaW Can the resolve function return a promise? so that I can use import() function in the resolver to read some config files.

Currently, it is still impossible: ESLint requires rules to be synchronous, and we must run the resolver within the rules because only there can we identify imports from the AST, which we then invoke the resolver on.

If the config file is in cloneable format (JSON, JSON5, JSONC, etc.), you can check out synckit. This is a library made by @JounQin that runs async functions in a worker_threads while blocking the main thread, then returning the result back to the main thread.

@pzmosquito
Copy link

It looks like this change will not be compatible with original the eslint-plugin-import package, which uses interface v2, right?

@SukkaW
Copy link
Collaborator Author

SukkaW commented Dec 17, 2024

It looks like this change will not be compatible with original the eslint-plugin-import package, which uses interface v2, right?

Yeah. So to retain compatibility with the eslint-plugin-import, the default export of the import resolver module should still be interface v2.

renovate bot added a commit to mmkal/eslint-plugin-mmkal that referenced this pull request Dec 19, 2024
##### [v4.6.0](https://github.com/un-ts/eslint-plugin-import-x/blob/HEAD/CHANGELOG.md#460)

##### Minor Changes

-   [#209](un-ts/eslint-plugin-import-x#209) [`46d2360`](un-ts/eslint-plugin-import-x@46d2360) Thanks [@SukkaW](https://github.com/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 import-js/eslint-plugin-import#1810.

    So we decided to implement our own resolver that "just works". The new resolver is built upon the [`enhanced-resolve`](https://www.npmjs.com/package/enhanced-resolve) that implements the full Node.js [Resolver Algorithm](https://nodejs.org/dist/v14.21.3/docs/api/esm.html#esm_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](un-ts/eslint-plugin-import-x#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:

    ```js
    // 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](https://www.npmjs.com/package/eslint-import-resolver-typescript) (also powered by `enhanced-resolve`), [eslint-import-resolver-oxc](https://www.npmjs.com/package/eslint-import-resolver-oxc) (powered by `oxc-resolver`), [eslint-import-resolver-next](https://www.npmjs.com/package/eslint-import-resolver-next) (also powered by `oxc-resolver`), or other similar resolvers.

##### Patch Changes

-   [#206](un-ts/eslint-plugin-import-x#206) [`449738f`](un-ts/eslint-plugin-import-x@449738f) Thanks [@privatenumber](https://github.com/privatenumber)! - insert type prefix without new line
@romanlex
Copy link

The interface of version 3 does not take into account one point in the monorepository.
For example project structure:

client
   src
      core
      // etc
      packages
         PackageA
            .eslintrc
   .eslintrc

If codebase use js and ts at the same time and .eslintrc on root and on package have different settings of resolver
For example root eslintrc:

settings: {
    'import/resolver': {
      [require.resolve('@helljs/eslint-import-resolver-x')]: {
        project: ['jsconfig?(.*).json', '*/packages/*/jsconfig?(.*).json', '*/jsconfig?(.*).json'],
        extensions: ['.js', '.jsx', '.mjs', '.cjs', '.json', '.flow', '.ts', '.tsx', '.d.ts'],
      },
    },
  },

and PackageA eslintrc:

settings = {
  'import/resolver': {
    [require.resolve('@helljs/eslint-import-resolver-x')]: {
      alwaysTryTypes: true,
      project: ['tsconfig?(.*).json', '*/packages/*/tsconfig?(.*).json', '*/tsconfig?(.*).json'],
      extensions: ['.ts', '.tsx', '.d.ts', '.js', '.jsx', '.mjs', '.cjs', '.json'],
    },
  },
}

and we must be able to resolve modules REGARDLESS of where eslint was launched from, i.e. we accept as a requirement that eslint must equally be able to resolve when linting at the package level or at the root level

then in the old interface, the resolve function always accepted the current resolver settings from the import plugin for the current file as an argument and we can use for example digest of options for cache mappers functions

I fixed this for myself by caching mappers based on the digest of the resolver settings object, but I can't imagine how this can be fixed in the v3 interface
helljs/eslint-import-resolver-x@b23ec50

@SukkaW
Copy link
Collaborator Author

SukkaW commented Dec 19, 2024

I fixed this for myself by caching mappers based on the digest of the resolver settings object, but I can't imagine how this can be fixed in the v3 interface

@romanlex

If I understand correctly, in the previous version of the import resolver interface, there was only one active resolver that received two different options from two eslintrc files. Since there is only one resolver, you will have to do a module-scope singleton "global" cache:

let previousDigestHash;
let cachedGlobalSingleton = new DummyExample();

const resolve = (source, parent, options) => {
  const digest = hash(options);
};

But with the interface version 3 and eslint.config.js, you can create two resolvers (one per eslint.config.js), and you can make them completely separated:

const createResolver = (options) => {
  const internalInstance = new DummyExample();
  // one internalInstance per `createResolver()` call.
  // only belong to its scope, not shared with other resolvers.

  const resolve = (source, parent) => {
  };
}

With that, both eslint.config.js files will initiate their own createResolver(), so there will be two internalInstance (one per eslint.config.js), and each belongs to their own createResolver scope, not shared! In fact, since you no longer rely on module-scope variables to store the instance, you don't even have to do digest anymore! And that's the primary reason why I push the interface version 3 in the first place.

@romanlex
Copy link

It is reasonable, I did not think that it is possible to create several revolvers at the same time
thx

renovate bot added a commit to mmkal/eslint-plugin-mmkal that referenced this pull request Dec 19, 2024
##### [v4.6.1](https://github.com/un-ts/eslint-plugin-import-x/blob/HEAD/CHANGELOG.md#461)

##### Patch Changes

-   [#211](un-ts/eslint-plugin-import-x#211) [`be9c3e8`](un-ts/eslint-plugin-import-x@be9c3e8) Thanks [@mrginglymus](https://github.com/mrginglymus)! - Fix enhanced-resolve dependency
##### [v4.6.0](https://github.com/un-ts/eslint-plugin-import-x/blob/HEAD/CHANGELOG.md#460)

##### Minor Changes

-   [#209](un-ts/eslint-plugin-import-x#209) [`46d2360`](un-ts/eslint-plugin-import-x@46d2360) Thanks [@SukkaW](https://github.com/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 import-js/eslint-plugin-import#1810.

    So we decided to implement our own resolver that "just works". The new resolver is built upon the [`enhanced-resolve`](https://www.npmjs.com/package/enhanced-resolve) that implements the full Node.js [Resolver Algorithm](https://nodejs.org/dist/v14.21.3/docs/api/esm.html#esm_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](un-ts/eslint-plugin-import-x#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:

    ```js
    // 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](https://www.npmjs.com/package/eslint-import-resolver-typescript) (also powered by `enhanced-resolve`), [eslint-import-resolver-oxc](https://www.npmjs.com/package/eslint-import-resolver-oxc) (powered by `oxc-resolver`), [eslint-import-resolver-next](https://www.npmjs.com/package/eslint-import-resolver-next) (also powered by `oxc-resolver`), or other similar resolvers.

##### Patch Changes

-   [#206](un-ts/eslint-plugin-import-x#206) [`449738f`](un-ts/eslint-plugin-import-x@449738f) Thanks [@privatenumber](https://github.com/privatenumber)! - insert type prefix without new line
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants