Improve validation errors

This commit is contained in:
Tobias Koppers 2016-12-14 11:34:31 +01:00
parent 2e2eac70ab
commit 3c1086303f
4 changed files with 67 additions and 30 deletions

View File

@ -56,7 +56,18 @@ WebpackOptionsValidationError.formatValidationError = function formatValidationE
return baseMessage;
case "oneOf":
case "anyOf":
if(err.children && err.children.length > 0) {
return dataPath + " should be one of these:\n" +
getSchemaPartText(err.parentSchema) + "\nDetails:\n" + err.children.map(function(err) {
return " * " + indent(WebpackOptionsValidationError.formatValidationError(err), " ", false);
}).join("\n")
}
return dataPath + " should be one of these:\n" +
getSchemaPartText(err.parentSchema);
case "enum":
if(err.parentSchema && err.parentSchema.enum && err.parentSchema.enum.length === 1) {
return dataPath + " should be " + getSchemaPartText(err.parentSchema);
}
return dataPath + " should be one of these:\n" +
getSchemaPartText(err.parentSchema);
case "allOf":
@ -72,6 +83,9 @@ WebpackOptionsValidationError.formatValidationError = function formatValidationE
return dataPath + " should be a boolean.";
case "number":
return dataPath + " should be a number.";
case "array":
return dataPath + " should be an array:\n" +
getSchemaPartText(err.parentSchema);
}
return dataPath + " should be " + err.params.type + ":\n" +
getSchemaPartText(err.parentSchema);
@ -143,6 +157,10 @@ function formatSchema(schema, prevSchemas) {
}
switch(schema.type) {
case "string":
if(schema.minLength === 1)
return "non-empty string";
else if(schema.minLength > 1)
return "string (min length " + schema.minLength + ")";
return "string";
case "boolean":
return "boolean";

View File

@ -8,7 +8,7 @@ var ajv = new Ajv({
allErrors: true,
verbose: true
});
require('ajv-keywords')(ajv);
require("ajv-keywords")(ajv);
function validateSchema(schema, options) {
if(Array.isArray(options)) {
@ -36,27 +36,29 @@ function validateObject(schema, options) {
}
function filterErrors(errors) {
var errorsByDataPath = {};
var newErrors = [];
errors.forEach(function(err) {
var dataPath = err.dataPath;
var key = "$" + dataPath;
if(errorsByDataPath[key]) {
var oldError = errorsByDataPath[key];
var idx = newErrors.indexOf(oldError);
newErrors.splice(idx, 1);
if(oldError.children) {
var children = oldError.children;
delete oldError.children;
var children = [];
newErrors = newErrors.filter(function(oldError) {
if(oldError.dataPath.indexOf(dataPath) >= 0) {
if(oldError.children) {
oldError.children.forEach(function(child) {
children.push(child);
});
}
oldError.children = undefined;
children.push(oldError);
err.children = children;
} else {
err.children = [oldError];
return false;
}
return true;
});
if(children.length) {
err.children = children;
}
errorsByDataPath[key] = err;
newErrors.push(err);
});
//console.log(JSON.stringify(newErrors, 0, 2));
return newErrors;
}

View File

@ -40,19 +40,25 @@
"anyOf": [
{
"additionalProperties": {
"$ref": "#/definitions/entry-item"
"anyOf": [
{
"description": "The string is resolved to a module which is loaded upon startup.",
"minLength": 1,
"type": "string"
},
{
"allOf": [
{
"$ref": "#/definitions/common.nonEmptyArrayOfUniqueStringValues"
}
],
"description": "All modules are loaded upon startup. The last one is exported."
}
]
},
"description": "Multiple entry bundles are created. The key is the chunk name. The value can be a string or an array.",
"type": "object"
},
{
"$ref": "#/definitions/entry-item"
}
]
},
"entry-item": {
"description": "The entry point for one output file.",
"anyOf": [
{
"description": "The string is resolved to a module which is loaded upon startup.",
"minLength": 1,

View File

@ -20,7 +20,7 @@ describe("Validation", function() {
config: {},
message: [
" - configuration misses the property 'entry'.",
" object { <key>: string | [string] } | string | [string]",
" object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string]",
" The entry point(s) of the compilation."
]
}, {
@ -30,8 +30,13 @@ describe("Validation", function() {
},
message: [
" - configuration.entry should be one of these:",
" object { <key>: string | [string] } | string | [string]",
" The entry point(s) of the compilation."
" 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 object.",
" * configuration.entry should not be empty.",
" * configuration.entry should be an array:",
" [non-empty string]"
]
}, {
name: "invalid instanceof",
@ -53,10 +58,13 @@ describe("Validation", function() {
}
},
message: [
" - configuration.entry[0] should be a string.",
" - configuration.entry should be one of these:",
" object { <key>: string | [string] } | string | [string]",
" 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 object.",
" * configuration.entry should be a string.",
" * configuration.entry[0] should be a string.",
" - configuration.output.filename should be a string."
]
}, {
@ -70,10 +78,13 @@ describe("Validation", function() {
}
}],
message: [
" - configuration[0].entry[0] should be a string.",
" - configuration[0].entry should be one of these:",
" object { <key>: string | [string] } | string | [string]",
" 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 object.",
" * configuration[0].entry should be a string.",
" * configuration[0].entry[0] should be a string.",
" - configuration[1].output.filename should be a string."
]
}, {