123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Gajus Kuizinas @gajus
- */
- "use strict";
-
- const WebpackError = require("./WebpackError");
- const webpackOptionsSchema = require("../schemas/webpackOptionsSchema.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 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();
-
- this.name = "WebpackOptionsValidationError";
- this.message = "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.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.indexOf(innerSchema) >= 0) return "(recursive)";
- return WebpackOptionsValidationError.formatSchema(innerSchema, prevSchemas.concat(schema));
- };
-
- if(schema.type === "string") {
- if(schema.minLength === 1)
- return "non-empty string";
- else if(schema.minLength > 1)
- return `string (min length ${schema.minLength})`;
- return "string";
- } else if(schema.type === "boolean") {
- return "boolean";
- } else if(schema.type === "number") {
- return "number";
- } else if(schema.type === "object") {
- if(schema.properties) {
- const required = schema.required || [];
- return `object { ${Object.keys(schema.properties).map(property => {
- if(required.indexOf(property) < 0) return property + "?";
- return property;
- }).concat(schema.additionalProperties ? ["..."] : []).join(", ")} }`;
- }
- if(schema.additionalProperties) {
- return `object { <key>: ${formatInnerSchema(schema.additionalProperties)} }`;
- }
- return "object";
- } else if(schema.type === "array") {
- return `[${formatInnerSchema(schema.items)}]`;
- }
-
- switch(schema.instanceof) {
- case "Function":
- return "function";
- case "RegExp":
- return "RegExp";
- }
- 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(" | ");
- if(schema.enum) return schema.enum.map(item => JSON.stringify(item)).join(" | ");
- return JSON.stringify(schema, 0, 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.\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 2 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) {
- return `${dataPath} should be one of these:\n${getSchemaPartText(err.parentSchema)}\n` +
- `Details:\n${err.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.`;
- case "string":
- return `${dataPath} should be a string.`;
- case "boolean":
- 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)}`;
- } 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 === "minLength" || err.keyword === "minItems") {
- if(err.params.limit === 1)
- return `${dataPath} should not be empty.`;
- else
- return `${dataPath} ${err.message}`;
- } else if(err.keyword === "absolutePath") {
- const baseMessage = `${dataPath}: ${err.message}`;
- 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 {
- // eslint-disable-line no-fallthrough
- return `${dataPath} ${err.message} (${JSON.stringify(err, 0, 2)}).\n${getSchemaPartText(err.parentSchema)}`;
- }
- }
- }
-
- module.exports = WebpackOptionsValidationError;
|