diff --git a/docs/rules/no-extraneous-import.md b/docs/rules/no-extraneous-import.md index fbeaaa63..1dee0836 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. +#### resolverConfig + +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 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..430a1bc0 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. +#### resolverConfig + +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 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..4d284cc2 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. +#### resolverConfig + +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 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..79c78c4d 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. +#### resolverConfig + +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 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..e944fe98 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. +#### resolverConfig + +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 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..f227b78e 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. +#### resolverConfig + +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 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..2e83967f 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": [] } ``` +## resolverConfig + +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 +{ "resolverConfig": { "modules": ["node_modules", "bower_components"] } } +``` + +### Default resolverConfig + +```json +{ "resolverConfig": {} } +``` + ## 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..c7e46b3d 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 getResolverConfig = require("../util/get-resolver-config") 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, + resolverConfig: getResolverConfig.schema, }, additionalProperties: false, }, diff --git a/lib/rules/no-extraneous-require.js b/lib/rules/no-extraneous-require.js index f733ee96..6ee15e18 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 getResolverConfig = require("../util/get-resolver-config") 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, + 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 2be89254..44a3e5db 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 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") @@ -29,6 +30,7 @@ module.exports = { properties: { allowModules: getAllowModules.schema, resolvePaths: getResolvePaths.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 cd2eabb7..fb7a1cd5 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 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") @@ -30,6 +31,7 @@ module.exports = { allowModules: getAllowModules.schema, tryExtensions: getTryExtensions.schema, resolvePaths: getResolvePaths.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 70911783..e517d454 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 getResolverConfig = require("../util/get-resolver-config") 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, + 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 a5f70bcf..a0f1bb92 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 getResolverConfig = require("../util/get-resolver-config") 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, + resolverConfig: getResolverConfig.schema, tryExtensions: getTryExtensions.schema, ignorePrivate: { type: "boolean", default: 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 75a5e11b..f598c017 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 {Partial} [resolverConfig] * @property {string} basedir */ /** @typedef { 'unknown' | 'relative' | 'absolute' | 'node' | 'npm' | 'http' } ModuleType */ @@ -316,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 164ba9b6..aee0a194 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 getResolverConfig = require("./get-resolver-config") 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 resolverConfig = getResolverConfig(context, optionIndex) const extensions = getTryExtensions(context, optionIndex) - const options = { basedir, paths, extensions } + const options = { basedir, paths, extensions, resolverConfig } /** * @param {( diff --git a/lib/util/visit-require.js b/lib/util/visit-require.js index 83924b8f..ba4026f4 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 getResolverConfig = require("./get-resolver-config") 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 resolverConfig = getResolverConfig(context) const extensions = getTryExtensions(context) - const options = { basedir, paths, extensions } + const options = { basedir, paths, extensions, resolverConfig } 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..da41a1b5 100644 --- a/tests/lib/rules/no-missing-require.js +++ b/tests/lib/rules/no-missing-require.js @@ -131,6 +131,31 @@ ruleTester.run("no-missing-require", rule, { options: [{ resolvePaths: ["tests"] }], }, + // resolverConfig + { + filename: fixture("test.js"), + code: "require('a');", + options: [ + { + resolverConfig: { + modules: [fixture("./")], + }, + }, + ], + }, + + { + filename: fixture("test.js"), + code: "require('my-module');", + options: [ + { + resolverConfig: { + modules: [fixture("my_modules")], + }, + }, + ], + }, + // Ignores it if not callee. { filename: fixture("test.js"),