Skip to content

Commit a0ad795

Browse files
willmendesnetoTheLarkInn
authored andcommitted
refactor(WebpackOptionsValidationError): upgrade to ES6 (webpack#3715)
1 parent ce9c9e2 commit a0ad795

1 file changed

Lines changed: 140 additions & 149 deletions

File tree

lib/WebpackOptionsValidationError.js

Lines changed: 140 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,123 @@
22
MIT License http://www.opensource.org/licenses/mit-license.php
33
Author Gajus Kuizinas @gajus
44
*/
5-
var webpackOptionsSchema = require("../schemas/webpackOptionsSchema.json");
5+
"use strict";
66

7-
function WebpackOptionsValidationError(validationErrors) {
8-
Error.call(this);
9-
Error.captureStackTrace(this, WebpackOptionsValidationError);
10-
this.name = "WebpackOptionsValidationError";
11-
this.message = "Invalid configuration object. " +
12-
"Webpack has been initialised using a configuration object that does not match the API schema.\n" +
13-
validationErrors.map(function(err) {
14-
return " - " + indent(WebpackOptionsValidationError.formatValidationError(err), " ", false);
15-
}).join("\n");
16-
this.validationErrors = validationErrors;
7+
const webpackOptionsSchema = require("../schemas/webpackOptionsSchema.json");
8+
9+
const getSchemaPart = (path, parents, additionalPath) => {
10+
parents = parents || 0;
11+
path = path.split("/");
12+
path = path.slice(0, path.length - parents);
13+
if(additionalPath) {
14+
additionalPath = additionalPath.split("/");
15+
path = path.concat(additionalPath);
16+
}
17+
let schemaPart = webpackOptionsSchema;
18+
for(let i = 1; i < path.length; i++) {
19+
const inner = schemaPart[path[i]];
20+
if(inner)
21+
schemaPart = inner;
22+
}
23+
return schemaPart;
1724
}
18-
module.exports = WebpackOptionsValidationError;
1925

20-
WebpackOptionsValidationError.prototype = Object.create(Error.prototype);
21-
WebpackOptionsValidationError.prototype.constructor = WebpackOptionsValidationError;
26+
const getSchemaPartText = (schemaPart, additionalPath) => {
27+
if(additionalPath) {
28+
for(let i = 0; i < additionalPath.length; i++) {
29+
const inner = schemaPart[additionalPath[i]];
30+
if(inner)
31+
schemaPart = inner;
32+
}
33+
}
34+
while(schemaPart.$ref) schemaPart = getSchemaPart(schemaPart.$ref);
35+
let schemaText = WebpackOptionsValidationError.formatSchema(schemaPart);
36+
if(schemaPart.description)
37+
schemaText += `\n${schemaPart.description}`;
38+
return schemaText;
39+
}
40+
41+
const indent = (str, prefix, firstLine) => {
42+
if(firstLine) {
43+
return prefix + str.replace(/\n(?!$)/g, "\n" + prefix);
44+
} else {
45+
return str.replace(/\n(?!$)/g, `\n${prefix}`);
46+
}
47+
}
48+
49+
class WebpackOptionsValidationError extends Error {
50+
51+
constructor(validationErrors) {
52+
super();
53+
54+
if(Error.hasOwnProperty("captureStackTrace")) {
55+
Error.captureStackTrace(this, this.constructor);
56+
}
57+
this.name = "WebpackOptionsValidationError";
58+
59+
this.message = "Invalid configuration object. " +
60+
"Webpack has been initialised using a configuration object that does not match the API schema.\n" +
61+
validationErrors.map(err => " - " + indent(WebpackOptionsValidationError.formatValidationError(err), " ", false)).join("\n");
62+
this.validationErrors = validationErrors;
63+
}
64+
65+
static formatSchema(schema, prevSchemas) {
66+
prevSchemas = prevSchemas || [];
67+
68+
const formatInnerSchema = (innerSchema, addSelf) => {
69+
if(!addSelf) return WebpackOptionsValidationError.formatSchema(innerSchema, prevSchemas);
70+
if(prevSchemas.indexOf(innerSchema) >= 0) return "(recursive)";
71+
return WebpackOptionsValidationError.formatSchema(innerSchema, prevSchemas.concat(schema));
72+
}
73+
74+
if(schema.type === "string") {
75+
if(schema.minLength === 1)
76+
return "non-empty string";
77+
else if(schema.minLength > 1)
78+
return `string (min length ${schema.minLength})`;
79+
return "string";
80+
} else if(schema.type === "boolean") {
81+
return "boolean";
82+
} else if(schema.type === "number") {
83+
return "number";
84+
} else if(schema.type === "object") {
85+
if(schema.properties) {
86+
const required = schema.required || [];
87+
return `object { ${Object.keys(schema.properties).map(property => {
88+
if(required.indexOf(property) < 0) return property + "?";
89+
return property;
90+
}).concat(schema.additionalProperties ? ["..."] : []).join(", ")} }`;
91+
}
92+
if(schema.additionalProperties) {
93+
return `object { <key>: ${formatInnerSchema(schema.additionalProperties)} }`;
94+
}
95+
return "object";
96+
} else if(schema.type === "array") {
97+
return `[${formatInnerSchema(schema.items)}]`;
98+
}
99+
100+
switch(schema.instanceof) {
101+
case "Function":
102+
return "function";
103+
case "RegExp":
104+
return "RegExp";
105+
}
106+
if(schema.$ref) return formatInnerSchema(getSchemaPart(schema.$ref), true);
107+
if(schema.allOf) return schema.allOf.map(formatInnerSchema).join(" & ");
108+
if(schema.oneOf) return schema.oneOf.map(formatInnerSchema).join(" | ");
109+
if(schema.anyOf) return schema.anyOf.map(formatInnerSchema).join(" | ");
110+
if(schema.enum) return schema.enum.map(item => JSON.stringify(item)).join(" | ");
111+
return JSON.stringify(schema, 0, 2);
112+
}
22113

23-
WebpackOptionsValidationError.formatValidationError = function formatValidationError(err) {
24-
var dataPath = "configuration" + err.dataPath;
25-
switch(err.keyword) {
26-
case "additionalProperties":
27-
var baseMessage = dataPath + " has an unknown property '" + err.params.additionalProperty + "'. These properties are valid:\n" +
28-
getSchemaPartText(err.parentSchema);
114+
static formatValidationError(err) {
115+
const dataPath = `configuration${err.dataPath}`;
116+
if(err.keyword === "additionalProperties") {
117+
const baseMessage = `${dataPath} has an unknown property '${err.params.additionalProperty}'. These properties are valid:\n${getSchemaPartText(err.parentSchema)}`;
29118
if(!err.dataPath) {
30119
switch(err.params.additionalProperty) {
31120
case "debug":
32-
return baseMessage + "\n" +
121+
return `${baseMessage}\n` +
33122
"The 'debug' property was removed in webpack 2.\n" +
34123
"Loaders should be updated to allow passing this option via loader options in module.rules.\n" +
35124
"Until loaders are updated one can use the LoaderOptionsPlugin to switch loaders into debug mode:\n" +
@@ -48,153 +137,55 @@ WebpackOptionsValidationError.formatValidationError = function formatValidationE
48137
" new webpack.LoaderOptionsPlugin({\n" +
49138
" // test: /\\.xxx$/, // may apply this only for some modules\n" +
50139
" options: {\n" +
51-
" " + err.params.additionalProperty + ": ...\n" +
140+
` ${err.params.additionalProperty}: ...\n` +
52141
" }\n" +
53142
" })\n" +
54143
" ]";
55144
}
56145
return baseMessage;
57-
case "oneOf":
58-
case "anyOf":
146+
} else if(err.keyword === "oneOf" || err.keyword === "anyOf") {
59147
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")
148+
return `${dataPath} should be one of these:\n${getSchemaPartText(err.parentSchema)}\n` +
149+
`Details:\n${err.children.map(err => " * " + indent(WebpackOptionsValidationError.formatValidationError(err), " ", false)).join("\n")}`
64150
}
65-
return dataPath + " should be one of these:\n" +
66-
getSchemaPartText(err.parentSchema);
67-
case "enum":
151+
return `${dataPath} should be one of these:\n${getSchemaPartText(err.parentSchema)}`;
152+
153+
} else if(err.keyword === "enum") {
68154
if(err.parentSchema && err.parentSchema.enum && err.parentSchema.enum.length === 1) {
69-
return dataPath + " should be " + getSchemaPartText(err.parentSchema);
155+
return `${dataPath} should be ${getSchemaPartText(err.parentSchema)}`;
70156
}
71-
return dataPath + " should be one of these:\n" +
72-
getSchemaPartText(err.parentSchema);
73-
case "allOf":
74-
return dataPath + " should be:\n" +
75-
getSchemaPartText(err.parentSchema);
76-
case "type":
157+
return `${dataPath} should be one of these:\n${getSchemaPartText(err.parentSchema)}`;
158+
} else if(err.keyword === "allOf") {
159+
return `${dataPath} should be:\n${getSchemaPartText(err.parentSchema)}`;
160+
} else if(err.keyword === "type") {
77161
switch(err.params.type) {
78162
case "object":
79-
return dataPath + " should be an object.";
163+
return `${dataPath} should be an object.`;
80164
case "string":
81-
return dataPath + " should be a string.";
165+
return `${dataPath} should be a string.`;
82166
case "boolean":
83-
return dataPath + " should be a boolean.";
167+
return `${dataPath} should be a boolean.`;
84168
case "number":
85-
return dataPath + " should be a number.";
169+
return `${dataPath} should be a number.`;
86170
case "array":
87-
return dataPath + " should be an array:\n" +
88-
getSchemaPartText(err.parentSchema);
171+
return `${dataPath} should be an array:\n${getSchemaPartText(err.parentSchema)}`;
89172
}
90-
return dataPath + " should be " + err.params.type + ":\n" +
91-
getSchemaPartText(err.parentSchema);
92-
case "instanceof":
93-
return dataPath + " should be an instance of " + getSchemaPartText(err.parentSchema) + ".";
94-
case "required":
95-
var missingProperty = err.params.missingProperty.replace(/^\./, "");
96-
return dataPath + " misses the property '" + missingProperty + "'.\n" +
97-
getSchemaPartText(err.parentSchema, ["properties", missingProperty]);
98-
case "minItems":
99-
case "minLength":
173+
return `${dataPath} should be ${err.params.type}:\n${getSchemaPartText(err.parentSchema)}`;
174+
} else if(err.keyword === "instanceof") {
175+
return `${dataPath} should be an instance of ${getSchemaPartText(err.parentSchema)}.`;
176+
} else if(err.keyword === "required") {
177+
const missingProperty = err.params.missingProperty.replace(/^\./, "");
178+
return `${dataPath} misses the property '${missingProperty}'.\n${getSchemaPartText(err.parentSchema, ["properties", missingProperty])}`;
179+
} else if(err.keyword === "minLength" || err.keyword === "minItems") {
100180
if(err.params.limit === 1)
101-
return dataPath + " should not be empty.";
181+
return `${dataPath} should not be empty.`;
102182
else
103-
return dataPath + " " + err.message;
104-
default: // eslint-disable-line no-fallthrough
105-
return dataPath + " " + err.message + " (" + JSON.stringify(err, 0, 2) + ").\n" +
106-
getSchemaPartText(err.parentSchema);
107-
}
108-
}
109-
110-
function getSchemaPart(path, parents, additionalPath) {
111-
parents = parents || 0;
112-
path = path.split("/");
113-
path = path.slice(0, path.length - parents);
114-
if(additionalPath) {
115-
additionalPath = additionalPath.split("/");
116-
path = path.concat(additionalPath);
117-
}
118-
var schemaPart = webpackOptionsSchema;
119-
for(var i = 1; i < path.length; i++) {
120-
var inner = schemaPart[path[i]];
121-
if(inner)
122-
schemaPart = inner;
123-
}
124-
return schemaPart;
125-
}
126-
127-
function getSchemaPartText(schemaPart, additionalPath) {
128-
if(additionalPath) {
129-
for(var i = 0; i < additionalPath.length; i++) {
130-
var inner = schemaPart[additionalPath[i]];
131-
if(inner)
132-
schemaPart = inner;
183+
return `${dataPath} ${err.message}`;
184+
} else {
185+
// eslint-disable-line no-fallthrough
186+
return `${dataPath} ${err.message} (${JSON.stringify(err, 0, 2)}).\n${getSchemaPartText(err.parentSchema)}`;
133187
}
134188
}
135-
while(schemaPart.$ref) schemaPart = getSchemaPart(schemaPart.$ref);
136-
var schemaText = WebpackOptionsValidationError.formatSchema(schemaPart);
137-
if(schemaPart.description)
138-
schemaText += "\n" + schemaPart.description;
139-
return schemaText;
140-
}
141-
142-
function formatSchema(schema, prevSchemas) {
143-
prevSchemas = prevSchemas || [];
144-
145-
function formatInnerSchema(innerSchema, addSelf) {
146-
if(!addSelf) return formatSchema(innerSchema, prevSchemas);
147-
if(prevSchemas.indexOf(innerSchema) >= 0) return "(recursive)";
148-
return formatSchema(innerSchema, prevSchemas.concat(schema));
149-
}
150-
switch(schema.type) {
151-
case "string":
152-
if(schema.minLength === 1)
153-
return "non-empty string";
154-
else if(schema.minLength > 1)
155-
return "string (min length " + schema.minLength + ")";
156-
return "string";
157-
case "boolean":
158-
return "boolean";
159-
case "number":
160-
return "number";
161-
case "object":
162-
if(schema.properties) {
163-
var required = schema.required || [];
164-
return "object { " + Object.keys(schema.properties).map(function(property) {
165-
if(required.indexOf(property) < 0) return property + "?";
166-
return property;
167-
}).concat(schema.additionalProperties ? ["..."] : []).join(", ") + " }";
168-
}
169-
if(schema.additionalProperties) {
170-
return "object { <key>: " + formatInnerSchema(schema.additionalProperties) + " }";
171-
}
172-
return "object";
173-
case "array":
174-
return "[" + formatInnerSchema(schema.items) + "]";
175-
}
176-
switch(schema.instanceof) {
177-
case "Function":
178-
return "function";
179-
case "RegExp":
180-
return "RegExp";
181-
}
182-
if(schema.$ref) return formatInnerSchema(getSchemaPart(schema.$ref), true);
183-
if(schema.allOf) return schema.allOf.map(formatInnerSchema).join(" & ");
184-
if(schema.oneOf) return schema.oneOf.map(formatInnerSchema).join(" | ");
185-
if(schema.anyOf) return schema.anyOf.map(formatInnerSchema).join(" | ");
186-
if(schema.enum) return schema.enum.map(function(item) {
187-
return JSON.stringify(item);
188-
}).join(" | ");
189-
return JSON.stringify(schema, 0, 2);
190189
}
191190

192-
function indent(str, prefix, firstLine) {
193-
if(firstLine) {
194-
return prefix + str.replace(/\n(?!$)/g, "\n" + prefix);
195-
} else {
196-
return str.replace(/\n(?!$)/g, "\n" + prefix);
197-
}
198-
}
199-
200-
WebpackOptionsValidationError.formatSchema = formatSchema;
191+
module.exports = WebpackOptionsValidationError;

0 commit comments

Comments
 (0)