From ed6efb1f96516dfb93fa83aee7155a81854256c0 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Wed, 18 Dec 2024 20:43:31 +0800 Subject: [PATCH 1/7] feat: implement a resolver that supports `exports` --- package.json | 1 + src/index.ts | 2 ++ src/node-resolver.ts | 60 ++++++++++++++++++++++++++++++++++++++++++++ yarn.lock | 5 ++++ 4 files changed, 68 insertions(+) create mode 100644 src/node-resolver.ts diff --git a/package.json b/package.json index 8a521302..9f3cdc60 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "eslint": "^8.57.0 || ^9.0.0" }, "dependencies": { + "@dual-bundle/import-meta-resolve": "^4.1.0", "@types/doctrine": "^0.0.9", "@typescript-eslint/scope-manager": "^8.1.0", "@typescript-eslint/utils": "^8.1.0", diff --git a/src/index.ts b/src/index.ts index 5d34ef1b..bf7a43c3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -72,6 +72,7 @@ import type { PluginFlatConfig, } from './types' import { importXResolverCompat } from './utils' +import { createNodeResolver } from './node-resolver' const rules = { 'no-unresolved': noUnresolved, @@ -183,4 +184,5 @@ export = { flatConfigs, rules, importXResolverCompat, + createNodeResolver } diff --git a/src/node-resolver.ts b/src/node-resolver.ts new file mode 100644 index 00000000..b5c9221f --- /dev/null +++ b/src/node-resolver.ts @@ -0,0 +1,60 @@ +import { fileURLToPath, pathToFileURL } from "url"; +import type { NewResolver } from "./types"; + +import type { ErrnoException } from '@dual-bundle/import-meta-resolve' with { "resolution-mode": "import" }; + +const importMetaResolveExports: typeof import('@dual-bundle/import-meta-resolve', { with: { "resolution-mode": "import" } }) = require('@dual-bundle/import-meta-resolve'); +const { moduleResolve } = importMetaResolveExports; + +interface NodeResolverOptions { + /** + * The import conditions the resolver will used when reading the exports map from "package.json" + * @default new Set(['default', 'module', 'import', 'require']) + */ + conditions?: Set; + /** + * keep symlinks instead of resolving them + * @default false + */ + preserveSymlinks?: boolean; +} + +const assertErrNoException = (error: unknown): error is ErrnoException => { + return ( + typeof error === 'object' && + error !== null && + 'code' in error && + typeof error.code === 'string' + ); +} + +export function createNodeResolver({ + conditions = new Set(['default', 'module', 'import', 'require']), + preserveSymlinks = false, +}: NodeResolverOptions = {}): NewResolver { + return { + interfaceVersion: 3, + name: 'eslint-plugin-import-x built-in node resolver', + resolve: (modulePath, sourceFile) => { + try { + const found = moduleResolve( + modulePath, + pathToFileURL(sourceFile), + conditions, + preserveSymlinks + ); + return { + found: true, + path: fileURLToPath(found), + }; + } catch (error) { + if (assertErrNoException(error) && error.code === 'ERR_MODULE_NOT_FOUND') { + return { + found: false, + } + } + throw error; + } + } + } +} diff --git a/yarn.lock b/yarn.lock index dfe2a1bd..3d722929 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1375,6 +1375,11 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@dual-bundle/import-meta-resolve@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#519c1549b0e147759e7825701ecffd25e5819f7b" + integrity sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg== + "@emnapi/core@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.1.0.tgz#6b552ea3d2b303965c52661c05976d3072ccb6a3" From 6396f5006185b377bc4cc7edd4dd793b0dc5a39b Mon Sep 17 00:00:00 2001 From: SukkaW Date: Wed, 18 Dec 2024 20:58:08 +0800 Subject: [PATCH 2/7] chore: typedocs --- src/node-resolver.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/node-resolver.ts b/src/node-resolver.ts index b5c9221f..7706ca4d 100644 --- a/src/node-resolver.ts +++ b/src/node-resolver.ts @@ -9,11 +9,13 @@ const { moduleResolve } = importMetaResolveExports; interface NodeResolverOptions { /** * The import conditions the resolver will used when reading the exports map from "package.json" + * @type {Set | undefined} * @default new Set(['default', 'module', 'import', 'require']) */ conditions?: Set; /** * keep symlinks instead of resolving them + * @type {boolean | undefined} * @default false */ preserveSymlinks?: boolean; From 8d47a9d6015ed4528f5dcc774c7c8d24da953335 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Wed, 18 Dec 2024 20:58:11 +0800 Subject: [PATCH 3/7] chore: changeset --- .changeset/witty-garlics-destroy.md | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .changeset/witty-garlics-destroy.md diff --git a/.changeset/witty-garlics-destroy.md b/.changeset/witty-garlics-destroy.md new file mode 100644 index 00000000..c5b2b45f --- /dev/null +++ b/.changeset/witty-garlics-destroy.md @@ -0,0 +1,45 @@ +--- +"eslint-plugin-import-x": minor +--- + +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) simply refuses implementing this feature and he even 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 [`@dual-bundle/import-meta-resolve`](https://www.npmjs.com/package/@dual-bundle/import-meta-resolve) that implements the full Node.js [Resolver Algorithm](https://nodejs.org/dist/v14.21.3/docs/api/esm.html#esm_resolver_algorithm). The resolver is shipped with the `eslint-plugin-import-x` package. + +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), [eslint-import-resolver-oxc](https://www.npmjs.com/package/eslint-import-resolver-oxc), [eslint-import-resolver-next](https://www.npmjs.com/package/eslint-import-resolver-next), or other similar resolvers. + +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': [ + createNodeResolver({ + /** + * Optional, the import conditions the resolver will used when reading the exports map from "package.json" + * @default new Set(['default', 'module', 'import', 'require']) + */ + conditions: new Set(['default', 'module', 'import', 'require']), + /** + * Optional, keep symlinks instead of resolving them + * @default false + */ + preserveSymlinks: false + }), + // you can add more resolvers down below + require('eslint-import-resolver-typescript').createTypeScriptImportResolver( + /** options of eslint-import-resolver-typescript */ + ) + ], + }, +}; +``` + +Note that you can't use this new resolver with the `eslint-plugin-import` package as we only implemented the import resolver interface v3 (For more details about the import resolver interface v3, please check out [#192](https://github.com/un-ts/eslint-plugin-import-x/pull/192)). From 1be95ccf67fb3bb75f5bec3e824ea39be774306f Mon Sep 17 00:00:00 2001 From: SukkaW Date: Wed, 18 Dec 2024 22:22:48 +0800 Subject: [PATCH 4/7] test: add for new node resolver --- src/node-resolver.ts | 55 ++++++++++++++++++++++++++++++-------- test/node-resolver.spec.ts | 45 +++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 11 deletions(-) create mode 100644 test/node-resolver.spec.ts diff --git a/src/node-resolver.ts b/src/node-resolver.ts index 7706ca4d..eab0befc 100644 --- a/src/node-resolver.ts +++ b/src/node-resolver.ts @@ -1,10 +1,12 @@ -import { fileURLToPath, pathToFileURL } from "url"; import type { NewResolver } from "./types"; -import type { ErrnoException } from '@dual-bundle/import-meta-resolve' with { "resolution-mode": "import" }; +import { fileURLToPath, pathToFileURL } from "url"; +import fs from "fs"; +import { createRequire } from "module"; -const importMetaResolveExports: typeof import('@dual-bundle/import-meta-resolve', { with: { "resolution-mode": "import" } }) = require('@dual-bundle/import-meta-resolve'); -const { moduleResolve } = importMetaResolveExports; +import type { ErrnoException, moduleResolve as $moduleResolve } from '@dual-bundle/import-meta-resolve' with { "resolution-mode": "import" }; +const importMetaResolveExports = require('@dual-bundle/import-meta-resolve'); +const moduleResolve = importMetaResolveExports.moduleResolve as typeof $moduleResolve; interface NodeResolverOptions { /** @@ -45,17 +47,48 @@ export function createNodeResolver({ conditions, preserveSymlinks ); + + if (found.protocol === 'file:') { + if (fs.existsSync(found)) { + return { + found: true, + path: fileURLToPath(found), + }; + } + } else if (found.protocol === 'node:' || found.protocol === 'data:') { + return { + found: true, + path: null + } + } return { - found: true, - path: fileURLToPath(found), - }; + found: false + } } catch (error) { - if (assertErrNoException(error) && error.code === 'ERR_MODULE_NOT_FOUND') { - return { - found: false, + if (assertErrNoException(error)) { + if (error.code === 'ERR_MODULE_NOT_FOUND') { + return { + found: false, + } } + if (error.code === 'ERR_UNSUPPORTED_DIR_IMPORT') { + const $require = createRequire(sourceFile); + try { + const resolved = $require.resolve(modulePath); + return { + found: true, + path: resolved, + } + } catch { + return { + found: false, + } + } + } + } + return { + found: false, } - throw error; } } } diff --git a/test/node-resolver.spec.ts b/test/node-resolver.spec.ts new file mode 100644 index 00000000..12bb7355 --- /dev/null +++ b/test/node-resolver.spec.ts @@ -0,0 +1,45 @@ +import path from 'node:path' +import { cwd } from 'node:process' +import { createNodeResolver } from '../src/node-resolver'; + +const resolver = createNodeResolver() + +function expectResolve(source: string, expected: boolean | string) { + it(`${source} => ${expected}`, () => { + try { + console.log({ source, expected, requireResolve: require.resolve(source, { paths: [__dirname] }) }) + + } catch { + console.log({ source, expected, requireResolve: null }) + } + const result = resolver.resolve(source, __filename); + console.log({ source, expected, result }) + + if (typeof expected === 'string') { + expect(result.path).toBe(path.resolve(cwd(), expected)) + } else { + expect(result.found).toBe(expected) + } + }) +} + +describe('builtin', () => { + expectResolve('path', true) + expectResolve('node:path', true) +}) + +describe('modules', () => { + expectResolve('jest', true) + expectResolve('@sukka/does-not-exists', false) +}) + +describe('relative', () => { + expectResolve('../package.json', 'package.json') + expectResolve('../.github/dependabot.yml', false) + expectResolve('../babel.config.js', 'babel.config.js') + expectResolve('../test/index.js', 'test/index.js') + expectResolve('../test/', 'test/index.js') + expectResolve('../test', 'test/index.js') + + expectResolve('../inexistent.js', false) +}) From c2ced73ef804165fd0aebde5dc5c53c87cf67c4b Mon Sep 17 00:00:00 2001 From: SukkaW Date: Wed, 18 Dec 2024 22:50:31 +0800 Subject: [PATCH 5/7] refactor: let's use `enhanced-resolve`, much simpler --- package.json | 3 +- src/node-resolver.ts | 103 ++++++++++++++++--------------------------- yarn.lock | 15 ++++--- 3 files changed, 49 insertions(+), 72 deletions(-) diff --git a/package.json b/package.json index 9f3cdc60..a56871dc 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ "eslint": "^8.57.0 || ^9.0.0" }, "dependencies": { - "@dual-bundle/import-meta-resolve": "^4.1.0", "@types/doctrine": "^0.0.9", "@typescript-eslint/scope-manager": "^8.1.0", "@typescript-eslint/utils": "^8.1.0", @@ -97,7 +96,7 @@ "@typescript-eslint/rule-tester": "^8.15.0", "@unts/patch-package": "^8.0.0", "cross-env": "^7.0.3", - "enhanced-resolve": "^5.16.0", + "enhanced-resolve": "^5.17.1", "escope": "^4.0.0", "eslint": "^9.15.0", "eslint-config-prettier": "^9.1.0", diff --git a/src/node-resolver.ts b/src/node-resolver.ts index eab0befc..a02cfbad 100644 --- a/src/node-resolver.ts +++ b/src/node-resolver.ts @@ -1,14 +1,16 @@ -import type { NewResolver } from "./types"; - -import { fileURLToPath, pathToFileURL } from "url"; -import fs from "fs"; -import { createRequire } from "module"; - -import type { ErrnoException, moduleResolve as $moduleResolve } from '@dual-bundle/import-meta-resolve' with { "resolution-mode": "import" }; -const importMetaResolveExports = require('@dual-bundle/import-meta-resolve'); -const moduleResolve = importMetaResolveExports.moduleResolve as typeof $moduleResolve; +import { ResolverFactory, CachedInputFileSystem } from 'enhanced-resolve'; +import fs from 'node:fs'; +import type { NewResolver } from './types'; +import { isBuiltin } from 'node:module'; +import { dirname } from 'node:path'; interface NodeResolverOptions { + /** + * The allowed extensions the resolver will attempt to find when resolving a module + * @type {string[] | undefined} + * @default ['.mjs', '.cjs', '.js', '.json', '.node'] + */ + extensions?: string[]; /** * The import conditions the resolver will used when reading the exports map from "package.json" * @type {Set | undefined} @@ -23,72 +25,45 @@ interface NodeResolverOptions { preserveSymlinks?: boolean; } -const assertErrNoException = (error: unknown): error is ErrnoException => { - return ( - typeof error === 'object' && - error !== null && - 'code' in error && - typeof error.code === 'string' - ); -} - export function createNodeResolver({ + extensions = ['.mjs', '.cjs', '.js', '.json', '.node'], conditions = new Set(['default', 'module', 'import', 'require']), preserveSymlinks = false, }: NodeResolverOptions = {}): NewResolver { + const resolver = ResolverFactory.createResolver({ + fileSystem: new CachedInputFileSystem(fs, 4 * 1000), + extensions, + conditionNames: Array.from(conditions), + symlinks: !preserveSymlinks, + useSyncFileSystemCalls: true + }); + + // shared context across all resolve calls + return { interfaceVersion: 3, name: 'eslint-plugin-import-x built-in node resolver', resolve: (modulePath, sourceFile) => { + if (isBuiltin(modulePath)) { + return { found: true, path: null }; + } + + if (modulePath.startsWith('data:')) { + return { found: true, path: null }; + } + try { - const found = moduleResolve( - modulePath, - pathToFileURL(sourceFile), - conditions, - preserveSymlinks + const path = resolver.resolveSync( + {}, + dirname(sourceFile), + modulePath ); - - if (found.protocol === 'file:') { - if (fs.existsSync(found)) { - return { - found: true, - path: fileURLToPath(found), - }; - } - } else if (found.protocol === 'node:' || found.protocol === 'data:') { - return { - found: true, - path: null - } - } - return { - found: false - } - } catch (error) { - if (assertErrNoException(error)) { - if (error.code === 'ERR_MODULE_NOT_FOUND') { - return { - found: false, - } - } - if (error.code === 'ERR_UNSUPPORTED_DIR_IMPORT') { - const $require = createRequire(sourceFile); - try { - const resolved = $require.resolve(modulePath); - return { - found: true, - path: resolved, - } - } catch { - return { - found: false, - } - } - } - } - return { - found: false, + if (path) { + return { found: true, path }; } + return { found: false }; + } catch { + return { found: false }; } } } diff --git a/yarn.lock b/yarn.lock index 3d722929..258fe862 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1375,11 +1375,6 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@dual-bundle/import-meta-resolve@^4.1.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#519c1549b0e147759e7825701ecffd25e5819f7b" - integrity sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg== - "@emnapi/core@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.1.0.tgz#6b552ea3d2b303965c52661c05976d3072ccb6a3" @@ -3418,7 +3413,7 @@ enhanced-resolve@^0.9.1: memory-fs "^0.2.0" tapable "^0.1.8" -enhanced-resolve@^5.12.0, enhanced-resolve@^5.16.0: +enhanced-resolve@^5.12.0: version "5.16.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz#65ec88778083056cb32487faa9aef82ed0864787" integrity sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA== @@ -3426,6 +3421,14 @@ enhanced-resolve@^5.12.0, enhanced-resolve@^5.16.0: graceful-fs "^4.2.4" tapable "^2.2.0" +enhanced-resolve@^5.17.1: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + enquirer@^2.3.0: version "2.4.1" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" From 4aa44b81c304ca101c0b5605944a0e8c6ea4c2d1 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Thu, 19 Dec 2024 14:16:41 +0800 Subject: [PATCH 6/7] refactor: accept more options from `enhanced-resolvers` --- .changeset/witty-garlics-destroy.md | 23 ++++++++++++------- src/node-resolver.ts | 34 ++++++++++++++--------------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/.changeset/witty-garlics-destroy.md b/.changeset/witty-garlics-destroy.md index c5b2b45f..aff49238 100644 --- a/.changeset/witty-garlics-destroy.md +++ b/.changeset/witty-garlics-destroy.md @@ -2,9 +2,9 @@ "eslint-plugin-import-x": minor --- -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) simply refuses implementing this feature and he even locked the issue (https://github.com/import-js/eslint-plugin-import/issues/1810). +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 [`@dual-bundle/import-meta-resolve`](https://www.npmjs.com/package/@dual-bundle/import-meta-resolve) that implements the full Node.js [Resolver Algorithm](https://nodejs.org/dist/v14.21.3/docs/api/esm.html#esm_resolver_algorithm). The resolver is shipped with the `eslint-plugin-import-x` package. +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 resolver is shipped with the `eslint-plugin-import-x` package. 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), [eslint-import-resolver-oxc](https://www.npmjs.com/package/eslint-import-resolver-oxc), [eslint-import-resolver-next](https://www.npmjs.com/package/eslint-import-resolver-next), or other similar resolvers. @@ -21,17 +21,24 @@ module.exports = { }, settings: { 'import-x/resolver-next': [ + // This is the new resolver we are introducing createNodeResolver({ /** - * Optional, the import conditions the resolver will used when reading the exports map from "package.json" - * @default new Set(['default', 'module', 'import', 'require']) + * 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'] */ - conditions: new Set(['default', 'module', 'import', 'require']), + extensions?: string[]; /** - * Optional, keep symlinks instead of resolving them - * @default false + * 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'] */ - preserveSymlinks: false + conditions: ['default', 'module', 'import', 'require'], }), // you can add more resolvers down below require('eslint-import-resolver-typescript').createTypeScriptImportResolver( diff --git a/src/node-resolver.ts b/src/node-resolver.ts index a02cfbad..9b0e6b34 100644 --- a/src/node-resolver.ts +++ b/src/node-resolver.ts @@ -1,10 +1,10 @@ -import { ResolverFactory, CachedInputFileSystem } from 'enhanced-resolve'; +import { ResolverFactory, CachedInputFileSystem, type ResolveOptions } from 'enhanced-resolve'; import fs from 'node:fs'; import type { NewResolver } from './types'; import { isBuiltin } from 'node:module'; import { dirname } from 'node:path'; -interface NodeResolverOptions { +interface NodeResolverOptions extends Omit { /** * The allowed extensions the resolver will attempt to find when resolving a module * @type {string[] | undefined} @@ -13,29 +13,27 @@ interface NodeResolverOptions { extensions?: string[]; /** * The import conditions the resolver will used when reading the exports map from "package.json" - * @type {Set | undefined} - * @default new Set(['default', 'module', 'import', 'require']) - */ - conditions?: Set; - /** - * keep symlinks instead of resolving them - * @type {boolean | undefined} - * @default false + * @type {string[] | undefined} + * @default ['default', 'module', 'import', 'require'] */ - preserveSymlinks?: boolean; + conditionNames?: string[]; } export function createNodeResolver({ extensions = ['.mjs', '.cjs', '.js', '.json', '.node'], - conditions = new Set(['default', 'module', 'import', 'require']), - preserveSymlinks = false, -}: NodeResolverOptions = {}): NewResolver { + conditionNames = ['default', 'module', 'import', 'require'], + mainFields = ['main'], + exportsFields = ['exports'], + mainFiles = ['index'], + fileSystem = new CachedInputFileSystem(fs, 4 * 1000), + ...restOptions +}: Partial = {}): NewResolver { const resolver = ResolverFactory.createResolver({ - fileSystem: new CachedInputFileSystem(fs, 4 * 1000), extensions, - conditionNames: Array.from(conditions), - symlinks: !preserveSymlinks, - useSyncFileSystemCalls: true + fileSystem, + conditionNames, + useSyncFileSystemCalls: true, + ...restOptions, }); // shared context across all resolve calls From e8361920a4f705d42886040e34629f24c0a09296 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Thu, 19 Dec 2024 14:19:07 +0800 Subject: [PATCH 7/7] chore: update changeset --- .changeset/witty-garlics-destroy.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.changeset/witty-garlics-destroy.md b/.changeset/witty-garlics-destroy.md index aff49238..ac826a23 100644 --- a/.changeset/witty-garlics-destroy.md +++ b/.changeset/witty-garlics-destroy.md @@ -2,11 +2,9 @@ "eslint-plugin-import-x": minor --- -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). +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`](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 resolver is shipped with the `eslint-plugin-import-x` package. - -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), [eslint-import-resolver-oxc](https://www.npmjs.com/package/eslint-import-resolver-oxc), [eslint-import-resolver-next](https://www.npmjs.com/package/eslint-import-resolver-next), or other similar resolvers. +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](https://github.com/un-ts/eslint-plugin-import-x/pull/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: @@ -39,6 +37,8 @@ module.exports = { * @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( @@ -49,4 +49,4 @@ module.exports = { }; ``` -Note that you can't use this new resolver with the `eslint-plugin-import` package as we only implemented the import resolver interface v3 (For more details about the import resolver interface v3, please check out [#192](https://github.com/un-ts/eslint-plugin-import-x/pull/192)). +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.