improve validation errors

This commit is contained in:
Tobias Koppers 2019-05-21 11:27:45 +02:00
parent 0963d744e9
commit a4406ff187
4 changed files with 68 additions and 93 deletions

View File

@ -22,6 +22,8 @@ export type EntryDynamic = () => EntryStatic | Promise<EntryStatic>;
*/
export type EntryStatic = EntryObject | EntryItem;
/**
* A non-empty array of non-empty strings
*
* This interface was referenced by `WebpackOptions`'s JSON-Schema
* via the `definition` "NonEmptyArrayOfUniqueStringValues".
*/

View File

@ -50,13 +50,34 @@ const getSchemaPartDescription = schemaPart => {
return "";
};
const SPECIFICITY = {
type: 1,
oneOf: 1,
anyOf: 1,
allOf: 1,
additionalProperties: 2,
enum: 1,
instanceof: 1,
required: 2,
minimum: 2,
uniqueItems: 2,
minLength: 2,
minItems: 2,
minProperties: 2,
absolutePath: 2
};
const filterMax = (array, fn) => {
const max = array.reduce((max, item) => Math.max(max, fn(item)), 0);
return array.filter(item => fn(item) === max);
};
const filterChildren = children => {
return children.filter(
err =>
err.keyword !== "anyOf" &&
err.keyword !== "allOf" &&
err.keyword !== "oneOf"
children = filterMax(children, err =>
err.dataPath ? err.dataPath.length : 0
);
children = filterMax(children, err => SPECIFICITY[err.keyword] || 2);
return children;
};
const indent = (str, prefix, firstLine) => {
@ -230,11 +251,17 @@ class WebpackOptionsValidationError extends WebpackError {
})
);
}
const children = filterChildren(err.children);
if (children.length === 1) {
return WebpackOptionsValidationError.formatValidationError(
children[0]
);
}
return (
`${dataPath} should be one of these:\n${getSchemaPartText(
err.parentSchema
)}\n` +
`Details:\n${filterChildren(err.children)
`Details:\n${children
.map(
err =>
" * " +
@ -251,7 +278,6 @@ class WebpackOptionsValidationError extends WebpackError {
err.parentSchema
)}`;
} else if (err.keyword === "enum") {
console.log(err.parentSchema);
if (
err.parentSchema &&
err.parentSchema.enum &&
@ -314,7 +340,21 @@ class WebpackOptionsValidationError extends WebpackError {
err.keyword === "minProperties"
) {
if (err.params.limit === 1) {
return `${dataPath} should not be empty.${getSchemaPartDescription(
switch (err.keyword) {
case "minLength":
return `${dataPath} should be an non-empty string.${getSchemaPartDescription(
err.parentSchema
)}`;
case "minItems":
return `${dataPath} should be an non-empty array.${getSchemaPartDescription(
err.parentSchema
)}`;
case "minProperties":
return `${dataPath} should be an non-empty object.${getSchemaPartDescription(
err.parentSchema
)}`;
}
return `${dataPath} should be not empty.${getSchemaPartDescription(
err.parentSchema
)}`;
} else {

View File

@ -385,6 +385,7 @@
}
},
"NonEmptyArrayOfUniqueStringValues": {
"description": "A non-empty array of non-empty strings",
"type": "array",
"items": {
"description": "A non-empty string",

View File

@ -43,18 +43,8 @@ describe("Validation", () => {
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.entry should be one of these:
function | object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string]
-> The entry point(s) of the compilation.
Details:
* configuration.entry should be an instance of function
-> A Function returning an entry object, an entry string, an entry array or a promise to these things.
* configuration.entry should be an object.
-> Multiple entry bundles are created. The key is the chunk name. The value can be a string or an array.
* configuration.entry should not be empty.
-> An entry point without name. The string is resolved to a module which is loaded upon startup.
* configuration.entry should be an array:
[non-empty string]"
- configuration.entry should be an non-empty string.
-> An entry point without name. The string is resolved to a module which is loaded upon startup."
`)
);
@ -68,19 +58,8 @@ describe("Validation", () => {
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.entry should be one of these:
function | object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string]
-> The entry point(s) of the compilation.
Details:
* configuration.entry should be an instance of function
-> A Function returning an entry object, an entry string, an entry array or a promise to these things.
* configuration.entry['bundle'] should be a string.
-> The string is resolved to a module which is loaded upon startup.
* configuration.entry['bundle'] should not be empty.
* configuration.entry should be a string.
-> An entry point without name. The string is resolved to a module which is loaded upon startup.
* configuration.entry should be an array:
[non-empty string]"
- configuration.entry['bundle'] should be an non-empty array.
-> A non-empty array of non-empty strings"
`)
);
@ -122,17 +101,8 @@ describe("Validation", () => {
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.entry should be one of these:
function | object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string]
-> The entry point(s) of the compilation.
Details:
* configuration.entry should be an instance of function
-> A Function returning an entry object, an entry string, an entry array or a promise to these things.
* configuration.entry should be an object.
-> Multiple entry bundles are created. The key is the chunk name. The value can be a string or an array.
* configuration.entry should be a string.
-> An entry point without name. The string is resolved to a module which is loaded upon startup.
* configuration.entry should not contain the item 'abc' twice."
- configuration.entry should not contain the item 'abc' twice.
-> A non-empty array of non-empty strings"
`)
);
@ -147,18 +117,8 @@ describe("Validation", () => {
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.entry should be one of these:
function | object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string]
-> The entry point(s) of the compilation.
Details:
* configuration.entry should be an instance of function
-> A Function returning an entry object, an entry string, an entry array or a promise to these things.
* configuration.entry should be an object.
-> Multiple entry bundles are created. The key is the chunk name. The value can be a string or an array.
* configuration.entry should be a string.
-> An entry point without name. The string is resolved to a module which is loaded upon startup.
* configuration.entry[0] should be a string.
-> A non-empty string
- configuration.entry[0] should be a string.
-> A non-empty string
- configuration.output.filename should be one of these:
string | function
-> Specifies the name of each output file on disk. You must **not** specify an absolute path here! The \`output.path\` option determines the location on disk the files are written to, filename is used solely for naming the individual files.
@ -184,18 +144,8 @@ describe("Validation", () => {
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration[0].entry should be one of these:
function | object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string]
-> The entry point(s) of the compilation.
Details:
* configuration[0].entry should be an instance of function
-> A Function returning an entry object, an entry string, an entry array or a promise to these things.
* configuration[0].entry should be an object.
-> Multiple entry bundles are created. The key is the chunk name. The value can be a string or an array.
* configuration[0].entry should be a string.
-> An entry point without name. The string is resolved to a module which is loaded upon startup.
* configuration[0].entry[0] should be a string.
-> A non-empty string
- configuration[0].entry[0] should be a string.
-> A non-empty string
- configuration[1].output.filename should be one of these:
string | function
-> Specifies the name of each output file on disk. You must **not** specify an absolute path here! The \`output.path\` option determines the location on disk the files are written to, filename is used solely for naming the individual files.
@ -305,13 +255,8 @@ describe("Validation", () => {
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.output.filename should be one of these:
string | function
-> Specifies the name of each output file on disk. You must **not** specify an absolute path here! The \`output.path\` option determines the location on disk the files are written to, filename is used solely for naming the individual files.
Details:
* configuration.output.filename: A relative path is expected. However, the provided value \\"/bar\\" is an absolute path!
Please use output.path to specify absolute path and output.filename for the file name.
* configuration.output.filename should be an instance of function"
- configuration.output.filename: A relative path is expected. However, the provided value \\"/bar\\" is an absolute path!
Please use output.path to specify absolute path and output.filename for the file name."
`)
);
@ -347,15 +292,8 @@ describe("Validation", () => {
.replace(/"none" \| .+/g, '"none" | ...')
).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.stats should be one of these:
object {...} | boolean | \\"none\\" | ...
-> Used by the webpack CLI program to pass stats options.
Details:
* configuration.stats has an unknown property 'foobar'. These properties are valid:
object {...}
* configuration.stats should be a boolean.
* configuration.stats should be one of these:
\\"none\\" | ..."
- configuration.stats has an unknown property 'foobar'. These properties are valid:
object {...}"
`);
}
);
@ -449,15 +387,9 @@ describe("Validation", () => {
msg =>
expect(msg).toMatchInlineSnapshot(`
"Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.plugins[0] should be one of these:
object { apply, } | function
-> Plugin of type object or instanceof Function
Details:
* configuration.plugins[0] misses the property 'apply'.
function
-> The run point of the plugin, required method.
* configuration.plugins[0] should be an instance of function
-> Function acting as plugin"
- configuration.plugins[0] misses the property 'apply'.
function
-> The run point of the plugin, required method."
`)
);