Merge pull request #12631 from webpack/feature/resolve-hint

add some better hints when resolving fails
This commit is contained in:
Tobias Koppers 2021-02-09 12:25:37 +01:00 committed by GitHub
commit 4ab27ff723
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 146 additions and 23 deletions

View File

@ -750,35 +750,148 @@ class NormalModuleFactory extends ModuleFactory {
resolveContext,
(err, resolvedResource, resolvedResourceResolveData) => {
if (err) {
if (resolver.options.fullySpecified) {
resolver
.withOptions({
fullySpecified: false
})
.resolve(
contextInfo,
context,
unresolvedResource,
resolveContext,
(err2, resolvedResource) => {
if (!err2 && resolvedResource) {
const resource = parseResource(
resolvedResource
).path.replace(/^.*[\\/]/, "");
err.message += `
Did you mean '${resource}'?
return this._resolveResourceErrorHints(
err,
contextInfo,
context,
unresolvedResource,
resolver,
resolveContext,
(err2, hints) => {
if (err2) {
err.message += `
An fatal error happened during resolving additional hints for this error: ${err2.message}`;
err.stack += `
An fatal error happened during resolving additional hints for this error:
${err2.stack}`;
return callback(err);
}
if (hints && hints.length > 0) {
err.message += `
${hints.join("\n\n")}`;
}
callback(err);
}
);
}
callback(err, resolvedResource, resolvedResourceResolveData);
}
);
}
_resolveResourceErrorHints(
error,
contextInfo,
context,
unresolvedResource,
resolver,
resolveContext,
callback
) {
asyncLib.parallel(
[
callback => {
if (!resolver.options.fullySpecified) return callback();
resolver
.withOptions({
fullySpecified: false
})
.resolve(
contextInfo,
context,
unresolvedResource,
resolveContext,
(err, resolvedResource) => {
if (!err && resolvedResource) {
const resource = parseResource(resolvedResource).path.replace(
/^.*[\\/]/,
""
);
return callback(
null,
`Did you mean '${resource}'?
BREAKING CHANGE: The request '${unresolvedResource}' failed to resolve only because it was resolved as fully specified
(probably because the origin is a '*.mjs' file or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.`;
}
callback(err);
Add the extension to the request.`
);
}
);
return;
callback();
}
);
},
callback => {
if (!resolver.options.enforceExtension) return callback();
resolver
.withOptions({
enforceExtension: false,
extensions: []
})
.resolve(
contextInfo,
context,
unresolvedResource,
resolveContext,
(err, resolvedResource) => {
if (!err && resolvedResource) {
let hint = "";
const match = /(\.[^.]+)(\?|$)/.exec(unresolvedResource);
if (match) {
const fixedRequest = unresolvedResource.replace(
/(\.[^.]+)(\?|$)/,
"$2"
);
if (resolver.options.extensions.has(match[1])) {
hint = `Did you mean '${fixedRequest}'?`;
} else {
hint = `Did you mean '${fixedRequest}'? Also note that '${match[1]}' is not in 'resolve.extensions' yet and need to be added for this to work?`;
}
} else {
hint = `Did you mean to omit the extension or to remove 'resolve.enforceExtension'?`;
}
return callback(
null,
`The request '${unresolvedResource}' failed to resolve only because 'resolve.enforceExtension' was specified.
${hint}
Including the extension in the request is no longer possible. Did you mean to enforce including the extension in requests with 'resolve.extensions: []' instead?`
);
}
callback();
}
);
},
callback => {
if (
/^\.\.?\//.test(unresolvedResource) ||
resolver.options.preferRelative
) {
return callback();
}
resolver.resolve(
contextInfo,
context,
`./${unresolvedResource}`,
resolveContext,
(err, resolvedResource) => {
if (err || !resolvedResource) return callback();
const moduleDirectories = resolver.options.modules
.map(m => (Array.isArray(m) ? m.join(", ") : m))
.join(", ");
callback(
null,
`Did you mean './${unresolvedResource}'?
Requests that should resolve in the current directory need to start with './'.
Requests that start with a name are treated as module requests and resolve within module directories (${moduleDirectories}).
If changing the source code is not an option there is also a resolve options called 'preferRelative' which tries to resolve these kind of requests in the current directory too.`
);
}
);
}
callback(err, resolvedResource, resolvedResourceResolveData);
],
(err, hints) => {
if (err) return callback(err);
callback(null, hints.filter(Boolean));
}
);
}

View File

@ -0,0 +1 @@
export {};

View File

@ -0,0 +1,3 @@
module.exports = [
[/Can't resolve 'dependency\.js'/, /Did you mean '\.\/dependency\.js'\?/]
];

View File

@ -0,0 +1,5 @@
it("should not resolve module requests relative", async () => {
await expect(import("./module.mjs")).rejects.toMatchObject({
code: "MODULE_NOT_FOUND"
});
});

View File

@ -0,0 +1 @@
import "dependency.js";