123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- 'use strict';
- /* eslint no-param-reassign: 'off' */
- const optionsSchema = require('./optionsSchema.json');
- const indent = (str, prefix, firstLine) => {
- if (firstLine) {
- return prefix + str.replace(/\n(?!$)/g, `\n${prefix}`);
- }
- return str.replace(/\n(?!$)/g, `\n${prefix}`);
- };
- 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 = optionsSchema;
- 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 = OptionsValidationError.formatSchema(schemaPart); // eslint-disable-line
- if (schemaPart.description) { schemaText += `\n${schemaPart.description}`; }
- return schemaText;
- };
- class OptionsValidationError extends Error {
- constructor(validationErrors) {
- super();
- if (Error.hasOwnProperty('captureStackTrace')) { // eslint-disable-line
- Error.captureStackTrace(this, this.constructor);
- }
- this.name = 'WebpackDevServerOptionsValidationError';
- this.message = `${'Invalid configuration object. ' +
- 'webpack-dev-server has been initialised using a configuration object that does not match the API schema.\n'}${
- validationErrors.map(err => ` - ${indent(OptionsValidationError.formatValidationError(err), ' ', false)}`).join('\n')}`;
- this.validationErrors = validationErrors;
- }
- static formatSchema(schema, prevSchemas) {
- prevSchemas = prevSchemas || [];
- const formatInnerSchema = (innerSchema, addSelf) => {
- if (!addSelf) return OptionsValidationError.formatSchema(innerSchema, prevSchemas);
- if (prevSchemas.indexOf(innerSchema) >= 0) return '(recursive)';
- return OptionsValidationError.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';
- default:
- }
- 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') {
- return `${dataPath} has an unknown property '${err.params.additionalProperty}'. These properties are valid:\n${getSchemaPartText(err.parentSchema)}`;
- } 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(e => ` * ${indent(OptionsValidationError.formatValidationError(e), ' ', 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)}`;
- default:
- }
- 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.`; }
- return `${dataPath} ${err.message}`;
- }
- // eslint-disable-line no-fallthrough
- return `${dataPath} ${err.message} (${JSON.stringify(err, 0, 2)}).\n${getSchemaPartText(err.parentSchema)}`;
- }
- }
- module.exports = OptionsValidationError;
|