| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 | /*	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;
 |