|
|
'use strict';
const Annotate = require('./annotate'); const Common = require('./common'); const Template = require('./template');
const internals = {};
exports.Report = class {
constructor(code, value, local, flags, messages, state, prefs) {
this.code = code; this.flags = flags; this.messages = messages; this.path = state.path; this.prefs = prefs; this.state = state; this.value = value;
this.message = null; this.template = null;
this.local = local || {}; this.local.label = exports.label(this.flags, this.state, this.prefs, this.messages);
if (this.value !== undefined && !this.local.hasOwnProperty('value')) {
this.local.value = this.value; }
if (this.path.length) { const key = this.path[this.path.length - 1]; if (typeof key !== 'object') { this.local.key = key; } } }
_setTemplate(template) {
this.template = template;
if (!this.flags.label && this.path.length === 0) {
const localized = this._template(this.template, 'root'); if (localized) { this.local.label = localized; } } }
toString() {
if (this.message) { return this.message; }
const code = this.code;
if (!this.prefs.errors.render) { return this.code; }
const template = this._template(this.template) || this._template(this.prefs.messages) || this._template(this.messages);
if (template === undefined) { return `Error code "${code}" is not defined, your custom type is missing the correct messages definition`; }
// Render and cache result
this.message = template.render(this.value, this.state, this.prefs, this.local, { errors: this.prefs.errors, messages: [this.prefs.messages, this.messages] }); if (!this.prefs.errors.label) { this.message = this.message.replace(/^"" /, '').trim(); }
return this.message; }
_template(messages, code) {
return exports.template(this.value, messages, code || this.code, this.state, this.prefs); } };
exports.path = function (path) {
let label = ''; for (const segment of path) { if (typeof segment === 'object') { // Exclude array single path segment
continue; }
if (typeof segment === 'string') { if (label) { label += '.'; }
label += segment; } else { label += `[${segment}]`; } }
return label; };
exports.template = function (value, messages, code, state, prefs) {
if (!messages) { return; }
if (Template.isTemplate(messages)) { return code !== 'root' ? messages : null; }
let lang = prefs.errors.language; if (Common.isResolvable(lang)) { lang = lang.resolve(value, state, prefs); }
if (lang && messages[lang]) {
if (messages[lang][code] !== undefined) { return messages[lang][code]; }
if (messages[lang]['*'] !== undefined) { return messages[lang]['*']; } }
if (!messages[code]) { return messages['*']; }
return messages[code]; };
exports.label = function (flags, state, prefs, messages) {
if (!prefs.errors.label) { return ''; }
if (flags.label) { return flags.label; }
let path = state.path; if (prefs.errors.label === 'key' && state.path.length > 1) {
path = state.path.slice(-1); }
const normalized = exports.path(path); if (normalized) { return normalized; }
return exports.template(null, prefs.messages, 'root', state, prefs) || messages && exports.template(null, messages, 'root', state, prefs) || 'value'; };
exports.process = function (errors, original, prefs) {
if (!errors) { return null; }
const { override, message, details } = exports.details(errors); if (override) { return override; }
if (prefs.errors.stack) { return new exports.ValidationError(message, details, original); }
const limit = Error.stackTraceLimit; Error.stackTraceLimit = 0; const validationError = new exports.ValidationError(message, details, original); Error.stackTraceLimit = limit; return validationError; };
exports.details = function (errors, options = {}) {
let messages = []; const details = [];
for (const item of errors) {
// Override
if (item instanceof Error) { if (options.override !== false) { return { override: item }; }
const message = item.toString(); messages.push(message);
details.push({ message, type: 'override', context: { error: item } });
continue; }
// Report
const message = item.toString(); messages.push(message);
details.push({ message, path: item.path.filter((v) => typeof v !== 'object'), type: item.code, context: item.local }); }
if (messages.length > 1) { messages = [...new Set(messages)]; }
return { message: messages.join('. '), details }; };
exports.ValidationError = class extends Error {
constructor(message, details, original) {
super(message); this._original = original; this.details = details; }
static isError(err) {
return err instanceof exports.ValidationError; } };
exports.ValidationError.prototype.isJoi = true;
exports.ValidationError.prototype.name = 'ValidationError';
exports.ValidationError.prototype.annotate = Annotate.error;
|