Cleanup error location and origin information

This commit is contained in:
Tobias Koppers 2018-06-04 10:10:23 +02:00
parent 53f7debdc9
commit 53103a9690
28 changed files with 192 additions and 170 deletions

View File

@ -16,13 +16,13 @@ class AsyncDependencyToInitialChunkError extends WebpackError {
* @param {TODO} loc location of dependency
*/
constructor(chunkName, module, loc) {
super();
super(
`It's not allowed to load an initial chunk on demand. The chunk name "${chunkName}" is already used by an entrypoint.`
);
this.name = "AsyncDependencyToInitialChunkError";
this.message = `It's not allowed to load an initial chunk on demand. The chunk name "${chunkName}" is already used by an entrypoint.`;
this.module = module;
this.origin = module;
this.originLoc = loc;
this.loc = loc;
Error.captureStackTrace(this, this.constructor);
}

View File

@ -8,64 +8,60 @@ const WebpackError = require("./WebpackError");
/** @typedef {import("./Module")} Module */
/**
* @param {Module[]} modules the modules to be sorted
* @returns {Module[]} sorted version of original modules
*/
const sortModules = modules => {
return modules.slice().sort((a, b) => {
a = a.identifier();
b = b.identifier();
/* istanbul ignore next */
if (a < b) return -1;
/* istanbul ignore next */
if (a > b) return 1;
/* istanbul ignore next */
return 0;
});
};
/**
* @param {Module[]} modules each module from throw
* @returns {string} each message from provided moduels
*/
const createModulesListMessage = modules => {
return modules
.map(m => {
let message = `* ${m.identifier()}`;
const validReasons = m.reasons.filter(reason => reason.module);
if (validReasons.length > 0) {
message += `\n Used by ${validReasons.length} module(s), i. e.`;
message += `\n ${validReasons[0].module.identifier()}`;
}
return message;
})
.join("\n");
};
class CaseSensitiveModulesWarning extends WebpackError {
/**
* Creates an instance of CaseSensitiveModulesWarning.
* @param {Module[]} modules modules that were detected
*/
constructor(modules) {
super();
this.name = "CaseSensitiveModulesWarning";
const sortedModules = this._sort(modules);
const modulesList = this._moduleMessages(sortedModules);
this.message = `There are multiple modules with names that only differ in casing.
const sortedModules = sortModules(modules);
const modulesList = createModulesListMessage(sortedModules);
super(`There are multiple modules with names that only differ in casing.
This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.
Use equal casing. Compare these module identifiers:
${modulesList}`;
${modulesList}`);
this.name = "CaseSensitiveModulesWarning";
this.origin = this.module = sortedModules[0];
Error.captureStackTrace(this, this.constructor);
}
/**
* @private
* @param {Module[]} modules the modules to be sorted
* @returns {Module[]} sorted version of original modules
*/
_sort(modules) {
return modules.slice().sort((a, b) => {
a = a.identifier();
b = b.identifier();
/* istanbul ignore next */
if (a < b) return -1;
/* istanbul ignore next */
if (a > b) return 1;
/* istanbul ignore next */
return 0;
});
}
/**
* @private
* @param {Module[]} modules each module from throw
* @returns {string} each message from provided moduels
*/
_moduleMessages(modules) {
return modules
.map(m => {
let message = `* ${m.identifier()}`;
const validReasons = m.reasons.filter(reason => reason.module);
if (validReasons.length > 0) {
message += `\n Used by ${validReasons.length} module(s), i. e.`;
message += `\n ${validReasons[0].module.identifier()}`;
}
return message;
})
.join("\n");
}
}
module.exports = CaseSensitiveModulesWarning;

View File

@ -487,6 +487,7 @@ class Compilation extends Tapable {
const errorAndCallback = err => {
err.origin = module;
err.dependencies = dependencies;
this.errors.push(err);
if (bail) {
callback(err);
@ -531,7 +532,7 @@ class Compilation extends Tapable {
if (err) {
semaphore.release();
return errorOrWarningAndCallback(
new ModuleNotFoundError(module, err, dependencies)
new ModuleNotFoundError(module, err)
);
}
if (!dependentModule) {

View File

@ -8,10 +8,9 @@ const WebpackError = require("./WebpackError");
class EntryModuleNotFoundError extends WebpackError {
constructor(err) {
super();
super("Entry module not found: " + err);
this.name = "EntryModuleNotFoundError";
this.message = "Entry module not found: " + err;
this.details = err.details;
this.error = err;

View File

@ -8,9 +8,8 @@ const WebpackError = require("./WebpackError");
module.exports = class HarmonyLinkingError extends WebpackError {
/** @param {string} message Error message */
constructor(message) {
super();
super(message);
this.name = "HarmonyLinkingError";
this.message = message;
this.hideStack = true;
Error.captureStackTrace(this, this.constructor);

View File

@ -9,29 +9,32 @@ const { cutOffLoaderExecution } = require("./ErrorHelpers");
class ModuleBuildError extends WebpackError {
constructor(module, err) {
super();
this.name = "ModuleBuildError";
this.message = "Module build failed: ";
let message = "Module build failed: ";
let details = undefined;
if (err !== null && typeof err === "object") {
if (typeof err.stack === "string" && err.stack) {
var stack = cutOffLoaderExecution(err.stack);
if (!err.hideStack) {
this.message += stack;
message += stack;
} else {
this.details = stack;
details = stack;
if (typeof err.message === "string" && err.message) {
this.message += err.message;
message += err.message;
} else {
this.message += err;
message += err;
}
}
} else if (typeof err.message === "string" && err.message) {
this.message += err.message;
message += err.message;
} else {
this.message += err;
message += err;
}
}
super(message);
this.name = "ModuleBuildError";
this.details = details;
this.module = module;
this.error = err;

View File

@ -5,7 +5,6 @@
"use strict";
const WebpackError = require("./WebpackError");
const formatLocation = require("./formatLocation");
/** @typedef {import("./Module")} Module */
@ -17,15 +16,15 @@ class ModuleDependencyError extends WebpackError {
* @param {TODO} loc location of dependency
*/
constructor(module, err, loc) {
super();
super(err.message);
this.name = "ModuleDependencyError";
this.message = `${formatLocation(loc)} ${err.message}`;
this.details = err.stack
.split("\n")
.slice(1)
.join("\n");
this.origin = this.module = module;
this.module = module;
this.loc = loc;
this.error = err;
Error.captureStackTrace(this, this.constructor);

View File

@ -5,19 +5,18 @@
"use strict";
const WebpackError = require("./WebpackError");
const formatLocation = require("./formatLocation");
module.exports = class ModuleDependencyWarning extends WebpackError {
constructor(module, err, loc) {
super();
super(err.message);
this.name = "ModuleDependencyWarning";
this.message = `${formatLocation(loc)} ${err.message}`;
this.details = err.stack
.split("\n")
.slice(1)
.join("\n");
this.origin = this.module = module;
this.module = module;
this.loc = loc;
this.error = err;
Error.captureStackTrace(this, this.constructor);

View File

@ -9,12 +9,10 @@ const { cleanUp } = require("./ErrorHelpers");
class ModuleError extends WebpackError {
constructor(module, err) {
super();
super(err && typeof err === "object" && err.message ? err.message : err);
this.name = "ModuleError";
this.module = module;
this.message =
err && typeof err === "object" && err.message ? err.message : err;
this.error = err;
this.details =
err && typeof err === "object" && err.stack

View File

@ -7,16 +7,13 @@
const WebpackError = require("./WebpackError");
class ModuleNotFoundError extends WebpackError {
constructor(module, err, dependencies) {
super();
constructor(module, err) {
super("Module not found: " + err);
this.name = "ModuleNotFoundError";
this.message = "Module not found: " + err;
this.details = err.details;
this.missing = err.missing;
this.module = module;
this.origin = module;
this.dependencies = dependencies;
this.error = err;
Error.captureStackTrace(this, this.constructor);

View File

@ -6,14 +6,18 @@
const WebpackError = require("./WebpackError");
class ModuleParseError extends WebpackError {
constructor(module, source, err) {
super();
/** @typedef {import("./Module")} Module */
this.name = "ModuleParseError";
this.message = "Module parse failed: " + err.message;
this.message +=
"\nYou may need an appropriate loader to handle this file type.";
class ModuleParseError extends WebpackError {
/**
* @param {Module} module the errored module
* @param {string} source source code
* @param {Error&any} err the parse error
*/
constructor(module, source, err) {
let message = "Module parse failed: " + err.message;
let loc = undefined;
message += "\nYou may need an appropriate loader to handle this file type.";
if (
err.loc &&
typeof err.loc === "object" &&
@ -22,19 +26,28 @@ class ModuleParseError extends WebpackError {
var lineNumber = err.loc.line;
if (/[\0\u0001\u0002\u0003\u0004\u0005\u0006\u0007]/.test(source)) {
// binary file
this.message += "\n(Source code omitted for this binary file)";
message += "\n(Source code omitted for this binary file)";
} else {
source = source.split("\n");
this.message +=
"\n| " +
source
.slice(Math.max(0, lineNumber - 3), lineNumber + 2)
.join("\n| ");
const sourceLines = source.split("\n");
const start = Math.max(0, lineNumber - 3);
const linesBefore = sourceLines.slice(start, lineNumber - 1);
const theLine = sourceLines[lineNumber - 1];
const linesAfter = sourceLines.slice(lineNumber, lineNumber + 2);
message +=
linesBefore.map(l => `\n| ${l}`).join("") +
`\n> ${theLine}` +
linesAfter.map(l => `\n| ${l}`).join("");
}
loc = err.loc;
} else {
this.message += "\n" + err.stack;
message += "\n" + err.stack;
}
super(message);
this.name = "ModuleParseError";
this.module = module;
this.loc = loc;
this.error = err;
Error.captureStackTrace(this, this.constructor);

View File

@ -9,14 +9,14 @@ const { cleanUp } = require("./ErrorHelpers");
class ModuleWarning extends WebpackError {
constructor(module, warning) {
super();
super(
warning && typeof warning === "object" && warning.message
? warning.message
: warning
);
this.name = "ModuleWarning";
this.module = module;
this.message =
warning && typeof warning === "object" && warning.message
? warning.message
: warning;
this.warning = warning;
this.details =
warning && typeof warning === "object" && warning.stack

View File

@ -88,7 +88,7 @@ ParserHelpers.expressionIsUnsupported = (parser, message) => {
parser.state.current.addDependency(dep);
if (!parser.state.module) return;
parser.state.module.warnings.push(
new UnsupportedFeatureWarning(parser.state.module, message)
new UnsupportedFeatureWarning(parser.state.module, message, expr.loc)
);
return true;
};

View File

@ -4,9 +4,7 @@ const WebpackError = require("./WebpackError");
module.exports = class RemovedPluginError extends WebpackError {
constructor(message) {
super();
this.message = message;
super(message);
Error.captureStackTrace(this, this.constructor);
}

View File

@ -56,8 +56,8 @@ class Stats {
formatFilePath(filePath) {
const OPTIONS_REGEXP = /^(\s|\S)*!/;
return filePath.includes("!")
? `${filePath.replace(OPTIONS_REGEXP, "")} (${filePath})\n`
: `${filePath}\n`;
? `${filePath.replace(OPTIONS_REGEXP, "")} (${filePath})`
: `${filePath}`;
}
hasWarnings() {
@ -288,6 +288,11 @@ class Stats {
text += this.formatFilePath(
e.module.readableIdentifier(requestShortener)
);
if (typeof e.loc === "object") {
const locInfo = formatLocation(e.loc);
if (locInfo) text += ` ${locInfo}`;
}
text += "\n";
}
text += e.message;
if (showErrorDetails && e.details) {
@ -297,7 +302,9 @@ class Stats {
text += e.missing.map(item => `\n[${item}]`).join("");
}
if (showModuleTrace && e.origin) {
text += `\n @ ${e.origin.readableIdentifier(requestShortener)}`;
text += `\n @ ${this.formatFilePath(
e.origin.readableIdentifier(requestShortener)
)}`;
if (typeof e.originLoc === "object") {
const locInfo = formatLocation(e.originLoc);
if (locInfo) text += ` ${locInfo}`;

View File

@ -7,12 +7,13 @@
const WebpackError = require("./WebpackError");
class UnsupportedFeatureWarning extends WebpackError {
constructor(module, message) {
super();
constructor(module, message, loc) {
super(message);
this.name = "UnsupportedFeatureWarning";
this.message = message;
this.origin = this.module = module;
this.module = module;
this.loc = loc;
this.hideStack = true;
Error.captureStackTrace(this, this.constructor);
}

View File

@ -69,23 +69,23 @@ const indent = (str, prefix, firstLine) => {
class WebpackOptionsValidationError extends WebpackError {
constructor(validationErrors) {
super();
super(
"Invalid configuration object. " +
"Webpack has been initialised using a configuration object that does not match the API schema.\n" +
validationErrors
.map(
err =>
" - " +
indent(
WebpackOptionsValidationError.formatValidationError(err),
" ",
false
)
)
.join("\n")
);
this.name = "WebpackOptionsValidationError";
this.message =
"Invalid configuration object. " +
"Webpack has been initialised using a configuration object that does not match the API schema.\n" +
validationErrors
.map(
err =>
" - " +
indent(
WebpackOptionsValidationError.formatValidationError(err),
" ",
false
)
)
.join("\n");
this.validationErrors = validationErrors;
Error.captureStackTrace(this, this.constructor);

View File

@ -219,7 +219,8 @@ class AMDRequireDependenciesBlockParserPlugin {
new UnsupportedFeatureWarning(
parser.state.module,
"Cannot statically analyse 'require(…, …)' in line " +
expr.loc.start.line
expr.loc.start.line,
expr.loc
)
);
}

View File

@ -41,7 +41,8 @@ class ImportParserPlugin {
parser.state.module,
`\`webpackIgnore\` expected a boolean, but received: ${
importOptions.webpackIgnore
}.`
}.`,
expr.loc
)
);
} else {
@ -58,7 +59,8 @@ class ImportParserPlugin {
parser.state.module,
`\`webpackChunkName\` expected a string, but received: ${
importOptions.webpackChunkName
}.`
}.`,
expr.loc
)
);
} else {
@ -72,7 +74,8 @@ class ImportParserPlugin {
parser.state.module,
`\`webpackMode\` expected a string, but received: ${
importOptions.webpackMode
}.`
}.`,
expr.loc
)
);
} else {
@ -90,7 +93,8 @@ class ImportParserPlugin {
parser.state.module,
`\`webpackPrefetch\` expected true or a number, but received: ${
importOptions.webpackPrefetch
}.`
}.`,
expr.loc
)
);
}
@ -106,7 +110,8 @@ class ImportParserPlugin {
parser.state.module,
`\`webpackPreload\` expected true or a number, but received: ${
importOptions.webpackPreload
}.`
}.`,
expr.loc
)
);
}
@ -121,7 +126,8 @@ class ImportParserPlugin {
parser.state.module,
`\`webpackInclude\` expected a regular expression, but received: ${
importOptions.webpackInclude
}.`
}.`,
expr.loc
)
);
} else {
@ -138,7 +144,8 @@ class ImportParserPlugin {
parser.state.module,
`\`webpackExclude\` expected a regular expression, but received: ${
importOptions.webpackExclude
}.`
}.`,
expr.loc
)
);
} else {
@ -152,7 +159,8 @@ class ImportParserPlugin {
parser.state.module.warnings.push(
new UnsupportedFeatureWarning(
parser.state.module,
`\`webpackMode\` expected 'lazy', 'eager' or 'weak', but received: ${mode}.`
`\`webpackMode\` expected 'lazy', 'eager' or 'weak', but received: ${mode}.`,
expr.loc
)
);
}
@ -195,7 +203,8 @@ class ImportParserPlugin {
parser.state.module.warnings.push(
new UnsupportedFeatureWarning(
parser.state.module,
`\`webpackMode\` expected 'lazy', 'lazy-once', 'eager' or 'weak', but received: ${mode}.`
`\`webpackMode\` expected 'lazy', 'lazy-once', 'eager' or 'weak', but received: ${mode}.`,
expr.loc
)
);
mode = "lazy";

View File

@ -108,15 +108,15 @@ class SystemPlugin {
class SystemImportDeprecationWarning extends WebpackError {
constructor(module, loc) {
super();
super(
"System.import() is deprecated and will be removed soon. Use import() instead.\n" +
"For more info visit https://webpack.js.org/guides/code-splitting/"
);
this.name = "SystemImportDeprecationWarning";
this.message =
"System.import() is deprecated and will be removed soon. Use import() instead.\n" +
"For more info visit https://webpack.js.org/guides/code-splitting/";
this.origin = this.module = module;
this.originLoc = loc;
this.module = module;
this.loc = loc;
Error.captureStackTrace(this, this.constructor);
}

View File

@ -9,21 +9,21 @@ const SizeFormatHelpers = require("../SizeFormatHelpers");
module.exports = class AssetsOverSizeLimitWarning extends WebpackError {
constructor(assetsOverSizeLimit, assetLimit) {
super();
this.name = "AssetsOverSizeLimitWarning";
this.assets = assetsOverSizeLimit;
const assetLists = this.assets
const assetLists = assetsOverSizeLimit
.map(
asset =>
`\n ${asset.name} (${SizeFormatHelpers.formatSize(asset.size)})`
)
.join("");
this.message = `asset size limit: The following asset(s) exceed the recommended size limit (${SizeFormatHelpers.formatSize(
super(`asset size limit: The following asset(s) exceed the recommended size limit (${SizeFormatHelpers.formatSize(
assetLimit
)}).
This can impact web performance.
Assets: ${assetLists}`;
Assets: ${assetLists}`);
this.name = "AssetsOverSizeLimitWarning";
this.assets = assetsOverSizeLimit;
Error.captureStackTrace(this, this.constructor);
}

View File

@ -9,11 +9,7 @@ const SizeFormatHelpers = require("../SizeFormatHelpers");
module.exports = class EntrypointsOverSizeLimitWarning extends WebpackError {
constructor(entrypoints, entrypointLimit) {
super();
this.name = "EntrypointsOverSizeLimitWarning";
this.entrypoints = entrypoints;
const entrypointList = this.entrypoints
const entrypointList = entrypoints
.map(
entrypoint =>
`\n ${entrypoint.name} (${SizeFormatHelpers.formatSize(
@ -21,10 +17,13 @@ module.exports = class EntrypointsOverSizeLimitWarning extends WebpackError {
)})\n${entrypoint.files.map(asset => ` ${asset}`).join("\n")}`
)
.join("");
this.message = `entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (${SizeFormatHelpers.formatSize(
super(`entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (${SizeFormatHelpers.formatSize(
entrypointLimit
)}). This can impact web performance.
Entrypoints:${entrypointList}\n`;
Entrypoints:${entrypointList}\n`);
this.name = "EntrypointsOverSizeLimitWarning";
this.entrypoints = entrypoints;
Error.captureStackTrace(this, this.constructor);
}

View File

@ -8,13 +8,13 @@ const WebpackError = require("../WebpackError");
module.exports = class NoAsyncChunksWarning extends WebpackError {
constructor() {
super();
super(
"webpack performance recommendations: \n" +
"You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.\n" +
"For more info visit https://webpack.js.org/guides/code-splitting/"
);
this.name = "NoAsyncChunksWarning";
this.message =
"webpack performance recommendations: \n" +
"You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.\n" +
"For more info visit https://webpack.js.org/guides/code-splitting/";
Error.captureStackTrace(this, this.constructor);
}

View File

@ -8,9 +8,8 @@ const WebpackError = require("../WebpackError");
module.exports = class UnsupportedWebAssemblyFeatureError extends WebpackError {
/** @param {string} message Error message */
constructor(message) {
super();
super(message);
this.name = "UnsupportedWebAssemblyFeatureError";
this.message = message;
this.hideStack = true;
Error.captureStackTrace(this, this.constructor);

View File

@ -29,7 +29,11 @@ describe("ModuleDependencyError", () => {
});
it("has a message property", () => {
expect(env.moduleDependencyError.message).toBe("Location Error Message");
expect(env.moduleDependencyError.message).toBe("Error Message");
});
it("has a loc property", () => {
expect(env.moduleDependencyError.loc).toBe("Location");
});
it("has a details property", () => {
@ -38,8 +42,8 @@ describe("ModuleDependencyError", () => {
);
});
it("has an origin property", () => {
expect(env.moduleDependencyError.origin).toBe("myModule");
it("has an module property", () => {
expect(env.moduleDependencyError.module).toBe("myModule");
});
it("has an error property", () => {

View File

@ -19,7 +19,7 @@ describe(
});
const inputPath =
"./node_modules/ts-loader!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/app.vue";
const expectPath = `./src/app.vue (${inputPath})\n`;
const expectPath = `./src/app.vue (${inputPath})`;
expect(mockStats.formatFilePath(inputPath)).toBe(expectPath);
});

View File

@ -1425,12 +1425,12 @@ Entrypoint main = main.js
| ./index.js 15 bytes [built]
| ./a.js 15 bytes [built]
ERROR in ./b.js
ERROR in ./b.js 6:7
Module parse failed: Unexpected token (6:7)
You may need an appropriate loader to handle this file type.
| includes
| a
| parser )
> parser )
| error
| in
@ ./a.js 2:0-13

View File

@ -1,5 +1,5 @@
module.exports = [
[/System.get is not supported by webpack/],
[/System.register is not supported by webpack/],
[/System.get is not supported by webpack/],
[/System.set is not supported by webpack/],
];