From c58a726e9d763c9d90e91c92c39d185e5ce895fd Mon Sep 17 00:00:00 2001 From: GertSallaerts <1267900+GertSallaerts@users.noreply.github.com> Date: Thu, 14 Nov 2024 17:27:46 +0100 Subject: [PATCH 1/3] feat(resolve): allow specifying enhanced-resolver's modules option --- docs/rules/no-extraneous-import.md | 5 +++ docs/rules/no-extraneous-require.md | 5 +++ docs/rules/no-missing-import.md | 5 +++ docs/rules/no-missing-require.md | 5 +++ docs/rules/no-unpublished-import.md | 5 +++ docs/rules/no-unpublished-require.md | 5 +++ docs/shared-settings.md | 24 ++++++++++ lib/rules/no-extraneous-import.js | 2 + lib/rules/no-extraneous-require.js | 2 + lib/rules/no-missing-import.js | 2 + lib/rules/no-missing-require.js | 2 + lib/rules/no-unpublished-import.js | 2 + lib/rules/no-unpublished-require.js | 2 + lib/util/get-resolve-modules.js | 45 +++++++++++++++++++ lib/util/import-target.js | 5 +++ lib/util/visit-import.js | 4 +- lib/util/visit-require.js | 4 +- .../no-missing/my_modules/my-module/a-file.js | 0 .../my_modules/my-module/package.json | 3 ++ tests/lib/rules/no-missing-require.js | 21 +++++++++ 20 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 lib/util/get-resolve-modules.js create mode 100644 tests/fixtures/no-missing/my_modules/my-module/a-file.js create mode 100644 tests/fixtures/no-missing/my_modules/my-module/package.json diff --git a/docs/rules/no-extraneous-import.md b/docs/rules/no-extraneous-import.md index fbeaaa63..dc51b261 100644 --- a/docs/rules/no-extraneous-import.md +++ b/docs/rules/no-extraneous-import.md @@ -37,6 +37,11 @@ Please see the shared settings documentation for more information. This can be configured in the rule options or as a shared setting [`settings.resolvePaths`](../shared-settings.md#resolvepaths). Please see the shared settings documentation for more information. +#### resolveModules + +This can be configured in the rule options or as a shared setting [`settings.resolveModules`](../shared-settings.md#resolvemodules). +Please see the shared settings documentation for more information. + #### convertPath This can be configured in the rule options or as a shared setting [`settings.convertPath`](../shared-settings.md#convertpath). diff --git a/docs/rules/no-extraneous-require.md b/docs/rules/no-extraneous-require.md index 25a54224..d3c2e827 100644 --- a/docs/rules/no-extraneous-require.md +++ b/docs/rules/no-extraneous-require.md @@ -38,6 +38,11 @@ Please see the shared settings documentation for more information. This can be configured in the rule options or as a shared setting [`settings.resolvePaths`](../shared-settings.md#resolvepaths). Please see the shared settings documentation for more information. +#### resolveModules + +This can be configured in the rule options or as a shared setting [`settings.resolveModules`](../shared-settings.md#resolvemodules). +Please see the shared settings documentation for more information. + #### convertPath This can be configured in the rule options or as a shared setting [`settings.convertPath`](../shared-settings.md#convertpath). diff --git a/docs/rules/no-missing-import.md b/docs/rules/no-missing-import.md index 6de28349..4cdd22f2 100644 --- a/docs/rules/no-missing-import.md +++ b/docs/rules/no-missing-import.md @@ -52,6 +52,11 @@ Please see the shared settings documentation for more information. This can be configured in the rule options or as a shared setting [`settings.resolvePaths`](../shared-settings.md#resolvepaths). Please see the shared settings documentation for more information. +#### resolveModules + +This can be configured in the rule options or as a shared setting [`settings.resolveModules`](../shared-settings.md#resolvemodules). +Please see the shared settings documentation for more information. + #### tsconfigPath This can be configured in the rule options or as a shared setting [`settings.tsconfigPath`](../shared-settings.md#tsconfigpath). diff --git a/docs/rules/no-missing-require.md b/docs/rules/no-missing-require.md index 899fe381..827b02ac 100644 --- a/docs/rules/no-missing-require.md +++ b/docs/rules/no-missing-require.md @@ -60,6 +60,11 @@ Please see the shared settings documentation for more information. This can be configured in the rule options or as a shared setting [`settings.resolvePaths`](../shared-settings.md#resolvepaths). Please see the shared settings documentation for more information. +#### resolveModules + +This can be configured in the rule options or as a shared setting [`settings.resolveModules`](../shared-settings.md#resolvemodules). +Please see the shared settings documentation for more information. + #### tryExtensions This can be configured in the rule options or as a shared setting [`settings.tryExtensions`](../shared-settings.md#tryextensions). diff --git a/docs/rules/no-unpublished-import.md b/docs/rules/no-unpublished-import.md index c2ccb7f2..c8163d81 100644 --- a/docs/rules/no-unpublished-import.md +++ b/docs/rules/no-unpublished-import.md @@ -41,6 +41,11 @@ Please see the shared settings documentation for more information. This can be configured in the rule options or as a shared setting [`settings.resolvePaths`](../shared-settings.md#resolvepaths). Please see the shared settings documentation for more information. +#### resolveModules + +This can be configured in the rule options or as a shared setting [`settings.resolveModules`](../shared-settings.md#resolvemodules). +Please see the shared settings documentation for more information. + #### convertPath This can be configured in the rule options or as a shared setting [`settings.convertPath`](../shared-settings.md#convertpath). diff --git a/docs/rules/no-unpublished-require.md b/docs/rules/no-unpublished-require.md index be02aed1..e746b4b1 100644 --- a/docs/rules/no-unpublished-require.md +++ b/docs/rules/no-unpublished-require.md @@ -43,6 +43,11 @@ Please see the shared settings documentation for more information. This can be configured in the rule options or as a shared setting [`settings.resolvePaths`](../shared-settings.md#resolvepaths). Please see the shared settings documentation for more information. +#### resolveModules + +This can be configured in the rule options or as a shared setting [`settings.resolveModules`](../shared-settings.md#resolvemodules). +Please see the shared settings documentation for more information. + #### convertPath This can be configured in the rule options or as a shared setting [`settings.convertPath`](../shared-settings.md#convertpath). diff --git a/docs/shared-settings.md b/docs/shared-settings.md index 47b85ee9..7709db93 100644 --- a/docs/shared-settings.md +++ b/docs/shared-settings.md @@ -60,6 +60,30 @@ If a path is relative, it will be resolved from CWD. { "resolvePaths": [] } ``` +## resolveModules + +Adds additional directories that should be searched when resolving modules. + +Absolute and relative paths can both be used, but be aware that they will behave a bit differently. + +A relative path will be scanned similarly to how Node scans for node_modules, by looking through the current directory as well as its ancestors (i.e. ./node_modules, ../node_modules, and on). + +With an absolute path, it will only search in the given directory. + +If a path is relative, it will be resolved from CWD. + +### Example resolveModules + +```json +{ "resolveModules": ["/path/to/somewhere", "../relative/path", "node_modules"] } +``` + +### Default resolveModules + +```json +{ "resolveModules": ["node_modules"] } +``` + ## convertPath If we use transpilers (e.g. Babel), perhaps the file path to a source code is never published. diff --git a/lib/rules/no-extraneous-import.js b/lib/rules/no-extraneous-import.js index 0147bf32..43f96e2c 100644 --- a/lib/rules/no-extraneous-import.js +++ b/lib/rules/no-extraneous-import.js @@ -8,6 +8,7 @@ const { checkExtraneous, messages } = require("../util/check-extraneous") const getAllowModules = require("../util/get-allow-modules") const getConvertPath = require("../util/get-convert-path") const getResolvePaths = require("../util/get-resolve-paths") +const getResolveModules = require("../util/get-resolve-modules") const visitImport = require("../util/visit-import") /** @type {import('eslint').Rule.RuleModule} */ @@ -28,6 +29,7 @@ module.exports = { allowModules: getAllowModules.schema, convertPath: getConvertPath.schema, resolvePaths: getResolvePaths.schema, + resolveModules: getResolveModules.schema, }, additionalProperties: false, }, diff --git a/lib/rules/no-extraneous-require.js b/lib/rules/no-extraneous-require.js index f733ee96..55f18edf 100644 --- a/lib/rules/no-extraneous-require.js +++ b/lib/rules/no-extraneous-require.js @@ -8,6 +8,7 @@ const { checkExtraneous, messages } = require("../util/check-extraneous") const getAllowModules = require("../util/get-allow-modules") const getConvertPath = require("../util/get-convert-path") const getResolvePaths = require("../util/get-resolve-paths") +const getResolveModules = require("../util/get-resolve-modules") const getTryExtensions = require("../util/get-try-extensions") const visitRequire = require("../util/visit-require") @@ -29,6 +30,7 @@ module.exports = { allowModules: getAllowModules.schema, convertPath: getConvertPath.schema, resolvePaths: getResolvePaths.schema, + resolveModules: getResolveModules.schema, tryExtensions: getTryExtensions.schema, }, additionalProperties: false, diff --git a/lib/rules/no-missing-import.js b/lib/rules/no-missing-import.js index 2be89254..851f08d9 100644 --- a/lib/rules/no-missing-import.js +++ b/lib/rules/no-missing-import.js @@ -7,6 +7,7 @@ const { checkExistence, messages } = require("../util/check-existence") const getAllowModules = require("../util/get-allow-modules") const getResolvePaths = require("../util/get-resolve-paths") +const getResolveModules = require("../util/get-resolve-modules") const getTryExtensions = require("../util/get-try-extensions") const getTSConfig = require("../util/get-tsconfig") const getTypescriptExtensionMap = require("../util/get-typescript-extension-map") @@ -29,6 +30,7 @@ module.exports = { properties: { allowModules: getAllowModules.schema, resolvePaths: getResolvePaths.schema, + resolveModules: getResolveModules.schema, tryExtensions: getTryExtensions.schema, ignoreTypeImport: { type: "boolean", default: false }, tsconfigPath: getTSConfig.schema, diff --git a/lib/rules/no-missing-require.js b/lib/rules/no-missing-require.js index cd2eabb7..987943db 100644 --- a/lib/rules/no-missing-require.js +++ b/lib/rules/no-missing-require.js @@ -7,6 +7,7 @@ const { checkExistence, messages } = require("../util/check-existence") const getAllowModules = require("../util/get-allow-modules") const getResolvePaths = require("../util/get-resolve-paths") +const getResolveModules = require("../util/get-resolve-modules") const getTSConfig = require("../util/get-tsconfig") const getTryExtensions = require("../util/get-try-extensions") const getTypescriptExtensionMap = require("../util/get-typescript-extension-map") @@ -30,6 +31,7 @@ module.exports = { allowModules: getAllowModules.schema, tryExtensions: getTryExtensions.schema, resolvePaths: getResolvePaths.schema, + resolveModules: getResolveModules.schema, typescriptExtensionMap: getTypescriptExtensionMap.schema, tsconfigPath: getTSConfig.schema, }, diff --git a/lib/rules/no-unpublished-import.js b/lib/rules/no-unpublished-import.js index 70911783..b15d290e 100644 --- a/lib/rules/no-unpublished-import.js +++ b/lib/rules/no-unpublished-import.js @@ -8,6 +8,7 @@ const { checkPublish, messages } = require("../util/check-publish") const getAllowModules = require("../util/get-allow-modules") const getConvertPath = require("../util/get-convert-path") const getResolvePaths = require("../util/get-resolve-paths") +const getResolveModules = require("../util/get-resolve-modules") const visitImport = require("../util/visit-import") /** @type {import('eslint').Rule.RuleModule} */ @@ -28,6 +29,7 @@ module.exports = { allowModules: getAllowModules.schema, convertPath: getConvertPath.schema, resolvePaths: getResolvePaths.schema, + resolveModules: getResolveModules.schema, ignoreTypeImport: { type: "boolean", default: false }, ignorePrivate: { type: "boolean", default: true }, }, diff --git a/lib/rules/no-unpublished-require.js b/lib/rules/no-unpublished-require.js index a5f70bcf..9ab4e5c7 100644 --- a/lib/rules/no-unpublished-require.js +++ b/lib/rules/no-unpublished-require.js @@ -8,6 +8,7 @@ const { checkPublish, messages } = require("../util/check-publish") const getAllowModules = require("../util/get-allow-modules") const getConvertPath = require("../util/get-convert-path") const getResolvePaths = require("../util/get-resolve-paths") +const getResolveModules = require("../util/get-resolve-modules") const getTryExtensions = require("../util/get-try-extensions") const visitRequire = require("../util/visit-require") @@ -29,6 +30,7 @@ module.exports = { allowModules: getAllowModules.schema, convertPath: getConvertPath.schema, resolvePaths: getResolvePaths.schema, + resolveModules: getResolveModules.schema, tryExtensions: getTryExtensions.schema, ignorePrivate: { type: "boolean", default: true }, }, diff --git a/lib/util/get-resolve-modules.js b/lib/util/get-resolve-modules.js new file mode 100644 index 00000000..4aeea0af --- /dev/null +++ b/lib/util/get-resolve-modules.js @@ -0,0 +1,45 @@ +/** + * @author Toru Nagashima + * See LICENSE file in root directory for full license. + */ +"use strict" + +/** @type {string[]} */ +const DEFAULT_VALUE = ["node_modules"] + +/** + * Gets `resolveModules` property from a given option object. + * + * @param {{ resolveModules: unknown[] } | undefined} option - An option object to get. + * @returns {string[] | undefined} The `allowModules` value, or `null`. + */ +function get(option) { + if (Array.isArray(option?.resolveModules)) { + return option.resolveModules.map(String) + } +} + +/** + * Gets "resolveModules" setting. + * + * 1. This checks `options` property, then returns it if exists. + * 2. This checks `settings.n` | `settings.node` property, then returns it if exists. + * 3. This returns `[]`. + * + * @param {import('eslint').Rule.RuleContext} context - The rule context. + * @returns {string[]} A list of modules paths. + */ +module.exports = function getResolveModules(context, optionIndex = 0) { + return ( + get(context.options?.[optionIndex]) ?? + get(context.settings?.n) ?? + get(context.settings?.node) ?? + DEFAULT_VALUE + ) +} + +module.exports.schema = { + type: "array", + items: { type: "string" }, + uniqueItems: true, +} diff --git a/lib/util/import-target.js b/lib/util/import-target.js index 75a5e11b..2ca6fa02 100644 --- a/lib/util/import-target.js +++ b/lib/util/import-target.js @@ -71,6 +71,7 @@ function getTSConfigAliases(context) { * @typedef Options * @property {string[]} [extensions] * @property {string[]} [paths] + * @property {string[]} [modules] * @property {string} basedir */ /** @typedef { 'unknown' | 'relative' | 'absolute' | 'node' | 'npm' | 'http' } ModuleType */ @@ -309,6 +310,10 @@ module.exports = class ImportTarget { this.resolverConfig.extensions = this.options.extensions } + if (this.options.modules) { + this.resolverConfig.modules = this.options.modules + } + if (isTypescript(this.context)) { this.resolverConfig.alias = getTSConfigAliases(this.context) this.resolverConfig.extensionAlias = getTypescriptExtensionMap( diff --git a/lib/util/visit-import.js b/lib/util/visit-import.js index 164ba9b6..ce38da7f 100644 --- a/lib/util/visit-import.js +++ b/lib/util/visit-import.js @@ -7,6 +7,7 @@ const path = require("path") const { isBuiltin } = require("node:module") const getResolvePaths = require("./get-resolve-paths") +const getResolveModules = require("./get-resolve-modules") const getTryExtensions = require("./get-try-extensions") const ImportTarget = require("./import-target") const stripImportPathParams = require("./strip-import-path-params") @@ -41,8 +42,9 @@ module.exports = function visitImport( path.resolve(context.filename ?? context.getFilename()) ) const paths = getResolvePaths(context, optionIndex) + const modules = getResolveModules(context, optionIndex) const extensions = getTryExtensions(context, optionIndex) - const options = { basedir, paths, extensions } + const options = { basedir, paths, modules, extensions } /** * @param {( diff --git a/lib/util/visit-require.js b/lib/util/visit-require.js index 83924b8f..8fd1f807 100644 --- a/lib/util/visit-require.js +++ b/lib/util/visit-require.js @@ -12,6 +12,7 @@ const { } = require("@eslint-community/eslint-utils") const { isBuiltin } = require("node:module") const getResolvePaths = require("./get-resolve-paths") +const getResolveModules = require("./get-resolve-modules") const getTryExtensions = require("./get-try-extensions") const ImportTarget = require("./import-target") const stripImportPathParams = require("./strip-import-path-params") @@ -42,8 +43,9 @@ module.exports = function visitRequire( path.resolve(context.filename ?? context.getFilename()) ) const paths = getResolvePaths(context) + const modules = getResolveModules(context) const extensions = getTryExtensions(context) - const options = { basedir, paths, extensions } + const options = { basedir, paths, modules, extensions } return { "Program:exit"(node) { diff --git a/tests/fixtures/no-missing/my_modules/my-module/a-file.js b/tests/fixtures/no-missing/my_modules/my-module/a-file.js new file mode 100644 index 00000000..e69de29b diff --git a/tests/fixtures/no-missing/my_modules/my-module/package.json b/tests/fixtures/no-missing/my_modules/my-module/package.json new file mode 100644 index 00000000..0dcbe24d --- /dev/null +++ b/tests/fixtures/no-missing/my_modules/my-module/package.json @@ -0,0 +1,3 @@ +{ + "main": "./a-file.js" +} diff --git a/tests/lib/rules/no-missing-require.js b/tests/lib/rules/no-missing-require.js index c21541d6..add29899 100644 --- a/tests/lib/rules/no-missing-require.js +++ b/tests/lib/rules/no-missing-require.js @@ -131,6 +131,27 @@ ruleTester.run("no-missing-require", rule, { options: [{ resolvePaths: ["tests"] }], }, + // resolveModules + { + filename: fixture("test.js"), + code: "require('a');", + options: [ + { + resolveModules: [fixture("./")], + }, + ], + }, + + { + filename: fixture("test.js"), + code: "require('my-module');", + options: [ + { + resolveModules: [fixture("my_modules")], + }, + ], + }, + // Ignores it if not callee. { filename: fixture("test.js"), From 542dbd1f6817f51f8bf8271ee3f7870cb38d63bc Mon Sep 17 00:00:00 2001 From: GertSallaerts <1267900+GertSallaerts@users.noreply.github.com> Date: Fri, 15 Nov 2024 12:27:10 +0100 Subject: [PATCH 2/3] feat(resolve): allow overriding enhanced-resolve's options --- docs/rules/no-extraneous-import.md | 4 +-- docs/rules/no-extraneous-require.md | 4 +-- docs/rules/no-missing-import.md | 4 +-- docs/rules/no-missing-require.md | 4 +-- docs/rules/no-unpublished-import.md | 4 +-- docs/rules/no-unpublished-require.md | 4 +-- docs/shared-settings.md | 22 +++++-------- lib/rules/no-extraneous-import.js | 4 +-- lib/rules/no-extraneous-require.js | 4 +-- lib/rules/no-missing-import.js | 4 +-- lib/rules/no-missing-require.js | 4 +-- lib/rules/no-unpublished-import.js | 4 +-- lib/rules/no-unpublished-require.js | 4 +-- lib/util/get-resolve-modules.js | 45 ------------------------- lib/util/get-resolver-config.js | 47 +++++++++++++++++++++++++++ lib/util/import-target.js | 11 ++++--- lib/util/visit-import.js | 6 ++-- lib/util/visit-require.js | 6 ++-- tests/lib/rules/no-missing-require.js | 10 ++++-- 19 files changed, 99 insertions(+), 96 deletions(-) delete mode 100644 lib/util/get-resolve-modules.js create mode 100644 lib/util/get-resolver-config.js diff --git a/docs/rules/no-extraneous-import.md b/docs/rules/no-extraneous-import.md index dc51b261..1dee0836 100644 --- a/docs/rules/no-extraneous-import.md +++ b/docs/rules/no-extraneous-import.md @@ -37,9 +37,9 @@ Please see the shared settings documentation for more information. This can be configured in the rule options or as a shared setting [`settings.resolvePaths`](../shared-settings.md#resolvepaths). Please see the shared settings documentation for more information. -#### resolveModules +#### resolverConfig -This can be configured in the rule options or as a shared setting [`settings.resolveModules`](../shared-settings.md#resolvemodules). +This can be configured in the rule options or as a shared setting [`settings.resolverConfig`](../shared-settings.md#resolverconfig). Please see the shared settings documentation for more information. #### convertPath diff --git a/docs/rules/no-extraneous-require.md b/docs/rules/no-extraneous-require.md index d3c2e827..430a1bc0 100644 --- a/docs/rules/no-extraneous-require.md +++ b/docs/rules/no-extraneous-require.md @@ -38,9 +38,9 @@ Please see the shared settings documentation for more information. This can be configured in the rule options or as a shared setting [`settings.resolvePaths`](../shared-settings.md#resolvepaths). Please see the shared settings documentation for more information. -#### resolveModules +#### resolverConfig -This can be configured in the rule options or as a shared setting [`settings.resolveModules`](../shared-settings.md#resolvemodules). +This can be configured in the rule options or as a shared setting [`settings.resolverConfig`](../shared-settings.md#resolverconfig). Please see the shared settings documentation for more information. #### convertPath diff --git a/docs/rules/no-missing-import.md b/docs/rules/no-missing-import.md index 4cdd22f2..4d284cc2 100644 --- a/docs/rules/no-missing-import.md +++ b/docs/rules/no-missing-import.md @@ -52,9 +52,9 @@ Please see the shared settings documentation for more information. This can be configured in the rule options or as a shared setting [`settings.resolvePaths`](../shared-settings.md#resolvepaths). Please see the shared settings documentation for more information. -#### resolveModules +#### resolverConfig -This can be configured in the rule options or as a shared setting [`settings.resolveModules`](../shared-settings.md#resolvemodules). +This can be configured in the rule options or as a shared setting [`settings.resolverConfig`](../shared-settings.md#resolverconfig). Please see the shared settings documentation for more information. #### tsconfigPath diff --git a/docs/rules/no-missing-require.md b/docs/rules/no-missing-require.md index 827b02ac..79c78c4d 100644 --- a/docs/rules/no-missing-require.md +++ b/docs/rules/no-missing-require.md @@ -60,9 +60,9 @@ Please see the shared settings documentation for more information. This can be configured in the rule options or as a shared setting [`settings.resolvePaths`](../shared-settings.md#resolvepaths). Please see the shared settings documentation for more information. -#### resolveModules +#### resolverConfig -This can be configured in the rule options or as a shared setting [`settings.resolveModules`](../shared-settings.md#resolvemodules). +This can be configured in the rule options or as a shared setting [`settings.resolverConfig`](../shared-settings.md#resolverconfig). Please see the shared settings documentation for more information. #### tryExtensions diff --git a/docs/rules/no-unpublished-import.md b/docs/rules/no-unpublished-import.md index c8163d81..e944fe98 100644 --- a/docs/rules/no-unpublished-import.md +++ b/docs/rules/no-unpublished-import.md @@ -41,9 +41,9 @@ Please see the shared settings documentation for more information. This can be configured in the rule options or as a shared setting [`settings.resolvePaths`](../shared-settings.md#resolvepaths). Please see the shared settings documentation for more information. -#### resolveModules +#### resolverConfig -This can be configured in the rule options or as a shared setting [`settings.resolveModules`](../shared-settings.md#resolvemodules). +This can be configured in the rule options or as a shared setting [`settings.resolverConfig`](../shared-settings.md#resolverconfig). Please see the shared settings documentation for more information. #### convertPath diff --git a/docs/rules/no-unpublished-require.md b/docs/rules/no-unpublished-require.md index e746b4b1..f227b78e 100644 --- a/docs/rules/no-unpublished-require.md +++ b/docs/rules/no-unpublished-require.md @@ -43,9 +43,9 @@ Please see the shared settings documentation for more information. This can be configured in the rule options or as a shared setting [`settings.resolvePaths`](../shared-settings.md#resolvepaths). Please see the shared settings documentation for more information. -#### resolveModules +#### resolverConfig -This can be configured in the rule options or as a shared setting [`settings.resolveModules`](../shared-settings.md#resolvemodules). +This can be configured in the rule options or as a shared setting [`settings.resolverConfig`](../shared-settings.md#resolverconfig). Please see the shared settings documentation for more information. #### convertPath diff --git a/docs/shared-settings.md b/docs/shared-settings.md index 7709db93..d7544761 100644 --- a/docs/shared-settings.md +++ b/docs/shared-settings.md @@ -60,28 +60,24 @@ If a path is relative, it will be resolved from CWD. { "resolvePaths": [] } ``` -## resolveModules +## resolverConfig -Adds additional directories that should be searched when resolving modules. +Override the options generated by this plugin for resolving require and import statements. These +options are passed down to `enhanced-resolve`'s factory method and are documented [here on their +GitHub repository](https://github.com/webpack/enhanced-resolve?tab=readme-ov-file#resolver-options). -Absolute and relative paths can both be used, but be aware that they will behave a bit differently. +The options you define here are assigned over the default options generated by the plugin. -A relative path will be scanned similarly to how Node scans for node_modules, by looking through the current directory as well as its ancestors (i.e. ./node_modules, ../node_modules, and on). - -With an absolute path, it will only search in the given directory. - -If a path is relative, it will be resolved from CWD. - -### Example resolveModules +### Example resolverConfig ```json -{ "resolveModules": ["/path/to/somewhere", "../relative/path", "node_modules"] } +{ "resolverConfig": { "modules": ["node_modules", "bower_components"] } } ``` -### Default resolveModules +### Default resolverConfig ```json -{ "resolveModules": ["node_modules"] } +{ "resolverConfig": {} } ``` ## convertPath diff --git a/lib/rules/no-extraneous-import.js b/lib/rules/no-extraneous-import.js index 43f96e2c..c7e46b3d 100644 --- a/lib/rules/no-extraneous-import.js +++ b/lib/rules/no-extraneous-import.js @@ -8,7 +8,7 @@ const { checkExtraneous, messages } = require("../util/check-extraneous") const getAllowModules = require("../util/get-allow-modules") const getConvertPath = require("../util/get-convert-path") const getResolvePaths = require("../util/get-resolve-paths") -const getResolveModules = require("../util/get-resolve-modules") +const getResolverConfig = require("../util/get-resolver-config") const visitImport = require("../util/visit-import") /** @type {import('eslint').Rule.RuleModule} */ @@ -29,7 +29,7 @@ module.exports = { allowModules: getAllowModules.schema, convertPath: getConvertPath.schema, resolvePaths: getResolvePaths.schema, - resolveModules: getResolveModules.schema, + resolverConfig: getResolverConfig.schema, }, additionalProperties: false, }, diff --git a/lib/rules/no-extraneous-require.js b/lib/rules/no-extraneous-require.js index 55f18edf..6ee15e18 100644 --- a/lib/rules/no-extraneous-require.js +++ b/lib/rules/no-extraneous-require.js @@ -8,7 +8,7 @@ const { checkExtraneous, messages } = require("../util/check-extraneous") const getAllowModules = require("../util/get-allow-modules") const getConvertPath = require("../util/get-convert-path") const getResolvePaths = require("../util/get-resolve-paths") -const getResolveModules = require("../util/get-resolve-modules") +const getResolverConfig = require("../util/get-resolver-config") const getTryExtensions = require("../util/get-try-extensions") const visitRequire = require("../util/visit-require") @@ -30,7 +30,7 @@ module.exports = { allowModules: getAllowModules.schema, convertPath: getConvertPath.schema, resolvePaths: getResolvePaths.schema, - resolveModules: getResolveModules.schema, + resolverConfig: getResolverConfig.schema, tryExtensions: getTryExtensions.schema, }, additionalProperties: false, diff --git a/lib/rules/no-missing-import.js b/lib/rules/no-missing-import.js index 851f08d9..44a3e5db 100644 --- a/lib/rules/no-missing-import.js +++ b/lib/rules/no-missing-import.js @@ -7,7 +7,7 @@ const { checkExistence, messages } = require("../util/check-existence") const getAllowModules = require("../util/get-allow-modules") const getResolvePaths = require("../util/get-resolve-paths") -const getResolveModules = require("../util/get-resolve-modules") +const getResolverConfig = require("../util/get-resolver-config") const getTryExtensions = require("../util/get-try-extensions") const getTSConfig = require("../util/get-tsconfig") const getTypescriptExtensionMap = require("../util/get-typescript-extension-map") @@ -30,7 +30,7 @@ module.exports = { properties: { allowModules: getAllowModules.schema, resolvePaths: getResolvePaths.schema, - resolveModules: getResolveModules.schema, + resolverConfig: getResolverConfig.schema, tryExtensions: getTryExtensions.schema, ignoreTypeImport: { type: "boolean", default: false }, tsconfigPath: getTSConfig.schema, diff --git a/lib/rules/no-missing-require.js b/lib/rules/no-missing-require.js index 987943db..fb7a1cd5 100644 --- a/lib/rules/no-missing-require.js +++ b/lib/rules/no-missing-require.js @@ -7,7 +7,7 @@ const { checkExistence, messages } = require("../util/check-existence") const getAllowModules = require("../util/get-allow-modules") const getResolvePaths = require("../util/get-resolve-paths") -const getResolveModules = require("../util/get-resolve-modules") +const getResolverConfig = require("../util/get-resolver-config") const getTSConfig = require("../util/get-tsconfig") const getTryExtensions = require("../util/get-try-extensions") const getTypescriptExtensionMap = require("../util/get-typescript-extension-map") @@ -31,7 +31,7 @@ module.exports = { allowModules: getAllowModules.schema, tryExtensions: getTryExtensions.schema, resolvePaths: getResolvePaths.schema, - resolveModules: getResolveModules.schema, + resolverConfig: getResolverConfig.schema, typescriptExtensionMap: getTypescriptExtensionMap.schema, tsconfigPath: getTSConfig.schema, }, diff --git a/lib/rules/no-unpublished-import.js b/lib/rules/no-unpublished-import.js index b15d290e..e517d454 100644 --- a/lib/rules/no-unpublished-import.js +++ b/lib/rules/no-unpublished-import.js @@ -8,7 +8,7 @@ const { checkPublish, messages } = require("../util/check-publish") const getAllowModules = require("../util/get-allow-modules") const getConvertPath = require("../util/get-convert-path") const getResolvePaths = require("../util/get-resolve-paths") -const getResolveModules = require("../util/get-resolve-modules") +const getResolverConfig = require("../util/get-resolver-config") const visitImport = require("../util/visit-import") /** @type {import('eslint').Rule.RuleModule} */ @@ -29,7 +29,7 @@ module.exports = { allowModules: getAllowModules.schema, convertPath: getConvertPath.schema, resolvePaths: getResolvePaths.schema, - resolveModules: getResolveModules.schema, + resolverConfig: getResolverConfig.schema, ignoreTypeImport: { type: "boolean", default: false }, ignorePrivate: { type: "boolean", default: true }, }, diff --git a/lib/rules/no-unpublished-require.js b/lib/rules/no-unpublished-require.js index 9ab4e5c7..a0f1bb92 100644 --- a/lib/rules/no-unpublished-require.js +++ b/lib/rules/no-unpublished-require.js @@ -8,7 +8,7 @@ const { checkPublish, messages } = require("../util/check-publish") const getAllowModules = require("../util/get-allow-modules") const getConvertPath = require("../util/get-convert-path") const getResolvePaths = require("../util/get-resolve-paths") -const getResolveModules = require("../util/get-resolve-modules") +const getResolverConfig = require("../util/get-resolver-config") const getTryExtensions = require("../util/get-try-extensions") const visitRequire = require("../util/visit-require") @@ -30,7 +30,7 @@ module.exports = { allowModules: getAllowModules.schema, convertPath: getConvertPath.schema, resolvePaths: getResolvePaths.schema, - resolveModules: getResolveModules.schema, + resolverConfig: getResolverConfig.schema, tryExtensions: getTryExtensions.schema, ignorePrivate: { type: "boolean", default: true }, }, diff --git a/lib/util/get-resolve-modules.js b/lib/util/get-resolve-modules.js deleted file mode 100644 index 4aeea0af..00000000 --- a/lib/util/get-resolve-modules.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @author Toru Nagashima - * See LICENSE file in root directory for full license. - */ -"use strict" - -/** @type {string[]} */ -const DEFAULT_VALUE = ["node_modules"] - -/** - * Gets `resolveModules` property from a given option object. - * - * @param {{ resolveModules: unknown[] } | undefined} option - An option object to get. - * @returns {string[] | undefined} The `allowModules` value, or `null`. - */ -function get(option) { - if (Array.isArray(option?.resolveModules)) { - return option.resolveModules.map(String) - } -} - -/** - * Gets "resolveModules" setting. - * - * 1. This checks `options` property, then returns it if exists. - * 2. This checks `settings.n` | `settings.node` property, then returns it if exists. - * 3. This returns `[]`. - * - * @param {import('eslint').Rule.RuleContext} context - The rule context. - * @returns {string[]} A list of modules paths. - */ -module.exports = function getResolveModules(context, optionIndex = 0) { - return ( - get(context.options?.[optionIndex]) ?? - get(context.settings?.n) ?? - get(context.settings?.node) ?? - DEFAULT_VALUE - ) -} - -module.exports.schema = { - type: "array", - items: { type: "string" }, - uniqueItems: true, -} diff --git a/lib/util/get-resolver-config.js b/lib/util/get-resolver-config.js new file mode 100644 index 00000000..d7fd95b9 --- /dev/null +++ b/lib/util/get-resolver-config.js @@ -0,0 +1,47 @@ +/** + * @author Toru Nagashima + * See LICENSE file in root directory for full license. + */ +"use strict" + +/** + * @typedef {Partial} ResolverConfig + */ + +/** @type {ResolverConfig} */ +const DEFAULT_VALUE = {} + +/** + * Gets `resolverConfig` property from a given option object. + * + * @param {{ resolverConfig: ResolverConfig } | undefined} option - An option object to get. + * @returns {ResolverConfig | undefined} The `allowModules` value, or `null`. + */ +function get(option) { + if (option?.resolverConfig) return option.resolverConfig +} + +/** + * Gets "resolverConfig" setting. + * + * 1. This checks `options` property, then returns it if exists. + * 2. This checks `settings.n` | `settings.node` property, then returns it if exists. + * 3. This returns `[]`. + * + * @param {import('eslint').Rule.RuleContext} context - The rule context. + * @returns {ResolverConfig} A resolver config object. + */ +module.exports = function getResolverConfig(context, optionIndex = 0) { + return ( + get(context.options?.[optionIndex]) ?? + get(context.settings?.n) ?? + get(context.settings?.node) ?? + DEFAULT_VALUE + ) +} + +module.exports.schema = { + type: "object", + properties: {}, + additionalProperties: true, +} diff --git a/lib/util/import-target.js b/lib/util/import-target.js index 2ca6fa02..f598c017 100644 --- a/lib/util/import-target.js +++ b/lib/util/import-target.js @@ -71,7 +71,7 @@ function getTSConfigAliases(context) { * @typedef Options * @property {string[]} [extensions] * @property {string[]} [paths] - * @property {string[]} [modules] + * @property {Partial} [resolverConfig] * @property {string} basedir */ /** @typedef { 'unknown' | 'relative' | 'absolute' | 'node' | 'npm' | 'http' } ModuleType */ @@ -310,10 +310,6 @@ module.exports = class ImportTarget { this.resolverConfig.extensions = this.options.extensions } - if (this.options.modules) { - this.resolverConfig.modules = this.options.modules - } - if (isTypescript(this.context)) { this.resolverConfig.alias = getTSConfigAliases(this.context) this.resolverConfig.extensionAlias = getTypescriptExtensionMap( @@ -321,6 +317,11 @@ module.exports = class ImportTarget { ).backward } + this.resolverConfig = { + ...this.resolverConfig, + ...this.options.resolverConfig, + } + const requireResolve = resolver.create.sync(this.resolverConfig) const cwd = this.context.settings?.cwd ?? process.cwd() diff --git a/lib/util/visit-import.js b/lib/util/visit-import.js index ce38da7f..aee0a194 100644 --- a/lib/util/visit-import.js +++ b/lib/util/visit-import.js @@ -7,7 +7,7 @@ const path = require("path") const { isBuiltin } = require("node:module") const getResolvePaths = require("./get-resolve-paths") -const getResolveModules = require("./get-resolve-modules") +const getResolverConfig = require("./get-resolver-config") const getTryExtensions = require("./get-try-extensions") const ImportTarget = require("./import-target") const stripImportPathParams = require("./strip-import-path-params") @@ -42,9 +42,9 @@ module.exports = function visitImport( path.resolve(context.filename ?? context.getFilename()) ) const paths = getResolvePaths(context, optionIndex) - const modules = getResolveModules(context, optionIndex) + const resolverConfig = getResolverConfig(context, optionIndex) const extensions = getTryExtensions(context, optionIndex) - const options = { basedir, paths, modules, extensions } + const options = { basedir, paths, extensions, resolverConfig } /** * @param {( diff --git a/lib/util/visit-require.js b/lib/util/visit-require.js index 8fd1f807..ba4026f4 100644 --- a/lib/util/visit-require.js +++ b/lib/util/visit-require.js @@ -12,7 +12,7 @@ const { } = require("@eslint-community/eslint-utils") const { isBuiltin } = require("node:module") const getResolvePaths = require("./get-resolve-paths") -const getResolveModules = require("./get-resolve-modules") +const getResolverConfig = require("./get-resolver-config") const getTryExtensions = require("./get-try-extensions") const ImportTarget = require("./import-target") const stripImportPathParams = require("./strip-import-path-params") @@ -43,9 +43,9 @@ module.exports = function visitRequire( path.resolve(context.filename ?? context.getFilename()) ) const paths = getResolvePaths(context) - const modules = getResolveModules(context) + const resolverConfig = getResolverConfig(context) const extensions = getTryExtensions(context) - const options = { basedir, paths, modules, extensions } + const options = { basedir, paths, extensions, resolverConfig } return { "Program:exit"(node) { diff --git a/tests/lib/rules/no-missing-require.js b/tests/lib/rules/no-missing-require.js index add29899..da41a1b5 100644 --- a/tests/lib/rules/no-missing-require.js +++ b/tests/lib/rules/no-missing-require.js @@ -131,13 +131,15 @@ ruleTester.run("no-missing-require", rule, { options: [{ resolvePaths: ["tests"] }], }, - // resolveModules + // resolverConfig { filename: fixture("test.js"), code: "require('a');", options: [ { - resolveModules: [fixture("./")], + resolverConfig: { + modules: [fixture("./")], + }, }, ], }, @@ -147,7 +149,9 @@ ruleTester.run("no-missing-require", rule, { code: "require('my-module');", options: [ { - resolveModules: [fixture("my_modules")], + resolverConfig: { + modules: [fixture("my_modules")], + }, }, ], }, From ceb33ff935ce9d6ba7453f3dc1b00257d1309637 Mon Sep 17 00:00:00 2001 From: GertSallaerts <1267900+GertSallaerts@users.noreply.github.com> Date: Fri, 6 Dec 2024 08:12:26 +0100 Subject: [PATCH 3/3] chore(resolve): update docs to mention we only support resolverConfig.modules --- docs/shared-settings.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/shared-settings.md b/docs/shared-settings.md index d7544761..2e83967f 100644 --- a/docs/shared-settings.md +++ b/docs/shared-settings.md @@ -62,12 +62,16 @@ If a path is relative, it will be resolved from CWD. ## resolverConfig -Override the options generated by this plugin for resolving require and import statements. These -options are passed down to `enhanced-resolve`'s factory method and are documented [here on their -GitHub repository](https://github.com/webpack/enhanced-resolve?tab=readme-ov-file#resolver-options). +Override the options generated by this plugin for resolving require and import statements. + +While these options are passed down to [`enhanced-resolve`](https://github.com/webpack/enhanced-resolve)'s factory method, we only support a subset of the options they allow. These are documented below. The options you define here are assigned over the default options generated by the plugin. +Supported options: + +- `resolverConfig.modules`: A list of directories to resolve modules from, can be absolute path or folder name + ### Example resolverConfig ```json