Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(import-target): Add resolution error reason #231

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 5 additions & 2 deletions lib/util/check-existence.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, *>} */ (target),
data: { resolveError: target.resolveError },
})
}

Expand Down Expand Up @@ -85,5 +88,5 @@ exports.checkExistence = function checkExistence(context, targets) {
}

exports.messages = {
notFound: '"{{name}}" is not found.',
notFound: "{{resolveError}}",
}
26 changes: 23 additions & 3 deletions lib/util/import-target.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
}
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions tests/lib/configs/eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
73 changes: 55 additions & 18 deletions tests/lib/rules/no-missing-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)}'`,
},
Comment on lines +46 to +48

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
data: {
resolveError: `Can't resolve '${name}' in '${fixture(dir)}'`,
},

},
]
}

/** @type {import('eslint').RuleTester} */
const ruleTester = new RuleTester({
languageOptions: {
sourceType: "module",
Expand Down Expand Up @@ -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()
Expand All @@ -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"),
},
]
: []),
Expand Down
33 changes: 22 additions & 11 deletions tests/lib/rules/no-missing-require.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down Expand Up @@ -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"),
},
],
})
Expand Down