text: /*
  	MIT License http://www.opensource.org/licenses/mit-license.php
  	Author Gajus Kuizinas @gajus
  */
  "use strict";
  
  const WebpackError = require("./WebpackError");
  const webpackOptionsSchema = require("../schemas/WebpackOptions.json");
  
  const getSchemaPart = (path, parents, additionalPath) => {
  	parents = parents || 0;
  	path = path.split("/");
  	path = path.slice(0, path.length - parents);
  	if (additionalPath) {
  		additionalPath = additionalPath.split("/");
  		path = path.concat(additionalPath);
  	}
  	let schemaPart = webpackOptionsSchema;
  	for (let i = 1; i < path.length; i++) {
  		const inner = schemaPart[path[i]];
  		if (inner) schemaPart = inner;
  	}
  	return schemaPart;
  };
  
  const getSchemaPartText = (schemaPart, additionalPath) => {
  	if (additionalPath) {
  		for (let i = 0; i < additionalPath.length; i++) {
  			const inner = schemaPart[additionalPath[i]];
  			if (inner) schemaPart = inner;
  		}
  	}
  	while (schemaPart.$ref) {
  		schemaPart = getSchemaPart(schemaPart.$ref);
  	}
  	let schemaText = WebpackOptionsValidationError.formatSchema(schemaPart);
  	if (schemaPart.description) {
  		schemaText += `\n-> ${schemaPart.description}`;
  	}
  	return schemaText;
  };
  
  const getSchemaPartDescription = schemaPart => {
  	while (schemaPart.$ref) {
  		schemaPart = getSchemaPart(schemaPart.$ref);
  	}
  	if (schemaPart.description) {
  		return `\n-> ${schemaPart.description}`;
  	}
  	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 => {
  	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) => {
  	if (firstLine) {
  		return prefix + str.replace(/\n(?!$)/g, "\n" + prefix);
  	} else {
  		return str.replace(/\n(?!$)/g, `\n${prefix}`);
  	}
  };
  
  class WebpackOptionsValidationError extends WebpackError {
  	constructor(validationErrors) {
  		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.validationErrors = validationErrors;
  
  		Error.captureStackTrace(this, this.constructor);
  	}
  
  	static formatSchema(schema, prevSchemas) {
  		prevSchemas = prevSchemas || [];
  
  		const formatInnerSchema = (innerSchema, addSelf) => {
  			if (!addSelf) {
  				return WebpackOptionsValidationError.formatSchema(
  					innerSchema,
  					prevSchemas
  				);
  			}
  			if (prevSchemas.includes(innerSchema)) {
  				return "(recursive)";
  			}
  			return WebpackOptionsValidationError.formatSchema(
  				innerSchema,
  				prevSchemas.concat(schema)
  			);
  		};
  
  		if (schema.type === "string") {
  			if (schema.minLength === 1) {
  				return "non-empty string";
  			}
  			if (schema.minLength > 1) {
  				return `string (min length ${schema.minLength})`;
  			}
  			return "string";
  		}
  		if (schema.type === "boolean") {
  			return "boolean";
  		}
  		if (schema.type === "number") {
  			return "number";
  		}
  		if (schema.type === "object") {
  			if (schema.properties) {
  				const required = schema.required || [];
  				return `object { ${Object.keys(schema.properties)
  					.map(property => {
  						if (!required.includes(property)) return property + "?";
  						return property;
  					})
  					.concat(schema.additionalProperties ? ["…"] : [])
  					.join(", ")} }`;
  			}
  			if (schema.additionalProperties) {
  				return `object { <key>: ${formatInnerSchema(
  					schema.additionalProperties
  				)} }`;
  			}
  			return "object";
  		}
  		if (schema.type === "array") {
  			return `[${formatInnerSchema(schema.items)}]`;
  		}
  
  		switch (schema.instanceof) {
  			case "Function":
  				return "function";
  			case "RegExp":
  				return "RegExp";
  		}
  
  		if (schema.enum) {
  			return schema.enum.map(item => JSON.stringify(item)).join(" | ");
  		}
  
  		if (schema.$ref) {
  			return formatInnerSchema(getSchemaPart(schema.$ref), true);
  		}
  		if (schema.allOf) {
  			return schema.allOf.map(formatInnerSchema).join(" & ");
  		}
  		if (schema.oneOf) {
  			return schema.oneOf.map(formatInnerSchema).join(" | ");
  		}
  		if (schema.anyOf) {
  			return schema.anyOf.map(formatInnerSchema).join(" | ");
  		}
  		return JSON.stringify(schema, null, 2);
  	}
  
  	static formatValidationError(err) {
  		const dataPath = `configuration${err.dataPath}`;
  		if (err.keyword === "additionalProperties") {
  			const baseMessage = `${dataPath} has an unknown property '${
  				err.params.additionalProperty
  			}'. These properties are valid:\n${getSchemaPartText(err.parentSchema)}`;
  			if (!err.dataPath) {
  				switch (err.params.additionalProperty) {
  					case "debug":
  						return (
  							`${baseMessage}\n` +
  							"The 'debug' property was removed in webpack 2.0.0.\n" +
  							"Loaders should be updated to allow passing this option via loader options in module.rules.\n" +
  							"Until loaders are updated one can use the LoaderOptionsPlugin to switch loaders into debug mode:\n" +
  							"plugins: [\n" +
  							"  new webpack.LoaderOptionsPlugin({\n" +
  							"    debug: true\n" +
  							"  })\n" +
  							"]"
  						);
  				}
  				return (
  					`${baseMessage}\n` +
  					"For typos: please correct them.\n" +
  					"For loader options: webpack >= v2.0.0 no longer allows custom properties in configuration.\n" +
  					"  Loaders should be updated to allow passing options via loader options in module.rules.\n" +
  					"  Until loaders are updated one can use the LoaderOptionsPlugin to pass these options to the loader:\n" +
  					"  plugins: [\n" +
  					"    new webpack.LoaderOptionsPlugin({\n" +
  					"      // test: /\\.xxx$/, // may apply this only for some modules\n" +
  					"      options: {\n" +
  					`        ${err.params.additionalProperty}: …\n` +
  					"      }\n" +
  					"    })\n" +
  					"  ]"
  				);
  			}
  			return baseMessage;
  		} else if (err.keyword === "oneOf" || err.keyword === "anyOf") {
  			if (err.children && err.children.length > 0) {
  				if (err.schema.length === 1) {
  					const lastChild = err.children[err.children.length - 1];
  					const remainingChildren = err.children.slice(
  						0,
  						err.children.length - 1
  					);
  					return WebpackOptionsValidationError.formatValidationError(
  						Object.assign({}, lastChild, {
  							children: remainingChildren,
  							parentSchema: Object.assign(
  								{},
  								err.parentSchema,
  								lastChild.parentSchema
  							)
  						})
  					);
  				}
  				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${children
  						.map(
  							err =>
  								" * " +
  								indent(
  									WebpackOptionsValidationError.formatValidationError(err),
  									"   ",
  									false
  								)
  						)
  						.join("\n")}`
  				);
  			}
  			return `${dataPath} should be one of these:\n${getSchemaPartText(
  				err.parentSchema
  			)}`;
  		} else if (err.keyword === "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
  			)}`;
  		} else if (err.keyword === "allOf") {
  			return `${dataPath} should be:\n${getSchemaPartText(err.parentSchema)}`;
  		} else if (err.keyword === "type") {
  			switch (err.params.type) {
  				case "object":
  					return `${dataPath} should be an object.${getSchemaPartDescription(
  						err.parentSchema
  					)}`;
  				case "string":
  					return `${dataPath} should be a string.${getSchemaPartDescription(
  						err.parentSchema
  					)}`;
  				case "boolean":
  					return `${dataPath} should be a boolean.${getSchemaPartDescription(
  						err.parentSchema
  					)}`;
  				case "number":
  					return `${dataPath} should be a number.${getSchemaPartDescription(
  						err.parentSchema
  					)}`;
  				case "array":
  					return `${dataPath} should be an array:\n${getSchemaPartText(
  						err.parentSchema
  					)}`;
  			}
  			return `${dataPath} should be ${err.params.type}:\n${getSchemaPartText(
  				err.parentSchema
  			)}`;
  		} else if (err.keyword === "instanceof") {
  			return `${dataPath} should be an instance of ${getSchemaPartText(
  				err.parentSchema
  			)}`;
  		} else if (err.keyword === "required") {
  			const missingProperty = err.params.missingProperty.replace(/^\./, "");
  			return `${dataPath} misses the property '${missingProperty}'.\n${getSchemaPartText(
  				err.parentSchema,
  				["properties", missingProperty]
  			)}`;
  		} else if (err.keyword === "minimum") {
  			return `${dataPath} ${err.message}.${getSchemaPartDescription(
  				err.parentSchema
  			)}`;
  		} else if (err.keyword === "uniqueItems") {
  			return `${dataPath} should not contain the item '${
  				err.data[err.params.i]
  			}' twice.${getSchemaPartDescription(err.parentSchema)}`;
  		} else if (
  			err.keyword === "minLength" ||
  			err.keyword === "minItems" ||
  			err.keyword === "minProperties"
  		) {
  			if (err.params.limit === 1) {
  				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 {
  				return `${dataPath} ${err.message}${getSchemaPartDescription(
  					err.parentSchema
  				)}`;
  			}
  		} else if (err.keyword === "not") {
  			return `${dataPath} should not be ${getSchemaPartText(
  				err.schema
  			)}\n${getSchemaPartText(err.parentSchema)}`;
  		} else if (err.keyword === "absolutePath") {
  			const baseMessage = `${dataPath}: ${
  				err.message
  			}${getSchemaPartDescription(err.parentSchema)}`;
  			if (dataPath === "configuration.output.filename") {
  				return (
  					`${baseMessage}\n` +
  					"Please use output.path to specify absolute path and output.filename for the file name."
  				);
  			}
  			return baseMessage;
  		} else {
  			return `${dataPath} ${err.message} (${JSON.stringify(
  				err,
  				null,
  				2
  			)}).\n${getSchemaPartText(err.parentSchema)}`;
  		}
  	}
  }
  
  module.exports = WebpackOptionsValidationError;
  