UNPKG

webpack

Version:

Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.

391 lines (371 loc) 11.1 kB
/* 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;