Skip to content

Commit c0b5515

Browse files
authored
Merge pull request webpack#3477 from webpack/feature/validation
Improve validation errors
2 parents 324d309 + 9598f64 commit c0b5515

4 files changed

Lines changed: 81 additions & 30 deletions

File tree

lib/WebpackOptionsValidationError.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,18 @@ WebpackOptionsValidationError.formatValidationError = function formatValidationE
5656
return baseMessage;
5757
case "oneOf":
5858
case "anyOf":
59+
if(err.children && err.children.length > 0) {
60+
return dataPath + " should be one of these:\n" +
61+
getSchemaPartText(err.parentSchema) + "\nDetails:\n" + err.children.map(function(err) {
62+
return " * " + indent(WebpackOptionsValidationError.formatValidationError(err), " ", false);
63+
}).join("\n")
64+
}
65+
return dataPath + " should be one of these:\n" +
66+
getSchemaPartText(err.parentSchema);
5967
case "enum":
68+
if(err.parentSchema && err.parentSchema.enum && err.parentSchema.enum.length === 1) {
69+
return dataPath + " should be " + getSchemaPartText(err.parentSchema);
70+
}
6071
return dataPath + " should be one of these:\n" +
6172
getSchemaPartText(err.parentSchema);
6273
case "allOf":
@@ -72,6 +83,9 @@ WebpackOptionsValidationError.formatValidationError = function formatValidationE
7283
return dataPath + " should be a boolean.";
7384
case "number":
7485
return dataPath + " should be a number.";
86+
case "array":
87+
return dataPath + " should be an array:\n" +
88+
getSchemaPartText(err.parentSchema);
7589
}
7690
return dataPath + " should be " + err.params.type + ":\n" +
7791
getSchemaPartText(err.parentSchema);
@@ -143,6 +157,10 @@ function formatSchema(schema, prevSchemas) {
143157
}
144158
switch(schema.type) {
145159
case "string":
160+
if(schema.minLength === 1)
161+
return "non-empty string";
162+
else if(schema.minLength > 1)
163+
return "string (min length " + schema.minLength + ")";
146164
return "string";
147165
case "boolean":
148166
return "boolean";

lib/validateSchema.js

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ var ajv = new Ajv({
88
allErrors: true,
99
verbose: true
1010
});
11-
require('ajv-keywords')(ajv);
11+
require("ajv-keywords")(ajv);
1212

1313
function validateSchema(schema, options) {
1414
if(Array.isArray(options)) {
@@ -36,27 +36,29 @@ function validateObject(schema, options) {
3636
}
3737

3838
function filterErrors(errors) {
39-
var errorsByDataPath = {};
4039
var newErrors = [];
4140
errors.forEach(function(err) {
4241
var dataPath = err.dataPath;
43-
var key = "$" + dataPath;
44-
if(errorsByDataPath[key]) {
45-
var oldError = errorsByDataPath[key];
46-
var idx = newErrors.indexOf(oldError);
47-
newErrors.splice(idx, 1);
48-
if(oldError.children) {
49-
var children = oldError.children;
50-
delete oldError.children;
42+
var children = [];
43+
newErrors = newErrors.filter(function(oldError) {
44+
if(oldError.dataPath.indexOf(dataPath) >= 0) {
45+
if(oldError.children) {
46+
oldError.children.forEach(function(child) {
47+
children.push(child);
48+
});
49+
}
50+
oldError.children = undefined;
5151
children.push(oldError);
52-
err.children = children;
53-
} else {
54-
err.children = [oldError];
52+
return false;
5553
}
54+
return true;
55+
});
56+
if(children.length) {
57+
err.children = children;
5658
}
57-
errorsByDataPath[key] = err;
5859
newErrors.push(err);
5960
});
61+
//console.log(JSON.stringify(newErrors, 0, 2));
6062
return newErrors;
6163
}
6264

schemas/webpackOptionsSchema.json

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,25 @@
4040
"anyOf": [
4141
{
4242
"additionalProperties": {
43-
"$ref": "#/definitions/entry-item"
43+
"anyOf": [
44+
{
45+
"description": "The string is resolved to a module which is loaded upon startup.",
46+
"minLength": 1,
47+
"type": "string"
48+
},
49+
{
50+
"allOf": [
51+
{
52+
"$ref": "#/definitions/common.nonEmptyArrayOfUniqueStringValues"
53+
}
54+
],
55+
"description": "All modules are loaded upon startup. The last one is exported."
56+
}
57+
]
4458
},
4559
"description": "Multiple entry bundles are created. The key is the chunk name. The value can be a string or an array.",
4660
"type": "object"
4761
},
48-
{
49-
"$ref": "#/definitions/entry-item"
50-
}
51-
]
52-
},
53-
"entry-item": {
54-
"description": "The entry point for one output file.",
55-
"anyOf": [
5662
{
5763
"description": "The string is resolved to a module which is loaded upon startup.",
5864
"minLength": 1,

test/Validation.test.js

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe("Validation", function() {
2020
config: {},
2121
message: [
2222
" - configuration misses the property 'entry'.",
23-
" object { <key>: string | [string] } | string | [string]",
23+
" object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string]",
2424
" The entry point(s) of the compilation."
2525
]
2626
}, {
@@ -30,8 +30,13 @@ describe("Validation", function() {
3030
},
3131
message: [
3232
" - configuration.entry should be one of these:",
33-
" object { <key>: string | [string] } | string | [string]",
34-
" The entry point(s) of the compilation."
33+
" object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string]",
34+
" The entry point(s) of the compilation.",
35+
" Details:",
36+
" * configuration.entry should be an object.",
37+
" * configuration.entry should not be empty.",
38+
" * configuration.entry should be an array:",
39+
" [non-empty string]"
3540
]
3641
}, {
3742
name: "invalid instanceof",
@@ -53,10 +58,13 @@ describe("Validation", function() {
5358
}
5459
},
5560
message: [
56-
" - configuration.entry[0] should be a string.",
5761
" - configuration.entry should be one of these:",
58-
" object { <key>: string | [string] } | string | [string]",
62+
" object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string]",
5963
" The entry point(s) of the compilation.",
64+
" Details:",
65+
" * configuration.entry should be an object.",
66+
" * configuration.entry should be a string.",
67+
" * configuration.entry[0] should be a string.",
6068
" - configuration.output.filename should be a string."
6169
]
6270
}, {
@@ -70,10 +78,13 @@ describe("Validation", function() {
7078
}
7179
}],
7280
message: [
73-
" - configuration[0].entry[0] should be a string.",
7481
" - configuration[0].entry should be one of these:",
75-
" object { <key>: string | [string] } | string | [string]",
82+
" object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string]",
7683
" The entry point(s) of the compilation.",
84+
" Details:",
85+
" * configuration[0].entry should be an object.",
86+
" * configuration[0].entry should be a string.",
87+
" * configuration[0].entry[0] should be a string.",
7788
" - configuration[1].output.filename should be a string."
7889
]
7990
}, {
@@ -119,6 +130,20 @@ describe("Validation", function() {
119130
" })",
120131
" ]"
121132
]
133+
}, {
134+
name: "enum",
135+
config: {
136+
entry: "a",
137+
devtool: true
138+
},
139+
message: [
140+
" - configuration.devtool should be one of these:",
141+
" string | false",
142+
" A developer tool to enhance debugging.",
143+
" Details:",
144+
" * configuration.devtool should be a string.",
145+
" * configuration.devtool should be false"
146+
]
122147
}];
123148
testCases.forEach(function(testCase) {
124149
it("should fail validation for " + testCase.name, function() {

0 commit comments

Comments
 (0)