Skip to content

Commit

Permalink
feat(import-target): Pass raw "enhanced-resolve" error as message
Browse files Browse the repository at this point in the history
  • Loading branch information
scagood committed Apr 15, 2024
1 parent bad971c commit a256b25
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 37 deletions.
7 changes: 5 additions & 2 deletions lib/util/check-existence.js
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
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
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
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)}'`,
},
},
]
}

/** @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
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

0 comments on commit a256b25

Please sign in to comment.