From a256b25ed841d9ccf71b3514c8255c59a879890a Mon Sep 17 00:00:00 2001 From: scagood <2230835+scagood@users.noreply.github.com> Date: Mon, 8 Apr 2024 19:36:27 +0100 Subject: [PATCH] feat(import-target): Pass raw "enhanced-resolve" error as message --- lib/util/check-existence.js | 7 +- lib/util/import-target.js | 26 ++++++- .../misconfigured-default/build/index.js | 1 + .../misconfigured-default/package.json | 9 +++ .../misconfigured-default/src/index.ts | 1 + tests/lib/configs/eslintrc.js | 6 +- tests/lib/rules/no-missing-import.js | 73 ++++++++++++++----- tests/lib/rules/no-missing-require.js | 33 ++++++--- 8 files changed, 119 insertions(+), 37 deletions(-) create mode 100644 tests/fixtures/no-missing/node_modules/misconfigured-default/build/index.js create mode 100644 tests/fixtures/no-missing/node_modules/misconfigured-default/package.json create mode 100644 tests/fixtures/no-missing/node_modules/misconfigured-default/src/index.ts diff --git a/lib/util/check-existence.js b/lib/util/check-existence.js index a7899a75..d09770c7 100644 --- a/lib/util/check-existence.js +++ b/lib/util/check-existence.js @@ -17,13 +17,16 @@ const { convertJsExtensionToTs } = require("../util/map-typescript-extension") * @returns {void} */ function markMissing(context, target) { + // This should never happen... this is just a fallback for typescript + target.resolveError ??= `"${target.name}" is not found` + context.report({ node: target.node, loc: /** @type {import('eslint').AST.SourceLocation} */ ( target.node.loc ), messageId: "notFound", - data: /** @type {Record} */ (target), + data: { resolveError: target.resolveError }, }) } @@ -85,5 +88,5 @@ exports.checkExistence = function checkExistence(context, targets) { } exports.messages = { - notFound: '"{{name}}" is not found.', + notFound: "{{resolveError}}", } diff --git a/lib/util/import-target.js b/lib/util/import-target.js index 54741451..4fbc086c 100644 --- a/lib/util/import-target.js +++ b/lib/util/import-target.js @@ -131,6 +131,12 @@ module.exports = class ImportTarget { */ this.moduleName = this.getModuleName() + /** + * This is the full resolution failure reasons + * @type {string | null} + */ + this.resolveError = null + /** * The full path of this import target. * If the target is a module and it does not exist then this is `null`. @@ -239,6 +245,19 @@ module.exports = class ImportTarget { return [this.options.basedir] } + /** + * @param {string} baseDir + * @param {unknown} error + * @returns {void} + */ + handleResolutionError(baseDir, error) { + if (error instanceof Error === false) { + throw error + } + + this.resolveError = error.message + } + /** * Resolve the given id to file paths. * @returns {string | null} The resolved path. @@ -286,12 +305,13 @@ module.exports = class ImportTarget { const cwd = this.context.settings?.cwd ?? process.cwd() for (const directory of this.getPaths()) { + const baseDir = resolve(cwd, directory) + try { - const baseDir = resolve(cwd, directory) const resolved = requireResolve(baseDir, this.name) if (typeof resolved === "string") return resolved - } catch { - continue + } catch (error) { + this.handleResolutionError(baseDir, error) } } diff --git a/tests/fixtures/no-missing/node_modules/misconfigured-default/build/index.js b/tests/fixtures/no-missing/node_modules/misconfigured-default/build/index.js new file mode 100644 index 00000000..336ce12b --- /dev/null +++ b/tests/fixtures/no-missing/node_modules/misconfigured-default/build/index.js @@ -0,0 +1 @@ +export {} diff --git a/tests/fixtures/no-missing/node_modules/misconfigured-default/package.json b/tests/fixtures/no-missing/node_modules/misconfigured-default/package.json new file mode 100644 index 00000000..34cec732 --- /dev/null +++ b/tests/fixtures/no-missing/node_modules/misconfigured-default/package.json @@ -0,0 +1,9 @@ +{ + "name": "misconfigured-default", + "exports": { + ".": { + "default": "./build/index.js", + "types": "./src/index.ts" + } + } +} diff --git a/tests/fixtures/no-missing/node_modules/misconfigured-default/src/index.ts b/tests/fixtures/no-missing/node_modules/misconfigured-default/src/index.ts new file mode 100644 index 00000000..336ce12b --- /dev/null +++ b/tests/fixtures/no-missing/node_modules/misconfigured-default/src/index.ts @@ -0,0 +1 @@ +export {} diff --git a/tests/lib/configs/eslintrc.js b/tests/lib/configs/eslintrc.js index b79b15b7..9fc00bd6 100644 --- a/tests/lib/configs/eslintrc.js +++ b/tests/lib/configs/eslintrc.js @@ -84,7 +84,7 @@ describe("node/recommended config", () => { endLine: 1, line: 1, messageId: "notFound", - message: '"foo" is not found.', + message: `Can't resolve 'foo' in '${root}'`, nodeType: "Literal", ruleId: "n/no-missing-import", severity: 2, @@ -124,7 +124,7 @@ describe("node/recommended config", () => { endLine: 1, line: 1, messageId: "notFound", - message: '"foo" is not found.', + message: `Can't resolve 'foo' in '${root}'`, nodeType: "Literal", ruleId: "n/no-missing-import", severity: 2, @@ -165,7 +165,7 @@ describe("node/recommended config", () => { endLine: 1, line: 1, messageId: "notFound", - message: '"foo" is not found.', + message: `Can't resolve 'foo' in '${root}'`, nodeType: "Literal", ruleId: "n/no-missing-import", severity: 2, diff --git a/tests/lib/rules/no-missing-import.js b/tests/lib/rules/no-missing-import.js index 66190d3b..3236754c 100644 --- a/tests/lib/rules/no-missing-import.js +++ b/tests/lib/rules/no-missing-import.js @@ -39,6 +39,18 @@ function fixture(name) { return path.resolve(__dirname, "../../fixtures/no-missing", name) } +function cantResolve(name, dir = "") { + return [ + { + messageId: "notFound", + data: { + resolveError: `Can't resolve '${name}' in '${fixture(dir)}'`, + }, + }, + ] +} + +/** @type {import('eslint').RuleTester} */ const ruleTester = new RuleTester({ languageOptions: { sourceType: "module", @@ -320,89 +332,114 @@ ruleTester.run("no-missing-import", rule, { { filename: fixture("test.js"), code: "import abc from 'no-exist-package-0';", - errors: ['"no-exist-package-0" is not found.'], + errors: cantResolve("no-exist-package-0"), }, { filename: fixture("test.js"), code: "import abcdef from 'esm-module/sub.mjs';", - errors: ['"esm-module/sub.mjs" is not found.'], + errors: [ + { + messageId: "notFound", + data: { + resolveError: [ + "Package path ./sub.mjs is not exported from package", + fixture("node_modules/esm-module"), + `(see exports field in ${fixture("node_modules/esm-module/package.json")})`, + ].join(" "), + }, + }, + ], }, { filename: fixture("test.js"), code: "import test from '@mysticatea/test';", - errors: ['"@mysticatea/test" is not found.'], + errors: cantResolve("@mysticatea/test"), }, { filename: fixture("test.js"), code: "import c from './c';", - errors: ['"./c" is not found.'], + errors: cantResolve("./c"), }, { filename: fixture("test.ts"), code: "import d from './d';", - errors: ['"./d" is not found.'], + errors: cantResolve("./d"), }, { filename: fixture("test.js"), code: "import d from './d';", - errors: ['"./d" is not found.'], + errors: cantResolve("./d"), }, { filename: fixture("test.js"), code: "import a from './a.json';", - errors: ['"./a.json" is not found.'], + errors: cantResolve("./a.json"), }, // Should work fine if the filename is relative. { - filename: "tests/fixtures/no-missing/test.js", + filename: fixture("test.js"), code: "import eslint from 'no-exist-package-0';", - errors: ['"no-exist-package-0" is not found.'], + errors: cantResolve("no-exist-package-0"), }, { filename: "tests/fixtures/no-missing/test.js", code: "import c from './c';", - errors: ['"./c" is not found.'], + errors: cantResolve("./c"), }, // Relative paths to a directory should work. { filename: fixture("test.js"), code: "import a from './bar';", - errors: ['"./bar" is not found.'], + errors: cantResolve("./bar"), }, { filename: fixture("test.js"), code: "import a from './bar/';", - errors: ['"./bar/" is not found.'], + errors: cantResolve("./bar/"), }, // Relative paths to an existing directory should not work. { filename: fixture("test.js"), code: "import a from '.';", - errors: ['"." is not found.'], + errors: cantResolve("."), }, { filename: fixture("test.js"), code: "import a from './';", - errors: ['"./" is not found.'], + errors: cantResolve("./"), }, { filename: fixture("test.js"), code: "import a from './foo';", - errors: ['"./foo" is not found.'], + errors: cantResolve("./foo"), }, { filename: fixture("test.js"), code: "import a from './foo/';", - errors: ['"./foo/" is not found.'], + errors: cantResolve("./foo/"), }, // Case sensitive { filename: fixture("test.js"), code: "import a from './A.js';", - errors: ['"./A.js" is not found.'], + errors: cantResolve("./A.js"), + }, + + { + filename: fixture("test.js"), + code: "import 'misconfigured-default';", + errors: [ + { + messageId: "notFound", + data: { + name: "misconfigured-default", + resolveError: "Default condition should be last one", + }, + }, + ], }, // import() @@ -412,7 +449,7 @@ ruleTester.run("no-missing-import", rule, { filename: fixture("test.js"), code: "function f() { import('no-exist-package-0') }", languageOptions: { ecmaVersion: 2020 }, - errors: ['"no-exist-package-0" is not found.'], + errors: cantResolve("no-exist-package-0"), }, ] : []), diff --git a/tests/lib/rules/no-missing-require.js b/tests/lib/rules/no-missing-require.js index 97c8506b..c055217c 100644 --- a/tests/lib/rules/no-missing-require.js +++ b/tests/lib/rules/no-missing-require.js @@ -25,6 +25,17 @@ function fixture(name) { return path.resolve(__dirname, "../../fixtures/no-missing", name) } +function cantResolve(name, dir = "") { + return [ + { + messageId: "notFound", + data: { + resolveError: `Can't resolve '${name}' in '${fixture(dir)}'`, + }, + }, + ] +} + const ruleTester = new RuleTester() ruleTester.run("no-missing-require", rule, { valid: [ @@ -333,65 +344,65 @@ ruleTester.run("no-missing-require", rule, { { filename: fixture("test.js"), code: "require('no-exist-package-0');", - errors: ['"no-exist-package-0" is not found.'], + errors: cantResolve("no-exist-package-0"), }, { filename: fixture("test.js"), code: "require('@mysticatea/test');", - errors: ['"@mysticatea/test" is not found.'], + errors: cantResolve("@mysticatea/test"), }, { filename: fixture("test.js"), code: "require('./c');", - errors: ['"./c" is not found.'], + errors: cantResolve("./c"), }, { filename: fixture("test.js"), code: "require('./d');", - errors: ['"./d" is not found.'], + errors: cantResolve("./d"), }, { filename: fixture("test.js"), code: "require('./a.json');", - errors: ['"./a.json" is not found.'], + errors: cantResolve("./a.json"), }, // Should work fine if the filename is relative. { filename: "tests/fixtures/no-missing/test.js", code: "require('no-exist-package-0');", - errors: ['"no-exist-package-0" is not found.'], + errors: cantResolve("no-exist-package-0"), }, { filename: "tests/fixtures/no-missing/test.js", code: "require('./c');", - errors: ['"./c" is not found.'], + errors: cantResolve("./c"), }, // Relative paths to a directory should work. { filename: fixture("test.js"), code: "require('./bar');", - errors: ['"./bar" is not found.'], + errors: cantResolve("./bar"), }, { filename: fixture("test.js"), code: "require('./bar/');", - errors: ['"./bar/" is not found.'], + errors: cantResolve("./bar/"), }, // Case sensitive { filename: fixture("test.js"), code: "require('./A');", - errors: ['"./A" is not found.'], + errors: cantResolve("./A"), }, // require.resolve { filename: fixture("test.js"), code: "require.resolve('no-exist-package-0');", - errors: ['"no-exist-package-0" is not found.'], + errors: cantResolve("no-exist-package-0"), }, ], })